aboutsummaryrefslogtreecommitdiff
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");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
- if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) {
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto fail;
- }
- if (sta->vlan_id)
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
- HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
-
- hostapd_free_psk_list(sta->psk);
- if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
- sta->psk = psk;
- psk = NULL;
- } else {
- sta->psk = NULL;
- }
-
- sta->identity = identity;
- identity = NULL;
- sta->radius_cui = radius_cui;
- radius_cui = NULL;
sta->flags &= ~WLAN_STA_PREAUTH;
ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
- if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
- sta->acct_interim_interval = acct_interim_interval;
- if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
- ap_sta_session_timeout(hapd, sta, session_timeout);
- else
- ap_sta_no_session_timeout(hapd, sta);
-
/*
* If the driver supports full AP client state, add a station to the
* driver before sending authentication reply to make sure the driver
@@ -1263,8 +1998,15 @@ static void handle_auth(struct hostapd_data *hapd,
*
* In mesh mode, the station was already added to the driver when the
* NEW_PEER_CANDIDATE event is received.
+ *
+ * If PMF was negotiated for the existing association, skip this to
+ * avoid dropping the STA entry and the associated keys. This is needed
+ * to allow the original connection work until the attempt can complete
+ * (re)association, so that unprotected Authentication frame cannot be
+ * used to bypass PMF protection.
*/
if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+ (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
!(hapd->conf->mesh & MESH_ENABLED) &&
!(sta->added_unassoc)) {
/*
@@ -1274,6 +2016,7 @@ static void handle_auth(struct hostapd_data *hapd,
* updated. To handle this, station's added_unassoc flag is
* cleared once the station has completed association.
*/
+ ap_sta_set_authorized(hapd, sta, 0);
hostapd_drv_sta_remove(hapd, sta->addr);
sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH |
WLAN_STA_AUTHORIZED);
@@ -1305,6 +2048,9 @@ static void handle_auth(struct hostapd_data *hapd,
case WLAN_AUTH_SHARED_KEY:
resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
fc & WLAN_FC_ISWEP);
+ if (resp != 0)
+ wpa_printf(MSG_DEBUG,
+ "auth_shared_key() failed: status=%d", resp);
sta->auth_alg = WLAN_AUTH_SHARED_KEY;
mlme_authenticate_indication(hapd, sta);
if (sta->challenge && auth_transaction == 1) {
@@ -1316,7 +2062,7 @@ static void handle_auth(struct hostapd_data *hapd,
}
break;
#endif /* CONFIG_NO_RC4 */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
case WLAN_AUTH_FT:
sta->auth_alg = WLAN_AUTH_FT;
if (sta->wpa_sm == NULL)
@@ -1335,7 +2081,7 @@ static void handle_auth(struct hostapd_data *hapd,
handle_auth_ft_finish, hapd);
/* handle_auth_ft_finish() callback will complete auth. */
return;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
case WLAN_AUTH_SAE:
#ifdef CONFIG_MESH
@@ -1357,6 +2103,15 @@ static void handle_auth(struct hostapd_data *hapd,
status_code);
return;
#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ case WLAN_AUTH_FILS_SK:
+ case WLAN_AUTH_FILS_SK_PFS:
+ handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
+ len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
+ auth_alg, auth_transaction, status_code,
+ handle_auth_fils_finish);
+ return;
+#endif /* CONFIG_FILS */
}
fail:
@@ -1366,7 +2121,7 @@ static void handle_auth(struct hostapd_data *hapd,
reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
auth_transaction + 1, resp, resp_ies,
- resp_ies_len);
+ resp_ies_len, "handle-auth");
if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
reply_res != WLAN_STATUS_SUCCESS)) {
@@ -1459,6 +2214,11 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems)
{
+ /* Supported rates not used in IEEE 802.11ad/DMG */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
+ return WLAN_STATUS_SUCCESS;
+
if (!elems->supp_rates) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -1496,12 +2256,187 @@ static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_INTERWORKING */
- if (ext_capab_ie_len > 0)
+ if (ext_capab_ie_len > 0) {
sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+ os_free(sta->ext_capability);
+ sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
+ if (sta->ext_capability) {
+ sta->ext_capability[0] = ext_capab_ie_len;
+ os_memcpy(sta->ext_capability + 1, ext_capab_ie,
+ ext_capab_ie_len);
+ }
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+#ifdef CONFIG_OWE
+
+static int owe_group_supported(struct hostapd_data *hapd, u16 group)
+{
+ int i;
+ int *groups = hapd->conf->owe_groups;
+
+ if (group != 19 && group != 20 && group != 21)
+ return 0;
+
+ if (!groups)
+ return 1;
+
+ for (i = 0; groups[i] > 0; i++) {
+ if (groups[i] == group)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static u16 owe_process_assoc_req(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *owe_dh,
+ u8 owe_dh_len)
+{
+ struct wpabuf *secret, *pub, *hkey;
+ int res;
+ u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+ const char *info = "OWE Key Generation";
+ const u8 *addr[2];
+ size_t len[2];
+ u16 group;
+ size_t hash_len, prime_len;
+
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ group = WPA_GET_LE16(owe_dh);
+ if (!owe_group_supported(hapd, group)) {
+ wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ }
+ if (group == 19)
+ prime_len = 32;
+ else if (group == 20)
+ prime_len = 48;
+ else if (group == 21)
+ prime_len = 66;
+ else
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+
+ crypto_ecdh_deinit(sta->owe_ecdh);
+ sta->owe_ecdh = crypto_ecdh_init(group);
+ if (!sta->owe_ecdh)
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ sta->owe_group = group;
+
+ secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
+ owe_dh_len - 2);
+ secret = wpabuf_zeropad(secret, prime_len);
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+ /* prk = HKDF-extract(C | A | group, z) */
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ /* PMKID = Truncate-128(Hash(C | A)) */
+ addr[0] = owe_dh + 2;
+ len[0] = owe_dh_len - 2;
+ addr[1] = wpabuf_head(pub);
+ len[1] = wpabuf_len(pub);
+ if (group == 19) {
+ res = sha256_vector(2, addr, len, pmkid);
+ hash_len = SHA256_MAC_LEN;
+ } else if (group == 20) {
+ res = sha384_vector(2, addr, len, pmkid);
+ hash_len = SHA384_MAC_LEN;
+ } else if (group == 21) {
+ res = sha512_vector(2, addr, len, pmkid);
+ hash_len = SHA512_MAC_LEN;
+ } else {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ pub = wpabuf_zeropad(pub, prime_len);
+ if (res < 0 || !pub) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
+ if (!hkey) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
+ wpabuf_put_buf(hkey, pub); /* A */
+ wpabuf_free(pub);
+ wpabuf_put_le16(hkey, group); /* group */
+ if (group == 19)
+ res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ else if (group == 20)
+ res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ else if (group == 21)
+ res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ wpabuf_clear_free(hkey);
+ wpabuf_clear_free(secret);
+ if (res < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+ /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+ os_free(sta->owe_pmk);
+ sta->owe_pmk = os_malloc(hash_len);
+ if (!sta->owe_pmk) {
+ os_memset(prk, 0, SHA512_MAC_LEN);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (group == 19)
+ res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ else if (group == 20)
+ res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ else if (group == 21)
+ res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ os_memset(prk, 0, SHA512_MAC_LEN);
+ if (res < 0) {
+ os_free(sta->owe_pmk);
+ sta->owe_pmk = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ sta->owe_pmk_len = hash_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
+ wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+ wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
+ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
return WLAN_STATUS_SUCCESS;
}
+#endif /* CONFIG_OWE */
+
static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len, int reassoc)
@@ -1644,32 +2579,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
wpa_ie, wpa_ie_len,
- elems.mdie, elems.mdie_len);
- if (res == WPA_INVALID_GROUP)
- resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
- else if (res == WPA_INVALID_PAIRWISE)
- resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
- else if (res == WPA_INVALID_AKMP)
- resp = WLAN_STATUS_AKMP_NOT_VALID;
- else if (res == WPA_ALLOC_FAIL)
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-#ifdef CONFIG_IEEE80211W
- else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
- resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
- else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
- resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
-#endif /* CONFIG_IEEE80211W */
- else if (res == WPA_INVALID_MDIE)
- resp = WLAN_STATUS_INVALID_MDIE;
- else if (res != WPA_IE_OK)
- resp = WLAN_STATUS_INVALID_IE;
+ elems.mdie, elems.mdie_len,
+ elems.owe_dh, elems.owe_dh_len);
+ resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
#ifdef CONFIG_IEEE80211W
- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
+ !sta->sa_query_timed_out &&
sta->sa_query_count > 0)
ap_check_sa_query_timeout(hapd, sta);
- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
+ !sta->sa_query_timed_out &&
(!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
/*
* STA has already been associated with MFP and SA
@@ -1690,7 +2613,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
sta->flags &= ~WLAN_STA_MFP;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sta->auth_alg == WLAN_AUTH_FT) {
if (!reassoc) {
wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
@@ -1705,9 +2628,13 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (resp != WLAN_STATUS_SUCCESS)
return resp;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
+ if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
+ sta->sae->state == SAE_ACCEPTED)
+ wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
+
if (wpa_auth_uses_sae(sta->wpa_sm) &&
sta->auth_alg == WLAN_AUTH_OPEN) {
struct rsn_pmksa_cache_entry *sa;
@@ -1731,6 +2658,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ elems.owe_dh) {
+ resp = owe_process_assoc_req(hapd, sta, elems.owe_dh,
+ elems.owe_dh_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_OWE */
+
#ifdef CONFIG_IEEE80211N
if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
@@ -1779,6 +2717,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
elems.hs20_len - 4);
} else
sta->hs20_ie = NULL;
+
+ wpabuf_free(sta->roaming_consortium);
+ if (elems.roaming_cons_sel)
+ sta->roaming_consortium = wpabuf_alloc_copy(
+ elems.roaming_cons_sel + 4,
+ elems.roaming_cons_sel_len - 4);
+ else
+ sta->roaming_consortium = NULL;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FST
@@ -1810,6 +2756,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
sizeof(sta->rrm_enabled_capa));
+ if (elems.power_capab) {
+ sta->min_tx_power = elems.power_capab[0];
+ sta->max_tx_power = elems.power_capab[1];
+ sta->power_capab = 1;
+ } else {
+ sta->power_capab = 0;
+ }
+
return WLAN_STATUS_SUCCESS;
}
@@ -1856,7 +2810,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
*/
if (!sta->added_unassoc &&
(!(sta->flags & WLAN_STA_AUTHORIZED) ||
- !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) {
+ (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+ !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
hostapd_drv_sta_remove(hapd, sta->addr);
wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
set = 0;
@@ -1904,21 +2859,36 @@ static int add_associated_sta(struct hostapd_data *hapd,
static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- u16 status_code, int reassoc, const u8 *ies,
- size_t ies_len)
+ const u8 *addr, u16 status_code, int reassoc,
+ const u8 *ies, size_t ies_len)
{
int send_len;
- u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+ u8 *buf;
+ size_t buflen;
struct ieee80211_mgmt *reply;
u8 *p;
-
- os_memset(buf, 0, sizeof(buf));
+ u16 res = WLAN_STATUS_SUCCESS;
+
+ buflen = sizeof(struct ieee80211_mgmt) + 1024;
+#ifdef CONFIG_FILS
+ if (sta && sta->fils_hlp_resp)
+ buflen += wpabuf_len(sta->fils_hlp_resp);
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ buflen += 150;
+#endif /* CONFIG_OWE */
+ buf = os_zalloc(buflen);
+ if (!buf) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
reply = (struct ieee80211_mgmt *) buf;
reply->frame_control =
IEEE80211_FC(WLAN_FC_TYPE_MGMT,
(reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
WLAN_FC_STYPE_ASSOC_RESP));
- os_memcpy(reply->da, sta->addr, ETH_ALEN);
+ os_memcpy(reply->da, addr, ETH_ALEN);
os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
@@ -1927,24 +2897,40 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
reply->u.assoc_resp.capab_info =
host_to_le16(hostapd_own_capab_info(hapd));
reply->u.assoc_resp.status_code = host_to_le16(status_code);
- reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
+
+ reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
+ BIT(14) | BIT(15));
/* Supported rates */
p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
/* Extended supported rates */
p = hostapd_eid_ext_supp_rates(hapd, p);
-#ifdef CONFIG_IEEE80211R
- if (status_code == WLAN_STATUS_SUCCESS) {
+#ifdef CONFIG_IEEE80211R_AP
+ if (sta && status_code == WLAN_STATUS_SUCCESS) {
/* IEEE 802.11r: Mobility Domain Information, Fast BSS
* Transition Information, RSN, [RIC Response] */
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
- buf + sizeof(buf) - p,
+ buf + buflen - p,
sta->auth_alg, ies, ies_len);
+ if (!p) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to write AssocResp IEs");
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_OWE
+ if (sta && status_code == WLAN_STATUS_SUCCESS &&
+ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
+ buf + buflen - p,
+ ies, ies_len);
+#endif /* CONFIG_OWE */
#ifdef CONFIG_IEEE80211W
- if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+ if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
#endif /* CONFIG_IEEE80211W */
@@ -1957,7 +2943,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
u32 nsts = 0, sta_nsts;
- if (hapd->conf->use_sta_nsts && sta->vht_capabilities) {
+ if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
struct ieee80211_vht_capabilities *capa;
nsts = (hapd->iface->conf->vht_capab >>
@@ -1978,7 +2964,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
p = hostapd_eid_ext_capab(hapd, p);
p = hostapd_eid_bss_max_idle_period(hapd, p);
- if (sta->qos_map_enabled)
+ if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
#ifdef CONFIG_FST
@@ -1990,16 +2976,17 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_FST */
#ifdef CONFIG_IEEE80211AC
- if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+ if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
p = hostapd_eid_vendor_vht(hapd, p);
#endif /* CONFIG_IEEE80211AC */
- if (sta->flags & WLAN_STA_WMM)
+ if (sta && (sta->flags & WLAN_STA_WMM))
p = hostapd_eid_wmm(hapd, p);
#ifdef CONFIG_WPS
- if ((sta->flags & WLAN_STA_WPS) ||
- ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) {
+ if (sta &&
+ ((sta->flags & WLAN_STA_WPS) ||
+ ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
struct wpabuf *wps = wps_build_assoc_resp_ie();
if (wps) {
os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
@@ -2010,7 +2997,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
- if (sta->p2p_ie && hapd->p2p_group) {
+ if (sta && sta->p2p_ie && hapd->p2p_group) {
struct wpabuf *p2p_resp_ie;
enum p2p_status_code status;
switch (status_code) {
@@ -2039,10 +3026,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
p = hostapd_eid_p2p_manage(hapd, p);
#endif /* CONFIG_P2P_MANAGER */
- p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p);
+ p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
if (hapd->conf->assocresp_elements &&
- (size_t) (buf + sizeof(buf) - p) >=
+ (size_t) (buf + buflen - p) >=
wpabuf_len(hapd->conf->assocresp_elements)) {
os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
wpabuf_len(hapd->conf->assocresp_elements));
@@ -2051,14 +3038,174 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
send_len += p - reply->u.assoc_resp.variable;
+#ifdef CONFIG_FILS
+ if (sta &&
+ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+ status_code == WLAN_STATUS_SUCCESS) {
+ struct ieee802_11_elems elems;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+ ParseFailed || !elems.fils_session) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+
+ /* FILS Session */
+ *p++ = WLAN_EID_EXTENSION; /* Element ID */
+ *p++ = 1 + FILS_SESSION_LEN; /* Length */
+ *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+ os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
+ send_len += 2 + 1 + FILS_SESSION_LEN;
+
+ send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
+ buflen, sta->fils_hlp_resp);
+ if (send_len < 0) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ /* OWE Diffie-Hellman Parameter element */
+ *p++ = WLAN_EID_EXTENSION; /* Element ID */
+ *p++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
+ WPA_PUT_LE16(p, sta->owe_group);
+ p += 2;
+ os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
+ p += wpabuf_len(pub);
+ send_len += 3 + 2 + wpabuf_len(pub);
+ wpabuf_free(pub);
+ }
+#endif /* CONFIG_OWE */
+
if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
strerror(errno));
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- return WLAN_STATUS_SUCCESS;
+done:
+ os_free(buf);
+ return res;
+}
+
+
+#ifdef CONFIG_OWE
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *owe_dh, u8 owe_dh_len,
+ u8 *owe_buf, size_t owe_buf_len, u16 *reason)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->own_ie_override) {
+ wpa_printf(MSG_DEBUG, "OWE: Using IE override");
+ *reason = WLAN_STATUS_SUCCESS;
+ return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+ *reason = WLAN_STATUS_SUCCESS;
+ return owe_buf;
+ }
+
+ *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+ if (*reason != WLAN_STATUS_SUCCESS)
+ return NULL;
+
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+
+ if (sta->owe_ecdh && owe_buf) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ *reason = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return owe_buf;
+ }
+
+ /* OWE Diffie-Hellman Parameter element */
+ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+ */
+ WPA_PUT_LE16(owe_buf, sta->owe_group);
+ owe_buf += 2;
+ os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+ owe_buf += wpabuf_len(pub);
+ wpabuf_free(pub);
+ }
+
+ return owe_buf;
}
+#endif /* CONFIG_OWE */
+
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ u16 reply_res;
+
+ wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
+ MAC2STR(sta->addr));
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ if (!sta->fils_pending_assoc_req)
+ return;
+ reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
+ sta->fils_pending_assoc_is_reassoc,
+ sta->fils_pending_assoc_req,
+ sta->fils_pending_assoc_req_len);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+
+ /*
+ * Remove the station in case transmission of a success response fails.
+ * At this point the station was already added associated to the driver.
+ */
+ if (reply_res != WLAN_STATUS_SUCCESS)
+ hostapd_drv_sta_remove(hapd, sta->addr);
+}
+
+
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = eloop_data;
+
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP response timeout - continue with association response for "
+ MACSTR, MAC2STR(sta->addr));
+ if (sta->fils_drv_assoc_finish)
+ hostapd_notify_assoc_fils_finish(hapd, sta);
+ else
+ fils_hlp_finish_assoc(hapd, sta);
+}
+
+#endif /* CONFIG_FILS */
static void handle_assoc(struct hostapd_data *hapd,
@@ -2070,6 +3217,13 @@ static void handle_assoc(struct hostapd_data *hapd,
const u8 *pos;
int left, i;
struct sta_info *sta;
+ u8 *tmp = NULL;
+ struct hostapd_sta_wpa_psk_short *psk = NULL;
+ char *identity = NULL;
+ char *radius_cui = NULL;
+#ifdef CONFIG_FILS
+ int delay_assoc = 0;
+#endif /* CONFIG_FILS */
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
@@ -2127,7 +3281,7 @@ static void handle_assoc(struct hostapd_data *hapd,
}
sta = ap_get_sta(hapd, mgmt->sa);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sta && sta->auth_alg == WLAN_AUTH_FT &&
(sta->flags & WLAN_STA_AUTH) == 0) {
wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
@@ -2140,24 +3294,76 @@ static void handle_assoc(struct hostapd_data *hapd,
*/
sta->flags |= WLAN_STA_AUTH;
} else
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
- hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO, "Station tried to "
- "associate before authentication "
- "(aid=%d flags=0x%x)",
- sta ? sta->aid : -1,
- sta ? sta->flags : 0);
- send_deauth(hapd, mgmt->sa,
- WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
- return;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode ==
+ HOSTAPD_MODE_IEEE80211AD) {
+ int acl_res;
+ u32 session_timeout, acct_interim_interval;
+ struct vlan_description vlan_id;
+
+ acl_res = ieee802_11_allowed_address(
+ hapd, mgmt->sa, (const u8 *) mgmt, len,
+ &session_timeout, &acct_interim_interval,
+ &vlan_id, &psk, &identity, &radius_cui, 0);
+ if (acl_res == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Association Request frame from "
+ MACSTR " due to ACL reject",
+ MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (acl_res == HOSTAPD_ACL_PENDING)
+ return;
+
+ /* DMG/IEEE 802.11ad does not use authentication.
+ * Allocate sta entry upon association. */
+ sta = ap_sta_add(hapd, mgmt->sa);
+ if (!sta) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Failed to add STA");
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+
+ acl_res = ieee802_11_set_radius_info(
+ hapd, sta, acl_res, session_timeout,
+ acct_interim_interval, &vlan_id, &psk,
+ &identity, &radius_cui);
+ if (acl_res) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Skip authentication for DMG/IEEE 802.11ad");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = WLAN_AUTH_OPEN;
+ } else {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station tried to associate before authentication (aid=%d flags=0x%x)",
+ sta ? sta->aid : -1,
+ sta ? sta->flags : 0);
+ send_deauth(hapd, mgmt->sa,
+ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+ return;
+ }
}
if ((fc & WLAN_FC_RETRY) &&
sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
sta->last_seq_ctrl == seq_ctrl &&
- sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
- WLAN_FC_STYPE_ASSOC_REQ) {
+ sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+ WLAN_FC_STYPE_ASSOC_REQ)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Drop repeated association frame seq_ctrl=0x%x",
@@ -2169,7 +3375,7 @@ static void handle_assoc(struct hostapd_data *hapd,
WLAN_FC_STYPE_ASSOC_REQ;
if (hapd->tkip_countermeasures) {
- resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2195,6 +3401,32 @@ static void handle_assoc(struct hostapd_data *hapd,
*/
sta->capability = capab_info;
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ int res;
+
+ /* The end of the payload is encrypted. Need to decrypt it
+ * before parsing. */
+
+ tmp = os_memdup(pos, left);
+ if (!tmp) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+ len, tmp, left);
+ if (res < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ pos = tmp;
+ left = res;
+ }
+#endif /* CONFIG_FILS */
+
/* followed by SSID and Supported rates; and HT capabilities if 802.11n
* is used */
resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
@@ -2210,7 +3442,8 @@ static void handle_assoc(struct hostapd_data *hapd,
sta->listen_interval = listen_interval;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
sta->flags |= WLAN_STA_NONERP;
for (i = 0; i < sta->supported_rates_len; i++) {
if ((sta->supported_rates[i] & 0x7f) > 22) {
@@ -2229,7 +3462,8 @@ static void handle_assoc(struct hostapd_data *hapd,
!sta->no_short_slot_time_set) {
sta->no_short_slot_time_set = 1;
hapd->iface->num_sta_no_short_slot_time++;
- if (hapd->iface->current_mode->mode ==
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode ==
HOSTAPD_MODE_IEEE80211G &&
hapd->iface->num_sta_no_short_slot_time == 1)
ieee802_11_set_beacons(hapd->iface);
@@ -2244,7 +3478,8 @@ static void handle_assoc(struct hostapd_data *hapd,
!sta->no_short_preamble_set) {
sta->no_short_preamble_set = 1;
hapd->iface->num_sta_no_short_preamble++;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_preamble == 1)
ieee802_11_set_beacons(hapd->iface);
}
@@ -2281,7 +3516,22 @@ static void handle_assoc(struct hostapd_data *hapd,
taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
#endif /* CONFIG_TAXONOMY */
+ sta->pending_wds_enable = 0;
+
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ if (fils_process_hlp(hapd, sta, pos, left) > 0)
+ delay_assoc = 1;
+ }
+#endif /* CONFIG_FILS */
+
fail:
+ os_free(identity);
+ os_free(radius_cui);
+ hostapd_free_psk_list(psk);
+
/*
* In case of a successful response, add the station to the driver.
* Otherwise, the kernel may ignore Data frames before we process the
@@ -2300,18 +3550,44 @@ static void handle_assoc(struct hostapd_data *hapd,
* issues with processing other non-Data Class 3 frames during this
* window.
*/
- if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta))
+ if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta))
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
- reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+#ifdef CONFIG_FILS
+ if (sta) {
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ }
+ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
+ sta->fils_pending_assoc_req = tmp;
+ sta->fils_pending_assoc_req_len = left;
+ sta->fils_pending_assoc_is_reassoc = reassoc;
+ sta->fils_drv_assoc_finish = 0;
+ wpa_printf(MSG_DEBUG,
+ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+ MACSTR, MAC2STR(sta->addr));
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
+ fils_hlp_timeout, hapd, sta);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
+ reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos,
+ left);
+ os_free(tmp);
/*
* Remove the station in case tranmission of a success response fails
* (the STA was added associated to the driver) or if the station was
* previously added unassociated.
*/
- if ((reply_res != WLAN_STATUS_SUCCESS &&
- resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) {
+ if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
+ resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
@@ -2368,6 +3644,17 @@ static void handle_disassoc(struct hostapd_data *hapd,
mlme_disassociate_indication(
hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
+
+ /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
+ * disassociation. */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ sta->flags &= ~WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+ ap_free_sta(hapd, sta);
+ }
}
@@ -2462,12 +3749,13 @@ static int robust_action_frame(u8 category)
static int handle_action(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ unsigned int freq)
{
struct sta_info *sta;
- sta = ap_get_sta(hapd, mgmt->sa);
+ u8 *action __maybe_unused;
- if (len < IEEE80211_HDRLEN + 1) {
+ if (len < IEEE80211_HDRLEN + 2 + 1) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"handle_action - too short payload (len=%lu)",
@@ -2475,11 +3763,19 @@ static int handle_action(struct hostapd_data *hapd,
return 0;
}
+ action = (u8 *) &mgmt->u.action.u;
+ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+ " da " MACSTR " len %d freq %u",
+ mgmt->u.action.category, *action,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+
if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
(sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
"frame (category=%u) from unassociated STA " MACSTR,
- MAC2STR(mgmt->sa), mgmt->u.action.category);
+ mgmt->u.action.category, MAC2STR(mgmt->sa));
return 0;
}
@@ -2516,14 +3812,14 @@ static int handle_action(struct hostapd_data *hapd,
}
switch (mgmt->u.action.category) {
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
case WLAN_ACTION_FT:
if (!sta ||
wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
len - IEEE80211_HDRLEN))
break;
return 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
case WLAN_ACTION_WMM:
hostapd_wmm_action(hapd, mgmt, len);
return 1;
@@ -2531,11 +3827,11 @@ static int handle_action(struct hostapd_data *hapd,
case WLAN_ACTION_SA_QUERY:
return hostapd_sa_query_action(hapd, mgmt, len);
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
case WLAN_ACTION_WNM:
ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
return 1;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_FST
case WLAN_ACTION_FST:
if (hapd->iface->fst)
@@ -2551,12 +3847,41 @@ static int handle_action(struct hostapd_data *hapd,
if (len >= IEEE80211_HDRLEN + 2 &&
mgmt->u.action.u.public_action.action ==
WLAN_PA_20_40_BSS_COEX) {
- wpa_printf(MSG_DEBUG,
- "HT20/40 coex mgmt frame received from STA "
- MACSTR, MAC2STR(mgmt->sa));
hostapd_2040_coex_action(hapd, mgmt, len);
+ return 1;
}
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_DPP
+ if (len >= IEEE80211_HDRLEN + 6 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ DPP_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.vs_public_action.oui;
+ end = ((const u8 *) mgmt) + len;
+ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+ freq);
+ return 1;
+ }
+ if (len >= IEEE80211_HDRLEN + 2 &&
+ (mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_INITIAL_RESP ||
+ mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_COMEBACK_RESP)) {
+ const u8 *pos, *end;
+
+ pos = &mgmt->u.action.u.public_action.action;
+ end = ((const u8 *) mgmt) + len;
+ gas_query_ap_rx(hapd->gas, mgmt->sa,
+ mgmt->u.action.category,
+ pos, end - pos, hapd->iface->freq);
+ return 1;
+ }
+#endif /* CONFIG_DPP */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len,
@@ -2600,10 +3925,9 @@ static int handle_action(struct hostapd_data *hapd,
*/
wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
"frame back to sender");
- resp = os_malloc(len);
+ resp = os_memdup(mgmt, len);
if (resp == NULL)
return 0;
- os_memcpy(resp, mgmt, len);
os_memcpy(resp->da, resp->sa, ETH_ALEN);
os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
@@ -2639,10 +3963,17 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct ieee80211_mgmt *mgmt;
u16 fc, stype;
int ret = 0;
+ unsigned int freq;
+ int ssi_signal = fi ? fi->ssi_signal : 0;
if (len < 24)
return 0;
+ if (fi && fi->freq)
+ freq = fi->freq;
+ else
+ freq = hapd->iface->freq;
+
mgmt = (struct ieee80211_mgmt *) buf;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
@@ -2669,11 +4000,13 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
- handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
+ handle_probe_req(hapd, mgmt, len, ssi_signal);
return 1;
}
- if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+ if ((!is_broadcast_ether_addr(mgmt->da) ||
+ stype != WLAN_FC_STYPE_ACTION) &&
+ os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"MGMT: DA=" MACSTR " not our address",
@@ -2682,7 +4015,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
}
if (hapd->iconf->track_sta_max_num)
- sta_track_add(hapd->iface, mgmt->sa);
+ sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
switch (stype) {
case WLAN_FC_STYPE_AUTH:
@@ -2712,7 +4045,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action");
- ret = handle_action(hapd, mgmt, len);
+ ret = handle_action(hapd, mgmt, len, freq);
break;
default:
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -2734,7 +4067,8 @@ static void handle_auth_cb(struct hostapd_data *hapd,
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
- wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+ wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
+ " not found",
MAC2STR(mgmt->da));
return;
}
@@ -2856,11 +4190,15 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
new_assoc = 0;
sta->flags |= WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
- if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
+ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+ !hapd->conf->osen) ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK ||
sta->auth_alg == WLAN_AUTH_FT) {
/*
- * Open, static WEP, or FT protocol; no separate authorization
- * step.
+ * Open, static WEP, FT protocol, or FILS; no separate
+ * authorization step.
*/
ap_sta_set_authorized(hapd, sta, 1);
}
@@ -2874,16 +4212,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
sta->sa_query_timed_out = 0;
#endif /* CONFIG_IEEE80211W */
- if (sta->flags & WLAN_STA_WDS) {
- int ret;
- char ifname_wds[IFNAMSIZ + 1];
-
- ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
- sta->aid, 1);
- if (!ret)
- hostapd_set_wds_encryption(hapd, sta, ifname_wds);
- }
-
if (sta->eapol_sm == NULL) {
/*
* This STA does not use RADIUS server for EAP authentication,
@@ -2900,6 +4228,27 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
hostapd_set_sta_flags(hapd, sta);
+ if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
+ wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
+ MACSTR " based on pending request",
+ MAC2STR(sta->addr));
+ sta->pending_wds_enable = 0;
+ sta->flags |= WLAN_STA_WDS;
+ }
+
+ if (sta->flags & WLAN_STA_WDS) {
+ int ret;
+ char ifname_wds[IFNAMSIZ + 1];
+
+ wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
+ MACSTR " (aid %u)",
+ MAC2STR(sta->addr), sta->aid);
+ ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+ sta->aid, 1);
+ if (!ret)
+ hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+ }
+
if (sta->auth_alg == WLAN_AUTH_FT)
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
else
@@ -2907,6 +4256,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+#ifdef CONFIG_FILS
+ if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+ fils_set_tk(sta->wpa_sm) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
if (sta->pending_eapol_rx) {
struct os_reltime now, age;
@@ -2976,6 +4337,65 @@ static void handle_disassoc_cb(struct hostapd_data *hapd,
}
+static void handle_action_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ const struct rrm_measurement_report_element *report;
+
+ if (is_multicast_ether_addr(mgmt->da))
+ return;
+#ifdef CONFIG_DPP
+ if (len >= IEEE80211_HDRLEN + 6 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ DPP_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = &mgmt->u.action.u.vs_public_action.variable[1];
+ end = ((const u8 *) mgmt) + len;
+ hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
+ return;
+ }
+ if (len >= IEEE80211_HDRLEN + 2 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ (mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_INITIAL_REQ ||
+ mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_COMEBACK_REQ)) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.public_action.variable;
+ end = ((const u8 *) mgmt) + len;
+ gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
+ return;
+ }
+#endif /* CONFIG_DPP */
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (len < 24 + 5 + sizeof(*report))
+ return;
+ report = (const struct rrm_measurement_report_element *)
+ &mgmt->u.action.u.rrm.variable[2];
+ if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
+ mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
+ report->eid == WLAN_EID_MEASURE_REQUEST &&
+ report->len >= 3 &&
+ report->type == MEASURE_TYPE_BEACON)
+ hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
+}
+
+
/**
* ieee802_11_mgmt_cb - Process management frame TX status callback
* @hapd: hostapd BSS data structure (the BSS from which the management frame
@@ -2993,8 +4413,16 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->ext_mgmt_frame_handling) {
- wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d",
- stype, ok);
+ size_t hex_len = 2 * len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "MGMT-TX-STATUS stype=%u ok=%d buf=%s",
+ stype, ok, hex);
+ os_free(hex);
+ }
return;
}
#endif /* CONFIG_TESTING_OPTIONS */
@@ -3025,6 +4453,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
+ handle_action_cb(hapd, mgmt, len, ok);
break;
default:
wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
@@ -3139,10 +4568,22 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
struct sta_info *sta;
sta = ap_get_sta(hapd, src);
- if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+ if (sta &&
+ ((sta->flags & WLAN_STA_ASSOC) ||
+ ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
if (!hapd->conf->wds_sta)
return;
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
+ WLAN_STA_ASSOC_REQ_OK) {
+ wpa_printf(MSG_DEBUG,
+ "Postpone 4-address WDS mode enabling for STA "
+ MACSTR " since TX status for AssocResp is not yet known",
+ MAC2STR(sta->addr));
+ sta->pending_wds_enable = 1;
+ return;
+ }
+
if (wds && !(sta->flags & WLAN_STA_WDS)) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 0327dec2a2bc..2f3b4da8e752 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -16,6 +16,8 @@ struct hostapd_frame_info;
struct ieee80211_ht_capabilities;
struct ieee80211_vht_capabilities;
struct ieee80211_mgmt;
+struct vlan_description;
+struct hostapd_sta_wpa_psk_short;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@@ -55,6 +57,8 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -135,4 +139,31 @@ void ap_copy_sta_supp_op_classes(struct sta_info *sta,
const u8 *supp_op_classes,
size_t supp_op_classes_len);
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len);
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *owe_dh, u8 owe_dh_len,
+ u8 *owe_buf, size_t owe_buf_len, u16 *reason);
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, size_t len, u16 auth_alg,
+ u16 auth_transaction, u16 status_code,
+ void (*cb)(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ u16 resp, struct wpabuf *data, int pub));
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
+int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+ u32 *acct_interim_interval,
+ struct vlan_description *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui,
+ int is_probe_req);
+
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index b8905373618d..5cb7fb1454f7 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -244,6 +244,7 @@ int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
* @psk: Linked list buffer for returning WPA PSK
* @identity: Buffer for returning identity (from RADIUS)
* @radius_cui: Buffer for returning CUI (from RADIUS)
+ * @is_probe_req: Whether this query for a Probe Request frame
* Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
*
* The caller is responsible for freeing the returned *identity and *radius_cui
@@ -254,7 +255,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
u32 *acct_interim_interval,
struct vlan_description *vlan_id,
struct hostapd_sta_wpa_psk_short **psk,
- char **identity, char **radius_cui)
+ char **identity, char **radius_cui,
+ int is_probe_req)
{
int res;
@@ -281,6 +283,12 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
#else /* CONFIG_NO_RADIUS */
struct hostapd_acl_query_data *query;
+ if (is_probe_req) {
+ /* Skip RADIUS queries for Probe Request frames to avoid
+ * excessive load on the authentication server. */
+ return HOSTAPD_ACL_ACCEPT;
+ };
+
/* Check whether ACL cache has an entry for this station */
res = hostapd_acl_cache_get(hapd, addr, session_timeout,
acct_interim_interval, vlan_id, psk,
@@ -327,14 +335,13 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
return HOSTAPD_ACL_REJECT;
}
- query->auth_msg = os_malloc(len);
+ query->auth_msg = os_memdup(msg, len);
if (query->auth_msg == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for "
"auth frame.");
hostapd_acl_query_free(query);
return HOSTAPD_ACL_REJECT;
}
- os_memcpy(query->auth_msg, msg, len);
query->auth_msg_len = len;
query->next = hapd->acl_queries;
hapd->acl_queries = query;
@@ -665,9 +672,11 @@ void hostapd_acl_deinit(struct hostapd_data *hapd)
#ifndef CONFIG_NO_RADIUS
hostapd_acl_cache_free(hapd->acl_cache);
+ hapd->acl_cache = NULL;
#endif /* CONFIG_NO_RADIUS */
query = hapd->acl_queries;
+ hapd->acl_queries = NULL;
while (query) {
prev = query;
query = query->next;
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 71f53b9612fa..5aece5183c69 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -23,7 +23,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
u32 *acct_interim_interval,
struct vlan_description *vlan_id,
struct hostapd_sta_wpa_psk_short **psk,
- char **identity, char **radius_cui);
+ char **identity, char **radius_cui,
+ int is_probe_req);
int hostapd_acl_init(struct hostapd_data *hapd);
void hostapd_acl_deinit(struct hostapd_data *hapd);
void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
new file mode 100644
index 000000000000..1a8d46972985
--- /dev/null
+++ b/src/ap/ieee802_11_he.c
@@ -0,0 +1,88 @@
+/*
+ * hostapd / IEEE 802.11ax HE
+ * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_he_capabilities *cap;
+ u8 *pos = eid;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(struct ieee80211_he_capabilities);
+ *pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
+
+ cap = (struct ieee80211_he_capabilities *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+
+ if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+ HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+
+ if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
+ HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+
+ if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
+ cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
+ HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+
+ pos += sizeof(*cap);
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_he_operation *oper;
+ u8 *pos = eid;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(struct ieee80211_he_operation);
+ *pos++ = WLAN_EID_EXT_HE_OPERATION;
+
+ oper = (struct ieee80211_he_operation *) pos;
+ os_memset(oper, 0, sizeof(*oper));
+
+ if (hapd->iface->conf->he_op.he_bss_color)
+ oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color;
+
+ if (hapd->iface->conf->he_op.he_default_pe_duration)
+ oper->he_oper_params |=
+ (hapd->iface->conf->he_op.he_default_pe_duration <<
+ HE_OPERATION_DFLT_PE_DURATION_OFFSET);
+
+ if (hapd->iface->conf->he_op.he_twt_required)
+ oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED;
+
+ if (hapd->iface->conf->he_op.he_rts_threshold)
+ oper->he_oper_params |=
+ (hapd->iface->conf->he_op.he_rts_threshold <<
+ HE_OPERATION_RTS_THRESHOLD_OFFSET);
+
+ /* TODO: conditional MaxBSSID Indicator subfield */
+
+ pos += sizeof(*oper);
+
+ return pos;
+}
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 5eb1060a2965..214855dccb8c 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -236,17 +236,29 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
int i;
const u8 *start = (const u8 *) mgmt;
const u8 *data = start + IEEE80211_HDRLEN + 2;
+ struct sta_info *sta;
+
+ wpa_printf(MSG_DEBUG,
+ "HT: Received 20/40 BSS Coexistence Management frame from "
+ MACSTR, MAC2STR(mgmt->sa));
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
mgmt->u.action.u.public_action.action);
- if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
return;
+ }
- if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
+ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore too short 20/40 BSS Coexistence Management frame");
return;
+ }
+ /* 20/40 BSS Coexistence element */
bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
bc_ie->length < 1) {
@@ -254,13 +266,35 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
bc_ie->element_id, bc_ie->length);
return;
}
- if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
+ if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
+ wpa_printf(MSG_DEBUG,
+ "Truncated 20/40 BSS Coexistence element");
return;
+ }
data += 2 + bc_ie->length;
- wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
- bc_ie->coex_param);
+ wpa_printf(MSG_DEBUG,
+ "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
+ bc_ie->coex_param,
+ (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
+ (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
+ (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
+ (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
+ (bc_ie->coex_param & BIT(4)) ?
+ "[OBSSScanExemptionGrant]" : "",
+ (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
+ "[Reserved]" : "");
+
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+ /* Intra-BSS communication prohibiting 20/40 MHz BSS operation
+ */
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
+ return;
+ }
+
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -269,6 +303,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
}
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+ /* Inter-BSS communication prohibiting 20/40 MHz BSS operation
+ */
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -276,12 +312,16 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
is_ht40_allowed = 0;
}
- if (start + len - data >= 3 &&
- data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+ /* 20/40 BSS Intolerant Channel Report element (zero or more times) */
+ while (start + len - data >= 3 &&
+ data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
u8 ielen = data[1];
- if (ielen > start + len - data - 2)
+ if (ielen > start + len - data - 2) {
+ wpa_printf(MSG_DEBUG,
+ "Truncated 20/40 BSS Intolerant Channel Report element");
return;
+ }
ic_report = (struct ieee80211_2040_intol_chan_report *) data;
wpa_printf(MSG_DEBUG,
"20/40 BSS Intolerant Channel Report: Operating Class %u",
@@ -292,8 +332,10 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
for (i = 0; i < ielen - 1; i++) {
u8 chan = ic_report->variable[i];
+ if (chan == iface->conf->channel)
+ continue; /* matching own primary channel */
if (is_40_allowed(iface, chan))
- continue;
+ continue; /* not within affected channels */
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -301,6 +343,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
chan);
is_ht40_allowed = 0;
}
+
+ data += 2 + ielen;
}
wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
is_ht40_allowed, iface->num_sta_ht40_intolerant);
@@ -340,8 +384,8 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
* that did not specify a valid WMM IE in the (Re)Association Request
* frame.
*/
- if (!ht_capab ||
- !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) {
+ if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
+ !hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
sta->flags &= ~WLAN_STA_HT;
os_free(sta->ht_capabilities);
sta->ht_capabilities = NULL;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 259413bd12ff..49e9bf8cc7c9 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -178,6 +178,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
case 1: /* Bits 8-15 */
if (hapd->conf->proxy_arp)
*pos |= 0x10; /* Bit 12 - Proxy ARP */
+ if (hapd->conf->coloc_intf_reporting) {
+ /* Bit 13 - Collocated Interference Reporting */
+ *pos |= 0x20;
+ }
break;
case 2: /* Bits 16-23 */
if (hapd->conf->wnm_sleep_mode)
@@ -186,9 +190,9 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
*pos |= 0x08; /* Bit 19 - BSS Transition */
break;
case 3: /* Bits 24-31 */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
*pos |= 0x02; /* Bit 25 - SSID List */
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
if (hapd->conf->time_advertisement == 2)
*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
if (hapd->conf->interworking)
@@ -218,12 +222,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
if (hapd->conf->ssid.utf8_ssid)
*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
break;
+ case 7: /* Bits 56-63 */
+ break;
case 8: /* Bits 64-71 */
if (hapd->conf->ftm_responder)
*pos |= 0x40; /* Bit 70 - FTM responder */
if (hapd->conf->ftm_initiator)
*pos |= 0x80; /* Bit 71 - FTM initiator */
break;
+ case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+ if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
+ wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+ *pos |= 0x01;
+#endif /* CONFIG_FILS */
+ break;
}
}
@@ -246,10 +259,10 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
if (len < 9 &&
(hapd->conf->ftm_initiator || hapd->conf->ftm_responder))
len = 9;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
if (len < 4)
len = 4;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_HS20
if (hapd->conf->hs20 && len < 6)
len = 6;
@@ -258,6 +271,11 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
if (hapd->conf->mbo_enabled && len < 6)
len = 6;
#endif /* CONFIG_MBO */
+#ifdef CONFIG_FILS
+ if ((!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
+ len = 10;
+#endif /* CONFIG_FILS */
if (len < hapd->iface->extended_capa_len)
len = hapd->iface->extended_capa_len;
if (len == 0)
@@ -432,7 +450,7 @@ u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
{
size_t len;
- if (hapd->conf->time_advertisement != 2)
+ if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
return eid;
len = os_strlen(hapd->conf->time_zone);
@@ -503,7 +521,7 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
if (hapd->conf->ap_max_inactivity > 0) {
unsigned int val;
*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
@@ -521,7 +539,7 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
pos += 2;
*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
}
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
return pos;
}
@@ -531,23 +549,38 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
{
- u8 mbo[6], *mbo_pos = mbo;
+ u8 mbo[9], *mbo_pos = mbo;
u8 *pos = eid;
- if (!hapd->conf->mbo_enabled)
+ if (!hapd->conf->mbo_enabled &&
+ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
return eid;
- *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
- *mbo_pos++ = 1;
- /* Not Cellular aware */
- *mbo_pos++ = 0;
+ if (hapd->conf->mbo_enabled) {
+ *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
+ *mbo_pos++ = 1;
+ /* Not Cellular aware */
+ *mbo_pos++ = 0;
+ }
- if (hapd->mbo_assoc_disallow) {
+ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
*mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
*mbo_pos++ = 1;
*mbo_pos++ = hapd->mbo_assoc_disallow;
}
+ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
+ u8 ctrl;
+
+ ctrl = OCE_RELEASE;
+ if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
+ ctrl |= OCE_IS_STA_CFON;
+
+ *mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = ctrl;
+ }
+
pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
return pos;
@@ -556,19 +589,91 @@ u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
{
- if (!hapd->conf->mbo_enabled)
+ u8 len;
+
+ if (!hapd->conf->mbo_enabled &&
+ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
return 0;
/*
* MBO IE header (6) + Capability Indication attribute (3) +
* Association Disallowed attribute (3) = 12
*/
- return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+ len = 6;
+ if (hapd->conf->mbo_enabled)
+ len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+
+ /* OCE capability indication attribute (3) */
+ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd))
+ len += 3;
+
+ return len;
}
#endif /* CONFIG_MBO */
+#ifdef CONFIG_OWE
+static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
+{
+ return hapd->conf->owe_transition_ssid_len > 0 &&
+ !is_zero_ether_addr(hapd->conf->owe_transition_bssid);
+}
+#endif /* CONFIG_OWE */
+
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_OWE
+ if (!hostapd_eid_owe_trans_enabled(hapd))
+ return 0;
+ return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
+#else /* CONFIG_OWE */
+ return 0;
+#endif /* CONFIG_OWE */
+}
+
+
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
+ size_t len)
+{
+#ifdef CONFIG_OWE
+ u8 *pos = eid;
+ size_t elen;
+
+ if (hapd->conf->owe_transition_ifname[0] &&
+ !hostapd_eid_owe_trans_enabled(hapd))
+ hostapd_owe_trans_get_info(hapd);
+
+ if (!hostapd_eid_owe_trans_enabled(hapd))
+ return pos;
+
+ elen = hostapd_eid_owe_trans_len(hapd);
+ if (len < elen) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Not enough room in the buffer for OWE IE");
+ return pos;
+ }
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = elen - 2;
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = OWE_OUI_TYPE;
+ os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ *pos++ = hapd->conf->owe_transition_ssid_len;
+ os_memcpy(pos, hapd->conf->owe_transition_ssid,
+ hapd->conf->owe_transition_ssid_len);
+ pos += hapd->conf->owe_transition_ssid_len;
+
+ return pos;
+#else /* CONFIG_OWE */
+ return eid;
+#endif /* CONFIG_OWE */
+}
+
+
void ap_copy_sta_supp_op_classes(struct sta_info *sta,
const u8 *supp_op_classes,
size_t supp_op_classes_len)
@@ -584,3 +689,66 @@ void ap_copy_sta_supp_op_classes(struct sta_info *sta,
os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
supp_op_classes_len);
}
+
+
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_FILS
+ u8 *len;
+ u16 fils_info = 0;
+ size_t realms;
+ struct fils_realm *realm;
+
+ if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+ return pos;
+
+ realms = dl_list_len(&hapd->conf->fils_realms);
+ if (realms > 7)
+ realms = 7; /* 3 bit count field limits this to max 7 */
+
+ *pos++ = WLAN_EID_FILS_INDICATION;
+ len = pos++;
+ /* TODO: B0..B2: Number of Public Key Identifiers */
+ if (hapd->conf->erp_domain) {
+ /* B3..B5: Number of Realm Identifiers */
+ fils_info |= realms << 3;
+ }
+ /* TODO: B6: FILS IP Address Configuration */
+ if (hapd->conf->fils_cache_id_set)
+ fils_info |= BIT(7);
+ if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
+ fils_info |= BIT(8); /* HESSID Included */
+ /* FILS Shared Key Authentication without PFS Supported */
+ fils_info |= BIT(9);
+ if (hapd->conf->fils_dh_group) {
+ /* FILS Shared Key Authentication with PFS Supported */
+ fils_info |= BIT(10);
+ }
+ /* TODO: B11: FILS Public Key Authentication Supported */
+ /* B12..B15: Reserved */
+ WPA_PUT_LE16(pos, fils_info);
+ pos += 2;
+ if (hapd->conf->fils_cache_id_set) {
+ os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
+ pos += FILS_CACHE_ID_LEN;
+ }
+ if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
+ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm,
+ list) {
+ if (realms == 0)
+ break;
+ realms--;
+ os_memcpy(pos, realm->hash, 2);
+ pos += 2;
+ }
+ *len = pos - len - 1;
+#endif /* CONFIG_FILS */
+
+ return pos;
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index f30f63bc5709..8d0662078bcf 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -334,7 +334,7 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
{
/* Disable VHT caps for STAs associated to no-VHT BSSes. */
if (!vht_capab ||
- hapd->conf->disable_11ac ||
+ !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
!check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
sta->flags &= ~WLAN_STA_VHT;
os_free(sta->vht_capabilities);
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 80ff996948f9..185279f9e3ef 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -31,6 +31,8 @@
#include "ap_drv_ops.h"
#include "wps_hostapd.h"
#include "hs20.h"
+/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
+#include "ieee802_11.h"
#include "ieee802_1x.h"
@@ -316,6 +318,7 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
hdr->code != EAP_CODE_INITIATE))
return;
+ eap_erp_update_identity(sm->eap, eap, len);
identity = eap_get_identity(sm->eap, &identity_len);
if (identity == NULL)
return;
@@ -472,7 +475,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd,
}
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
sta->wpa_sm &&
(wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
@@ -485,7 +488,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd,
wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
return -1;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
@@ -588,9 +591,9 @@ int add_common_radius_attr(struct hostapd_data *hapd,
}
-static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *eap, size_t len)
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *eap, size_t len)
{
struct radius_msg *msg;
struct eapol_state_machine *sm = sta->eapol_sm;
@@ -680,6 +683,8 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
#ifdef CONFIG_HS20
if (hapd->conf->hs20) {
u8 ver = 1; /* Release 2 */
+ if (HS20_VERSION > 0x10)
+ ver = 2; /* Release 3 */
if (!radius_msg_add_wfa(
msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
&ver, 1)) {
@@ -709,6 +714,41 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
goto fail;
}
}
+
+ if (sta->roaming_consortium &&
+ !radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM,
+ wpabuf_head(sta->roaming_consortium),
+ wpabuf_len(sta->roaming_consortium))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 Roaming Consortium");
+ goto fail;
+ }
+
+ if (hapd->conf->t_c_filename) {
+ be32 timestamp;
+
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME,
+ (const u8 *) hapd->conf->t_c_filename,
+ os_strlen(hapd->conf->t_c_filename))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 T&C Filename");
+ goto fail;
+ }
+
+ timestamp = host_to_be32(hapd->conf->t_c_timestamp);
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP,
+ (const u8 *) &timestamp,
+ sizeof(timestamp))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 Timestamp");
+ goto fail;
+ }
+ }
}
#endif /* CONFIG_HS20 */
@@ -845,7 +885,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
}
-static struct eapol_state_machine *
+struct eapol_state_machine *
ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
{
int flags = 0;
@@ -970,7 +1010,9 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
}
key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
- if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+ if (key_mgmt != -1 &&
+ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+ key_mgmt == WPA_KEY_MGMT_DPP)) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
"STA is using PSK");
return;
@@ -1113,7 +1155,9 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
}
key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
- if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+ if (key_mgmt != -1 &&
+ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+ key_mgmt == WPA_KEY_MGMT_DPP)) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
/*
* Clear any possible EAPOL authenticator state to support
@@ -1154,7 +1198,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
sta->eapol_sm->eap_if->portEnabled = TRUE;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sta->auth_alg == WLAN_AUTH_FT) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG,
@@ -1170,10 +1214,32 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
sta->eapol_sm->portValid = TRUE;
if (sta->eapol_sm->eap)
eap_sm_notify_cached(sta->eapol_sm->eap);
- /* TODO: get vlan_id from R0KH using RRB message */
+ ap_sta_bind_vlan(hapd, sta);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMK from FILS - skip IEEE 802.1X/EAP");
+ /* Setup EAPOL state machines to already authenticated state
+ * because of existing FILS information. */
+ sta->eapol_sm->keyRun = TRUE;
+ sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = TRUE;
+ sta->eapol_sm->authFail = FALSE;
+ sta->eapol_sm->portValid = TRUE;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
return;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
if (pmksa) {
@@ -1395,11 +1461,10 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
}
} while (class_len < 1);
- nclass[nclass_count].data = os_malloc(class_len);
+ nclass[nclass_count].data = os_memdup(attr_class, class_len);
if (nclass[nclass_count].data == NULL)
break;
- os_memcpy(nclass[nclass_count].data, attr_class, class_len);
nclass[nclass_count].len = class_len;
nclass_count++;
}
@@ -1559,6 +1624,33 @@ static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
ap_sta_session_warning_timeout(hapd, sta, warning_time);
}
+
+static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len)
+{
+ if (len < 4)
+ return; /* Malformed information */
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
+ pos[0], pos[1], pos[2], pos[3]);
+ hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
+}
+
+
+static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos, size_t len)
+{
+ os_free(sta->t_c_url);
+ sta->t_c_url = os_malloc(len + 1);
+ if (!sta->t_c_url)
+ return;
+ os_memcpy(sta->t_c_url, pos, len);
+ sta->t_c_url[len] = '\0';
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions URL %s", sta->t_c_url);
+}
+
#endif /* CONFIG_HS20 */
@@ -1606,6 +1698,12 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
session_timeout);
break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
+ ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
+ ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
+ break;
}
}
#endif /* CONFIG_HS20 */
@@ -1663,6 +1761,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
struct sta_info *sta;
u32 session_timeout = 0, termination_action, acct_interim_interval;
int session_timeout_set;
+ u32 reason_code;
struct eapol_state_machine *sm;
int override_eapReq = 0;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
@@ -1788,14 +1887,17 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
break;
sta->session_timeout_set = !!session_timeout_set;
- sta->session_timeout = session_timeout;
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
/* RFC 3580, Ch. 3.17 */
if (session_timeout_set && termination_action ==
- RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+ RADIUS_TERMINATION_ACTION_RADIUS_REQUEST)
sm->reAuthPeriod = session_timeout;
- } else if (session_timeout_set)
+ else if (session_timeout_set)
ap_sta_session_timeout(hapd, sta, session_timeout);
+ else
+ ap_sta_no_session_timeout(hapd, sta);
sm->eap_if->aaaSuccess = TRUE;
override_eapReq = 1;
@@ -1811,6 +1913,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
case RADIUS_CODE_ACCESS_REJECT:
sm->eap_if->aaaFail = TRUE;
override_eapReq = 1;
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
+ &reason_code) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for "
+ MACSTR, reason_code, MAC2STR(sta->addr));
+ sta->disconnect_reason_code = reason_code;
+ }
break;
case RADIUS_CODE_ACCESS_CHALLENGE:
sm->eap_if->aaaEapReq = TRUE;
@@ -1837,6 +1946,19 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
if (override_eapReq)
sm->eap_if->aaaEapReq = FALSE;
+#ifdef CONFIG_FILS
+#ifdef NEED_AP_MLME
+ if (sta->flags & WLAN_STA_PENDING_FILS_ERP) {
+ /* TODO: Add a PMKSA entry on success? */
+ ieee802_11_finish_fils_auth(
+ hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT,
+ sm->eap_if->aaaEapReqData,
+ sm->eap_if->aaaEapKeyData,
+ sm->eap_if->aaaEapKeyDataLen);
+ }
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_FILS */
+
eapol_auth_step(sm);
return RADIUS_RX_QUEUED;
@@ -1924,7 +2046,7 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
eapol->default_wep_key_idx);
-
+
if (ieee802_1x_rekey_broadcast(hapd)) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_WARNING, "failed to generate a "
@@ -2034,13 +2156,19 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
}
if (eap_user->password) {
- user->password = os_malloc(eap_user->password_len);
+ user->password = os_memdup(eap_user->password,
+ eap_user->password_len);
if (user->password == NULL)
goto out;
- os_memcpy(user->password, eap_user->password,
- eap_user->password_len);
user->password_len = eap_user->password_len;
user->password_hash = eap_user->password_hash;
+ if (eap_user->salt && eap_user->salt_len) {
+ user->salt = os_memdup(eap_user->salt,
+ eap_user->salt_len);
+ if (!user->salt)
+ goto out;
+ user->salt_len = eap_user->salt_len;
+ }
}
user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
@@ -2190,6 +2318,7 @@ int ieee802_1x_init(struct hostapd_data *hapd)
conf.erp_domain = hapd->conf->erp_domain;
conf.erp = hapd->conf->eap_server_erp;
conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+ conf.tls_flags = hapd->conf->tls_flags;
conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
@@ -2326,6 +2455,16 @@ int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
MAC2STR(sta->addr), xhdr->version, xhdr->type,
be_to_host16(xhdr->length), ack);
+#ifdef CONFIG_WPS
+ if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack &&
+ (sta->flags & WLAN_STA_WPS) &&
+ ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Indicate EAP completion on ACK for EAP-Failure");
+ hostapd_wps_eap_completed(hapd);
+ }
+#endif /* CONFIG_WPS */
+
if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
return 0;
@@ -2642,6 +2781,15 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
sta->hs20_deauth_req);
}
+
+ if (sta->hs20_t_c_filtering) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+ MACSTR " to indicate Terms and Conditions filtering",
+ MAC2STR(sta->addr));
+ hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url);
+ os_free(sta->t_c_url);
+ sta->t_c_url = NULL;
+ }
}
#endif /* CONFIG_HS20 */
@@ -2655,6 +2803,7 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
/* TODO: get PMKLifetime from WPA parameters */
static const int dot11RSNAConfigPMKLifetime = 43200;
unsigned int session_timeout;
+ struct os_reltime now, remaining;
#ifdef CONFIG_HS20
if (remediation && !sta->remediation) {
@@ -2665,7 +2814,8 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
sta->remediation_method = 1; /* SOAP-XML SPP */
}
- if (success && (sta->remediation || sta->hs20_deauth_req)) {
+ if (success && (sta->remediation || sta->hs20_deauth_req ||
+ sta->hs20_t_c_filtering)) {
wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
MACSTR " in 100 ms", MAC2STR(sta->addr));
eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
@@ -2675,10 +2825,13 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
#endif /* CONFIG_HS20 */
key = ieee802_1x_get_key(sta->eapol_sm, &len);
- if (sta->session_timeout_set)
- session_timeout = sta->session_timeout;
- else
+ if (sta->session_timeout_set) {
+ os_get_reltime(&now);
+ os_reltime_sub(&sta->session_timeout, &now, &remaining);
+ session_timeout = (remaining.sec > 0) ? remaining.sec : 1;
+ } else {
session_timeout = dot11RSNAConfigPMKLifetime;
+ }
if (success && key && len >= PMK_LEN && !sta->remediation &&
!sta->hs20_deauth_requested &&
wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
@@ -2699,15 +2852,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
* EAP-FAST with anonymous provisioning, may require another
* EAPOL authentication to be started to complete connection.
*/
- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force "
- "disconnection after EAP-Failure");
- /* Add a small sleep to increase likelihood of previously
- * requested EAP-Failure TX getting out before this should the
- * driver reorder operations.
- */
- os_sleep(0, 10000);
- ap_sta_disconnect(hapd, sta, sta->addr,
- WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
- hostapd_wps_eap_completed(hapd);
+ ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta);
}
}
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index ec80199007b6..9594661be8ee 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -57,5 +57,10 @@ int add_common_radius_attr(struct hostapd_data *hapd,
struct hostapd_radius_attr *req_attr,
struct sta_info *sta,
struct radius_msg *msg);
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *eap, size_t len);
+struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
#endif /* IEEE802_1X_H */
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 3c086bfc7131..4d6a92e086bd 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -182,4 +182,5 @@ int ndisc_snoop_init(struct hostapd_data *hapd)
void ndisc_snoop_deinit(struct hostapd_data *hapd)
{
l2_packet_deinit(hapd->sock_ndisc);
+ hapd->sock_ndisc = NULL;
}
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index a2efff618286..b8fd5924b889 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -43,6 +43,7 @@ static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
nr->civic = NULL;
os_memset(nr->bssid, 0, sizeof(nr->bssid));
os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+ nr->stationary = 0;
}
@@ -64,7 +65,7 @@ hostapd_neighbor_add(struct hostapd_data *hapd)
int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid,
const struct wpabuf *nr, const struct wpabuf *lci,
- const struct wpabuf *civic)
+ const struct wpabuf *civic, int stationary)
{
struct hostapd_neighbor_entry *entry;
@@ -83,18 +84,20 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
if (!entry->nr)
goto fail;
- if (lci) {
+ if (lci && wpabuf_len(lci)) {
entry->lci = wpabuf_dup(lci);
if (!entry->lci || os_get_time(&entry->lci_date))
goto fail;
}
- if (civic) {
+ if (civic && wpabuf_len(civic)) {
entry->civic = wpabuf_dup(civic);
if (!entry->civic)
goto fail;
}
+ entry->stationary = stationary;
+
return 0;
fail:
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index c22e043c120e..ba46d88435e5 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -16,7 +16,7 @@ hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid,
const struct wpabuf *nr, const struct wpabuf *lci,
- const struct wpabuf *civic);
+ const struct wpabuf *civic, int stationary);
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid);
void hostpad_free_neighbor_db(struct hostapd_data *hapd);
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
deleted file mode 100644
index efc1d7e4c78f..000000000000
--- a/src/ap/peerkey_auth.c
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * hostapd - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2009, Jouni Malinen <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 "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "crypto/random.h"
-#include "wpa_auth.h"
-#include "wpa_auth_i.h"
-#include "wpa_auth_ie.h"
-
-#ifdef CONFIG_PEERKEY
-
-static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx)
-{
-#if 0
- struct wpa_authenticator *wpa_auth = eloop_ctx;
- struct wpa_stsl_negotiation *neg = timeout_ctx;
-#endif
-
- /* TODO: ? */
-}
-
-
-struct wpa_stsl_search {
- const u8 *addr;
- struct wpa_state_machine *sm;
-};
-
-
-static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx)
-{
- struct wpa_stsl_search *search = ctx;
- if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) {
- search->sm = sm;
- return 1;
- }
- return 0;
-}
-
-
-static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, const u8 *peer,
- u16 mui, u16 error_type)
-{
- u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN +
- 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)];
- u8 *pos;
- struct rsn_error_kde error;
-
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
- "Sending SMK Error");
-
- pos = kde;
-
- if (peer) {
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN,
- NULL, 0);
- }
-
- error.mui = host_to_be16(mui);
- error.error_type = host_to_be16(error_type);
- pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR,
- (u8 *) &error, sizeof(error), NULL, 0);
-
- __wpa_send_eapol(wpa_auth, sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR,
- NULL, NULL, kde, pos - kde, 0, 0, 0);
-}
-
-
-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
- const u8 *key_data, size_t key_data_len)
-{
- struct wpa_eapol_ie_parse kde;
- struct wpa_stsl_search search;
- u8 *buf, *pos;
- size_t buf_len;
-
- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
- return;
- }
-
- if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
- kde.mac_addr_len < ETH_ALEN) {
- wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
- "SMK M1");
- return;
- }
-
- /* Initiator = sm->addr; Peer = kde.mac_addr */
-
- search.addr = kde.mac_addr;
- search.sm = NULL;
- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
- 0 || search.sm == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
- " aborted - STA not associated anymore",
- MAC2STR(kde.mac_addr));
- wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
- STK_ERR_STA_NR);
- /* FIX: wpa_stsl_remove(wpa_auth, neg); */
- return;
- }
-
- buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
- buf = os_malloc(buf_len);
- if (buf == NULL)
- return;
- /* Initiator RSN IE */
- os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len);
- pos = buf + kde.rsn_ie_len;
- /* Initiator MAC Address */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN,
- NULL, 0);
-
- /* SMK M2:
- * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
- * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE)
- */
-
- wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG,
- "Sending SMK M2");
-
- __wpa_send_eapol(wpa_auth, search.sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE,
- NULL, key->key_nonce, buf, pos - buf, 0, 0, 0);
-
- os_free(buf);
-}
-
-
-static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- struct wpa_eapol_key *key,
- struct wpa_eapol_ie_parse *kde,
- const u8 *smk)
-{
- u8 *buf, *pos;
- size_t buf_len;
- u32 lifetime;
-
- /* SMK M4:
- * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce,
- * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE,
- * Lifetime KDE)
- */
-
- buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN +
- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
- 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
- 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
- pos = buf = os_malloc(buf_len);
- if (buf == NULL)
- return;
-
- /* Initiator MAC Address */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN,
- NULL, 0);
-
- /* Initiator Nonce */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN,
- NULL, 0);
-
- /* SMK with PNonce */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
- key->key_nonce, WPA_NONCE_LEN);
-
- /* Lifetime */
- lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
- (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
-
- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "Sending SMK M4");
-
- __wpa_send_eapol(wpa_auth, sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE,
- NULL, key->key_nonce, buf, pos - buf, 0, 1, 0);
-
- os_free(buf);
-}
-
-
-static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- struct wpa_eapol_key *key,
- struct wpa_eapol_ie_parse *kde,
- const u8 *smk, const u8 *peer)
-{
- u8 *buf, *pos;
- size_t buf_len;
- u32 lifetime;
-
- /* SMK M5:
- * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
- * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE,
- * Lifetime KDE))
- */
-
- buf_len = kde->rsn_ie_len +
- 2 + RSN_SELECTOR_LEN + ETH_ALEN +
- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
- 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
- 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
- pos = buf = os_malloc(buf_len);
- if (buf == NULL)
- return;
-
- /* Peer RSN IE */
- os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
- pos += kde->rsn_ie_len;
-
- /* Peer MAC Address */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
-
- /* PNonce */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce,
- WPA_NONCE_LEN, NULL, 0);
-
- /* SMK and INonce */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
- kde->nonce, WPA_NONCE_LEN);
-
- /* Lifetime */
- lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
- (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
-
- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "Sending SMK M5");
-
- __wpa_send_eapol(wpa_auth, sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_SMK_MESSAGE,
- NULL, kde->nonce, buf, pos - buf, 0, 1, 0);
-
- os_free(buf);
-}
-
-
-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
- const u8 *key_data, size_t key_data_len)
-{
- struct wpa_eapol_ie_parse kde;
- struct wpa_stsl_search search;
- u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
-
- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
- return;
- }
-
- if (kde.rsn_ie == NULL ||
- kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
- kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) {
- wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or "
- "Nonce KDE in SMK M3");
- return;
- }
-
- /* Peer = sm->addr; Initiator = kde.mac_addr;
- * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */
-
- search.addr = kde.mac_addr;
- search.sm = NULL;
- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
- 0 || search.sm == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
- " aborted - STA not associated anymore",
- MAC2STR(kde.mac_addr));
- wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
- STK_ERR_STA_NR);
- /* FIX: wpa_stsl_remove(wpa_auth, neg); */
- return;
- }
-
- if (random_get_bytes(smk, PMK_LEN)) {
- wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
- return;
- }
-
- /* SMK = PRF-256(Random number, "SMK Derivation",
- * AA || Time || INonce || PNonce)
- */
- os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
- pos = buf + ETH_ALEN;
- wpa_get_ntp_timestamp(pos);
- pos += 8;
- os_memcpy(pos, kde.nonce, WPA_NONCE_LEN);
- pos += WPA_NONCE_LEN;
- os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN);
-#ifdef CONFIG_IEEE80211W
- sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
- smk, PMK_LEN);
-#else /* CONFIG_IEEE80211W */
- sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
- smk, PMK_LEN);
-#endif /* CONFIG_IEEE80211W */
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN);
-
- wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk);
- wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr);
-
- /* Authenticator does not need SMK anymore and it is required to forget
- * it. */
- os_memset(smk, 0, sizeof(*smk));
-}
-
-
-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- const u8 *key_data, size_t key_data_len)
-{
- struct wpa_eapol_ie_parse kde;
- struct wpa_stsl_search search;
- struct rsn_error_kde error;
- u16 mui, error_type;
-
- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
- return;
- }
-
- if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
- kde.error == NULL || kde.error_len < sizeof(error)) {
- wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in "
- "SMK Error");
- return;
- }
-
- search.addr = kde.mac_addr;
- search.sm = NULL;
- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
- 0 || search.sm == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not "
- "associated for SMK Error message from " MACSTR,
- MAC2STR(kde.mac_addr), MAC2STR(sm->addr));
- return;
- }
-
- os_memcpy(&error, kde.error, sizeof(error));
- mui = be_to_host16(error.mui);
- error_type = be_to_host16(error.error_type);
- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
- "STA reported SMK Error: Peer " MACSTR
- " MUI %d Error Type %d",
- MAC2STR(kde.mac_addr), mui, error_type);
-
- wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type);
-}
-
-
-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
- struct wpa_stsl_negotiation *neg)
-{
- struct wpa_stsl_negotiation *pos, *prev;
-
- if (wpa_auth == NULL)
- return -1;
- pos = wpa_auth->stsl_negotiations;
- prev = NULL;
- while (pos) {
- if (pos == neg) {
- if (prev)
- prev->next = pos->next;
- else
- wpa_auth->stsl_negotiations = pos->next;
-
- eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos);
- os_free(pos);
- return 0;
- }
- prev = pos;
- pos = pos->next;
- }
-
- return -1;
-}
-
-#endif /* CONFIG_PEERKEY */
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index d610e7e5b005..15e2c4943f2b 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -282,7 +282,42 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp)
{
- struct rsn_pmksa_cache_entry *entry, *pos;
+ struct rsn_pmksa_cache_entry *entry;
+
+ entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
+ aa, spa, session_timeout, eapol,
+ akmp);
+
+ if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
+ return NULL;
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_create_entry - Create a PMKSA cache entry
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @pmkid: Calculated PMKID
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function creates a PMKSA entry.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp)
+{
+ struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
if (pmk_len > PMK_LEN_MAX)
@@ -303,8 +338,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
else if (wpa_key_mgmt_suite_b(akmp))
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
else
- rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
- wpa_key_mgmt_sha256(akmp));
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
os_get_reltime(&now);
entry->expiration = now.sec;
if (session_timeout > 0)
@@ -315,9 +349,30 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
os_memcpy(entry->spa, spa, ETH_ALEN);
pmksa_cache_from_eapol_data(entry, eapol);
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_add_entry - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @entry: Pointer to PMKSA cache entry
+ *
+ * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
+ * already in the cache for the same Supplicant, this entry will be replaced
+ * with the new entry. PMKID will be calculated based on the PMK.
+ */
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *pos;
+
+ if (entry == NULL)
+ return -1;
+
/* Replace an old entry for the same STA (if found) with the new entry
*/
- pos = pmksa_cache_auth_get(pmksa, spa, NULL);
+ pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
if (pos)
pmksa_cache_free_entry(pmksa, pos);
@@ -331,7 +386,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
pmksa_cache_link_entry(pmksa, entry);
- return entry;
+ return 0;
}
@@ -462,7 +517,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
continue;
rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
- wpa_key_mgmt_sha256(entry->akmp));
+ entry->akmp);
if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
return entry;
}
@@ -605,3 +660,70 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
}
return pos - buf;
}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+/**
+ * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @addr: MAC address of the peer (NULL means any)
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store
+ * in external storage.
+ */
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+ char *buf, size_t len)
+{
+ int ret;
+ char *pos, *end;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ pos = buf;
+ end = buf + len;
+ os_get_reltime(&now);
+
+
+ /*
+ * Entry format:
+ * <BSSID> <PMKID> <PMK> <expiration in seconds>
+ */
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
+ continue;
+
+ ret = os_snprintf(pos, end - pos, MACSTR " ",
+ MAC2STR(entry->spa));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid,
+ PMKID_LEN);
+
+ ret = os_snprintf(pos, end - pos, " ");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ pos += wpa_snprintf_hex(pos, end - pos, entry->pmk,
+ entry->pmk_len);
+
+ ret = os_snprintf(pos, end - pos, " %d\n",
+ (int) (entry->expiration - now.sec));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index d8d9c5a25c0e..2ef217435b11 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -35,6 +35,7 @@ struct rsn_pmksa_cache_entry {
};
struct rsn_pmksa_cache;
+struct radius_das_attrs;
struct rsn_pmksa_cache *
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
@@ -53,6 +54,13 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp);
struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp);
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
const u8 *aa, const u8 *pmkid);
@@ -65,5 +73,7 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
struct radius_das_attrs *attr);
int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+ char *buf, size_t len);
#endif /* PMKSA_CACHE_H */
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
index 3569f955bcd2..56ed29c1c068 100644
--- a/src/ap/rrm.c
+++ b/src/ap/rrm.c
@@ -2,6 +2,7 @@
* hostapd / Radio Measurement (RRM)
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +11,7 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "ap_drv_ops.h"
#include "sta_info.h"
@@ -69,24 +71,47 @@ static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
}
+static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+ const u8 *addr, u8 token, u8 rep_mode,
+ const u8 *pos, size_t len)
+{
+ char report[2 * 255 + 1];
+
+ wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
+ token, len, MAC2STR(addr));
+ /* Skip to the beginning of the Beacon report */
+ if (len < 3)
+ return;
+ pos += 3;
+ len -= 3;
+ report[0] = '\0';
+ if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+ MAC2STR(addr), token, rep_mode, report);
+}
+
+
static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
const u8 *buf, size_t len)
{
const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
const u8 *pos, *ie, *end;
- u8 token;
+ u8 token, rep_mode;
end = buf + len;
token = mgmt->u.action.u.rrm.dialog_token;
pos = mgmt->u.action.u.rrm.variable;
while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
- if (ie[1] < 5) {
+ if (ie[1] < 3) {
wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
break;
}
- wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]);
+ rep_mode = ie[3];
+ wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
+ rep_mode, ie[4]);
switch (ie[4]) {
case MEASURE_TYPE_LCI:
@@ -95,6 +120,10 @@ static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
case MEASURE_TYPE_FTM_RANGE:
hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
break;
+ case MEASURE_TYPE_BEACON:
+ hostapd_handle_beacon_report(hapd, mgmt->sa, token,
+ rep_mode, ie + 2, ie[1]);
+ break;
default:
wpa_printf(MSG_DEBUG,
"Measurement report type %u is not supported",
@@ -118,7 +147,7 @@ static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
/* Subelements are arranged as IEs */
subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
if (subelem && subelem[1] == 2)
- return *(u16 *) (subelem + 2);
+ return WPA_GET_LE16(subelem + 2);
return 0;
}
@@ -129,12 +158,12 @@ static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
struct os_time curr, diff;
unsigned long diff_l;
+ if (nr->stationary || max_age == 0xffff)
+ return 1;
+
if (!max_age)
return 0;
- if (max_age == 0xffff)
- return 1;
-
if (os_get_time(&curr))
return 0;
@@ -341,13 +370,7 @@ int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
struct sta_info *sta = ap_get_sta(hapd, addr);
int ret;
- if (!sta) {
- wpa_printf(MSG_INFO,
- "Request LCI: Destination address is not in station list");
- return -1;
- }
-
- if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
wpa_printf(MSG_INFO,
"Request LCI: Destination address is not connected");
return -1;
@@ -450,9 +473,8 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
wpa_printf(MSG_DEBUG,
"Request range: Range request is already in process; overriding");
hapd->range_req_active = 0;
- eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
- hostapd_range_rep_timeout_handler, hapd,
- NULL);
+ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
+ NULL);
}
/* Action + measurement type + token + reps + EID + len = 7 */
@@ -542,3 +564,111 @@ void hostapd_clean_rrm(struct hostapd_data *hapd)
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
hapd->range_req_active = 0;
}
+
+
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+ u8 req_mode, const struct wpabuf *req)
+{
+ struct wpabuf *buf;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ int ret;
+ enum beacon_report_mode mode;
+ const u8 *pos;
+
+ /* Request data:
+ * Operating Class (1), Channel Number (1), Randomization Interval (2),
+ * Measurement Duration (2), Measurement Mode (1), BSSID (6),
+ * Optional Subelements (variable)
+ */
+ if (wpabuf_len(req) < 13) {
+ wpa_printf(MSG_INFO, "Beacon request: Too short request data");
+ return -1;
+ }
+ pos = wpabuf_head(req);
+ mode = pos[6];
+
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR " is not connected",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ switch (mode) {
+ case BEACON_REPORT_MODE_PASSIVE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support passive beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ case BEACON_REPORT_MODE_ACTIVE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support active beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ case BEACON_REPORT_MODE_TABLE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support table beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "Beacon request: Unknown measurement mode %d", mode);
+ return -1;
+ }
+
+ buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
+ if (!buf)
+ return -1;
+
+ hapd->beacon_req_token++;
+ if (!hapd->beacon_req_token)
+ hapd->beacon_req_token++;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, hapd->beacon_req_token);
+ wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+ /* Measurement Request element */
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+ wpabuf_put_u8(buf, 3 + wpabuf_len(req));
+ wpabuf_put_u8(buf, 1); /* Measurement Token */
+ wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
+ wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
+ wpabuf_put_buf(buf, req);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+ if (ret < 0)
+ return ret;
+
+ return hapd->beacon_req_token;
+}
+
+
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ if (len < 24 + 3)
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
+ " %u ack=%d", MAC2STR(mgmt->da),
+ mgmt->u.action.u.rrm.dialog_token, ok);
+}
diff --git a/src/ap/rrm.h b/src/ap/rrm.h
index f07fd41ac019..02cd522ee9d6 100644
--- a/src/ap/rrm.h
+++ b/src/ap/rrm.h
@@ -24,5 +24,10 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
u16 random_interval, u8 min_ap,
const u8 *responders, unsigned int n_responders);
void hostapd_clean_rrm(struct hostapd_data *hapd);
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+ u8 req_mode, const struct wpabuf *req);
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok);
#endif /* RRM_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index f12d4088b131..179cf43b60b1 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1,6 +1,6 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2013, Jouni Malinen <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.
@@ -17,6 +17,7 @@
#include "radius/radius_client.h"
#include "p2p/p2p.h"
#include "fst/fst.h"
+#include "crypto/crypto.h"
#include "hostapd.h"
#include "accounting.h"
#include "ieee802_1x.h"
@@ -36,6 +37,7 @@
#include "ndisc_snoop.h"
#include "sta_info.h"
#include "vlan.h"
+#include "wps_hostapd.h"
static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
struct sta_info *sta);
@@ -47,6 +49,7 @@ static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
#endif /* CONFIG_IEEE80211W */
static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx);
int ap_for_each_sta(struct hostapd_data *hapd,
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
@@ -194,7 +197,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
if (sta->no_short_slot_time_set) {
sta->no_short_slot_time_set = 0;
hapd->iface->num_sta_no_short_slot_time--;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_slot_time == 0)
set_beacon++;
}
@@ -202,7 +206,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
if (sta->no_short_preamble_set) {
sta->no_short_preamble_set = 0;
hapd->iface->num_sta_no_short_preamble--;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_preamble == 0)
set_beacon++;
}
@@ -316,6 +321,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
wpabuf_free(sta->wps_ie);
wpabuf_free(sta->p2p_ie);
wpabuf_free(sta->hs20_ie);
+ wpabuf_free(sta->roaming_consortium);
#ifdef CONFIG_FST
wpabuf_free(sta->mb_ies);
#endif /* CONFIG_FST */
@@ -326,6 +332,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->identity);
os_free(sta->radius_cui);
os_free(sta->remediation_url);
+ os_free(sta->t_c_url);
wpabuf_free(sta->hs20_deauth_req);
os_free(sta->hs20_session_info_url);
@@ -337,6 +344,31 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
mbo_ap_sta_free(sta);
os_free(sta->supp_op_classes);
+#ifdef CONFIG_FILS
+ os_free(sta->fils_pending_assoc_req);
+ wpabuf_free(sta->fils_hlp_resp);
+ wpabuf_free(sta->hlp_dhcp_discover);
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#ifdef CONFIG_FILS_SK_PFS
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ wpabuf_clear_free(sta->fils_dh_ss);
+ wpabuf_free(sta->fils_g_sta);
+#endif /* CONFIG_FILS_SK_PFS */
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ bin_clear_free(sta->owe_pmk, sta->owe_pmk_len);
+ crypto_ecdh_deinit(sta->owe_ecdh);
+#endif /* CONFIG_OWE */
+
+ os_free(sta->ext_capability);
+
+#ifdef CONFIG_WNM_AP
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+#endif /* CONFIG_WNM_AP */
+
+ os_free(sta->ifname_wds);
+
os_free(sta);
}
@@ -597,7 +629,7 @@ void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
{
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = timeout_ctx;
@@ -608,7 +640,7 @@ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
sta->hs20_disassoc_timer);
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
}
@@ -745,9 +777,17 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
- sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Skip deauthentication in DMG/IEEE 802.11ad */
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_REMOVE;
+ } else {
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_DEAUTH;
+ }
ap_sta_set_authorized(hapd, sta, 0);
- sta->timeout_next = STA_DEAUTH;
wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
"for " MACSTR " (%d seconds - "
"AP_MAX_INACTIVITY_AFTER_DISASSOC)",
@@ -783,6 +823,14 @@ static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason)
{
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ ap_sta_disassociate(hapd, sta, reason);
+ return;
+ }
+
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
@@ -1229,6 +1277,20 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
ap_handle_timer, hapd, sta);
sta->timeout_next = STA_REMOVE;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ sta->disassoc_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
+ 2 : 0, 0, ap_sta_disassoc_cb_timeout,
+ hapd, sta);
+ return;
+ }
+
sta->deauth_reason = reason;
sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
@@ -1275,6 +1337,15 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
"%s: Removed ap_sta_disassoc_cb_timeout timeout for "
MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
+ if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0)
+ {
+ wpa_printf(MSG_DEBUG,
+ "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for "
+ MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ if (sta->flags & WLAN_STA_WPS)
+ hostapd_wps_eap_completed(hapd);
+ }
}
@@ -1283,7 +1354,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
int res;
buf[0] = '\0';
- res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1300,6 +1371,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
(flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
(flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
(flags & WLAN_STA_GAS ? "[GAS]" : ""),
+ (flags & WLAN_STA_HT ? "[HT]" : ""),
(flags & WLAN_STA_VHT ? "[VHT]" : ""),
(flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
(flags & WLAN_STA_WNM_SLEEP_MODE ?
@@ -1309,3 +1381,48 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
return res;
}
+
+
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ u16 reason;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X: Scheduled disconnection of " MACSTR
+ " after EAP-Failure", MAC2STR(sta->addr));
+
+ reason = sta->disconnect_reason_code;
+ if (!reason)
+ reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
+ ap_sta_disconnect(hapd, sta, sta->addr, reason);
+ if (sta->flags & WLAN_STA_WPS)
+ hostapd_wps_eap_completed(hapd);
+}
+
+
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X: Force disconnection of " MACSTR
+ " after EAP-Failure in 10 ms", MAC2STR(sta->addr));
+
+ /*
+ * Add a small sleep to increase likelihood of previously requested
+ * EAP-Failure TX getting out before this should the driver reorder
+ * operations.
+ */
+ eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta);
+ eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb,
+ hapd, sta);
+}
+
+
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb,
+ hapd, sta);
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 099de62d1a9a..9cac6f157f68 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -1,6 +1,6 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2011, Jouni Malinen <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.
@@ -9,14 +9,11 @@
#ifndef STA_INFO_H
#define STA_INFO_H
-#ifdef CONFIG_MESH
-/* needed for mesh_plink_state enum */
#include "common/defs.h"
-#include "common/wpa_common.h"
-#endif /* CONFIG_MESH */
-
#include "list.h"
#include "vlan.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
@@ -38,6 +35,7 @@
#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
#define WLAN_STA_VENDOR_VHT BIT(21)
+#define WLAN_STA_PENDING_FILS_ERP BIT(22)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -46,6 +44,7 @@
* Supported Rates IEs). */
#define WLAN_SUPP_RATES_MAX 32
+struct hostapd_data;
struct mbo_non_pref_chan_info {
struct mbo_non_pref_chan_info *next;
@@ -68,6 +67,7 @@ struct sta_info {
be32 ipaddr;
struct dl_list ip6addr; /* list head for struct ip6addr */
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+ u16 disconnect_reason_code; /* RADIUS server override */
u32 flags; /* Bitfield of WLAN_STA_* */
u16 capability;
u16 listen_interval; /* or beacon_int for APs */
@@ -113,6 +113,10 @@ struct sta_info {
unsigned int radius_das_match:1;
unsigned int ecsa_supported:1;
unsigned int added_unassoc:1;
+ unsigned int pending_wds_enable:1;
+ unsigned int power_capab:1;
+ unsigned int agreed_to_steer:1;
+ unsigned int hs20_t_c_filtering:1;
u16 auth_alg;
@@ -170,17 +174,20 @@ struct sta_info {
struct os_reltime sa_query_start;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_INTERWORKING
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
struct gas_dialog_info *gas_dialog;
u8 gas_dialog_next;
-#endif /* CONFIG_INTERWORKING */
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+ /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
+ struct wpabuf *roaming_consortium;
u8 remediation_method;
char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+ char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
struct wpabuf *hs20_deauth_req;
char *hs20_session_info_url;
int hs20_disassoc_timer;
@@ -195,7 +202,8 @@ struct sta_info {
unsigned int mesh_sae_pmksa_caching:1;
#endif /* CONFIG_SAE */
- u32 session_timeout; /* valid only if session_timeout_set == 1 */
+ /* valid only if session_timeout_set == 1 */
+ struct os_reltime session_timeout;
/* Last Authentication/(Re)Association Request/Action frame sequence
* control */
@@ -214,10 +222,51 @@ struct sta_info {
u8 rrm_enabled_capa[5];
+ s8 min_tx_power;
+ s8 max_tx_power;
+
#ifdef CONFIG_TAXONOMY
struct wpabuf *probe_ie_taxonomy;
struct wpabuf *assoc_ie_taxonomy;
#endif /* CONFIG_TAXONOMY */
+
+#ifdef CONFIG_FILS
+ u8 fils_snonce[FILS_NONCE_LEN];
+ u8 fils_session[FILS_SESSION_LEN];
+ u8 fils_erp_pmkid[PMKID_LEN];
+ u8 *fils_pending_assoc_req;
+ size_t fils_pending_assoc_req_len;
+ unsigned int fils_pending_assoc_is_reassoc:1;
+ unsigned int fils_dhcp_rapid_commit_proxy:1;
+ unsigned int fils_erp_pmkid_set:1;
+ unsigned int fils_drv_assoc_finish:1;
+ struct wpabuf *fils_hlp_resp;
+ struct wpabuf *hlp_dhcp_discover;
+ void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 resp, struct wpabuf *data, int pub);
+#ifdef CONFIG_FILS_SK_PFS
+ struct crypto_ecdh *fils_ecdh;
+#endif /* CONFIG_FILS_SK_PFS */
+ struct wpabuf *fils_dh_ss;
+ struct wpabuf *fils_g_sta;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ u8 *owe_pmk;
+ size_t owe_pmk_len;
+ struct crypto_ecdh *owe_ecdh;
+ u16 owe_group;
+#endif /* CONFIG_OWE */
+
+ u8 *ext_capability;
+ char *ifname_wds; /* WDS ifname, if in use */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ enum wpa_alg last_tk_alg;
+ int last_tk_key_idx;
+ u8 last_tk[WPA_TK_MAX_LEN];
+ size_t last_tk_len;
+#endif /* CONFIG_TESTING_OPTIONS */
};
@@ -237,8 +286,6 @@ struct sta_info {
#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
-struct hostapd_data;
-
int ap_for_each_sta(struct hostapd_data *hapd,
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
void *ctx),
@@ -289,5 +336,9 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
struct sta_info *sta);
int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta);
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta);
#endif /* STA_INFO_H */
diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c
index cea8b726f47a..ae157a7c91d6 100644
--- a/src/ap/taxonomy.c
+++ b/src/ap/taxonomy.c
@@ -21,6 +21,7 @@
#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "sta_info.h"
+#include "taxonomy.h"
/* Copy a string with no funny schtuff allowed; only alphanumerics. */
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 4725e2b3e8f1..557570cd1bc4 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -71,6 +71,11 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
struct os_reltime now;
int ret = 0;
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Michael MIC failure detected in received frame%s",
+ local ? " (local)" : "");
+
if (addr && local) {
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta != NULL) {
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 31e4fc6b396a..01fecee026a1 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -138,6 +138,8 @@ int vlan_init(struct hostapd_data *hapd)
!hapd->conf->vlan) {
/* dynamic vlans enabled but no (or empty) vlan_file given */
struct hostapd_vlan *vlan;
+ int ret;
+
vlan = os_zalloc(sizeof(*vlan));
if (vlan == NULL) {
wpa_printf(MSG_ERROR, "Out of memory while assigning "
@@ -146,8 +148,16 @@ int vlan_init(struct hostapd_data *hapd)
}
vlan->vlan_id = VLAN_ID_WILDCARD;
- os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
- hapd->conf->iface);
+ ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
+ hapd->conf->iface);
+ if (ret >= (int) sizeof(vlan->ifname)) {
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan->ifname);
+ } else if (ret < 0) {
+ os_free(vlan);
+ return ret;
+ }
vlan->next = hapd->conf->vlan;
hapd->conf->vlan = vlan;
}
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 314e244bc956..8054c5d2f243 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -21,11 +21,6 @@
#include "wmm.h"
-/* TODO: maintain separate sequence and fragment numbers for each AC
- * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
- * if only WMM stations are receiving a certain group */
-
-
static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
{
u8 ret;
@@ -157,8 +152,9 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
int wmm_process_tspec(struct wmm_tspec_element *tspec)
{
- int medium_time, pps, duration;
- int up, psb, dir, tid;
+ u64 medium_time;
+ unsigned int pps, duration;
+ unsigned int up, psb, dir, tid;
u16 val, surplus;
up = (tspec->ts_info[1] >> 3) & 0x07;
@@ -206,8 +202,9 @@ int wmm_process_tspec(struct wmm_tspec_element *tspec)
return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
}
- medium_time = surplus * pps * duration / 0x2000;
- wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
+ medium_time = (u64) surplus * pps * duration / 0x2000;
+ wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu",
+ (unsigned long) medium_time);
/*
* TODO: store list of granted (and still active) TSPECs and check
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 41d50cebfbe0..1e8f58b4d07e 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -95,8 +95,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
"WNM-Sleep Response action frame");
- os_free(wnmtfs_ie);
- return -1;
+ res = -1;
+ goto fail;
}
os_memcpy(mgmt->da, addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
@@ -109,6 +109,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
/* add key data if MFP is enabled */
if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ hapd->conf->wnm_sleep_mode_no_keys ||
action_type != WNM_SLEEP_MODE_EXIT) {
mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
} else {
@@ -118,11 +119,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
(int) gtk_elem_len);
#ifdef CONFIG_IEEE80211W
res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
- if (res < 0) {
- os_free(wnmtfs_ie);
- os_free(mgmt);
- return -1;
- }
+ if (res < 0)
+ goto fail;
igtk_elem_len = res;
pos += igtk_elem_len;
wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
@@ -176,7 +174,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
wpa_set_wnmsleep(sta->wpa_sm, 0);
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
addr, NULL, NULL);
- if (!wpa_auth_uses_mfp(sta->wpa_sm))
+ if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ hapd->conf->wnm_sleep_mode_no_keys)
wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
}
} else
@@ -184,6 +183,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
#undef MAX_GTK_SUBELEM_LEN
#undef MAX_IGTK_SUBELEM_LEN
+fail:
os_free(wnmtfs_ie);
os_free(mgmt);
return res;
@@ -202,12 +202,20 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
u8 *tfsreq_ie_end = NULL;
u16 tfsreq_ie_len = 0;
+ if (!hapd->conf->wnm_sleep_mode) {
+ wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
+ MACSTR " since WNM-Sleep Mode is disabled",
+ MAC2STR(addr));
+ return;
+ }
+
dialog_token = *pos++;
while (pos + 1 < frm + len) {
u8 ie_len = pos[1];
if (pos + 2 + ie_len > frm + len)
break;
- if (*pos == WLAN_EID_WNMSLEEP)
+ if (*pos == WLAN_EID_WNMSLEEP &&
+ ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
wnmsleep_ie = (struct wnm_sleep_element *) pos;
else if (*pos == WLAN_EID_TFS_REQ) {
if (!tfsreq_ie_start)
@@ -251,20 +259,14 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
const u8 *addr,
- u8 dialog_token,
- const char *url)
+ u8 dialog_token)
{
struct ieee80211_mgmt *mgmt;
- size_t url_len, len;
+ size_t len;
u8 *pos;
int res;
- if (url)
- url_len = os_strlen(url);
- else
- url_len = 0;
-
- mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0));
+ mgmt = os_zalloc(sizeof(*mgmt));
if (mgmt == NULL)
return -1;
os_memcpy(mgmt->da, addr, ETH_ALEN);
@@ -279,11 +281,6 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
mgmt->u.action.u.bss_tm_req.validity_interval = 1;
pos = mgmt->u.action.u.bss_tm_req.variable;
- if (url) {
- *pos++ += url_len;
- os_memcpy(pos, url, url_len);
- pos += url_len;
- }
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
@@ -307,6 +304,20 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
{
u8 dialog_token, reason;
const u8 *pos, *end;
+ int enabled = hapd->conf->bss_transition;
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ enabled = 1;
+#endif /* CONFIG_MBO */
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore BSS Transition Management Query from "
+ MACSTR
+ " since BSS Transition Management is disabled",
+ MAC2STR(addr));
+ return;
+ }
if (len < 2) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
@@ -326,7 +337,20 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
pos, end - pos);
- ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL);
+ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+}
+
+
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ if (sta->agreed_to_steer) {
+ wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->agreed_to_steer = 0;
+ }
}
@@ -336,6 +360,21 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
{
u8 dialog_token, status_code, bss_termination_delay;
const u8 *pos, *end;
+ int enabled = hapd->conf->bss_transition;
+ struct sta_info *sta;
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ enabled = 1;
+#endif /* CONFIG_MBO */
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore BSS Transition Management Response from "
+ MACSTR
+ " since BSS Transition Management is disabled",
+ MAC2STR(addr));
+ return;
+ }
if (len < 3) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
@@ -354,11 +393,23 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
"bss_termination_delay=%u", MAC2STR(addr), dialog_token,
status_code, bss_termination_delay);
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for received BSS TM Response",
+ MAC2STR(addr));
+ return;
+ }
+
if (status_code == WNM_BSS_TM_ACCEPT) {
if (end - pos < ETH_ALEN) {
wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
return;
}
+ sta->agreed_to_steer = 1;
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+ eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+ hapd, sta);
wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
MAC2STR(pos));
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
@@ -368,6 +419,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
MAC2STR(pos));
pos += ETH_ALEN;
} else {
+ sta->agreed_to_steer = 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
" status_code=%u bss_termination_delay=%u",
MAC2STR(addr), status_code, bss_termination_delay);
@@ -401,6 +453,48 @@ static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
}
+static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *buf,
+ size_t len)
+{
+ u8 dialog_token;
+ char *hex;
+ size_t hex_len;
+
+ if (!hapd->conf->coloc_intf_reporting) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore unexpected Collocated Interference Report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore too short Collocated Interference Report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+ dialog_token = *buf++;
+ len--;
+
+ wpa_printf(MSG_DEBUG,
+ "WNM: Received Collocated Interference Report frame from "
+ MACSTR " (dialog_token=%u)",
+ MAC2STR(addr), dialog_token);
+ wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
+ buf, len);
+
+ hex_len = 2 * len + 1;
+ hex = os_malloc(hex_len);
+ if (!hex)
+ return;
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
+ MAC2STR(addr), dialog_token, hex);
+ os_free(hex);
+}
+
+
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -431,6 +525,10 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
plen);
return 0;
+ case WNM_COLLOCATED_INTERFERENCE_REPORT:
+ ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
}
wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
@@ -629,3 +727,40 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
return 0;
}
+
+
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+ unsigned int auto_report, unsigned int timeout)
+{
+ u8 buf[100], *pos;
+ struct ieee80211_mgmt *mgmt;
+ u8 dialog_token = 1;
+
+ if (auto_report > 3 || timeout > 63)
+ return -1;
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.coloc_intf_req.action =
+ WNM_COLLOCATED_INTERFERENCE_REQ;
+ mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
+ mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
+ pos = &mgmt->u.action.u.coloc_intf_req.req_info;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
+ MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
+ MAC2STR(sta->addr), dialog_token, auto_report, timeout);
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Failed to send Collocated Interference Request frame");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index a44eadb85e55..1806ba0e0525 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -23,5 +23,8 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *bss_term_dur, const char *url,
const u8 *nei_rep, size_t nei_rep_len,
const u8 *mbo_attrs, size_t mbo_len);
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+ unsigned int auto_report, unsigned int timeout);
#endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index bf10cc1646f7..34969e79e46f 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2015, Jouni Malinen <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.
@@ -13,10 +13,13 @@
#include "utils/state_machine.h"
#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
+#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
+#include "crypto/aes_siv.h"
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/sha384.h"
#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "ap_config.h"
@@ -33,8 +36,14 @@
static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
static int wpa_sm_step(struct wpa_state_machine *sm);
-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
- size_t data_len);
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+ u8 *data, size_t data_len);
+#ifdef CONFIG_FILS
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+ u8 *buf, size_t buf_len, u16 *_key_data_len);
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+ const struct wpabuf *hlp);
+#endif /* CONFIG_FILS */
static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
@@ -52,12 +61,12 @@ static void wpa_group_get(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_group_put(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
-static const u32 dot11RSNAConfigGroupUpdateCount = 4;
-static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
static const u32 eapol_key_timeout_first = 100; /* ms */
static const u32 eapol_key_timeout_subseq = 1000; /* ms */
static const u32 eapol_key_timeout_first_group = 500; /* ms */
+static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
/* TODO: make these configurable */
static const int dot11RSNAConfigPMKLifetime = 43200;
@@ -68,8 +77,8 @@ static const int dot11RSNAConfigSATimeout = 60;
static inline int wpa_auth_mic_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
- if (wpa_auth->cb.mic_failure_report)
- return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+ if (wpa_auth->cb->mic_failure_report)
+ return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr);
return 0;
}
@@ -77,8 +86,8 @@ static inline int wpa_auth_mic_failure_report(
static inline void wpa_auth_psk_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
- if (wpa_auth->cb.psk_failure_report)
- wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
+ if (wpa_auth->cb->psk_failure_report)
+ wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr);
}
@@ -86,38 +95,38 @@ static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
const u8 *addr, wpa_eapol_variable var,
int value)
{
- if (wpa_auth->cb.set_eapol)
- wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value);
+ if (wpa_auth->cb->set_eapol)
+ wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value);
}
static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
const u8 *addr, wpa_eapol_variable var)
{
- if (wpa_auth->cb.get_eapol == NULL)
+ if (wpa_auth->cb->get_eapol == NULL)
return -1;
- return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var);
+ return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var);
}
static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
const u8 *addr,
const u8 *p2p_dev_addr,
- const u8 *prev_psk)
+ const u8 *prev_psk, size_t *psk_len)
{
- if (wpa_auth->cb.get_psk == NULL)
+ if (wpa_auth->cb->get_psk == NULL)
return NULL;
- return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
- prev_psk);
+ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+ prev_psk, psk_len);
}
static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
const u8 *addr, u8 *msk, size_t *len)
{
- if (wpa_auth->cb.get_msk == NULL)
+ if (wpa_auth->cb->get_msk == NULL)
return -1;
- return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len);
+ return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len);
}
@@ -126,19 +135,19 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
enum wpa_alg alg, const u8 *addr, int idx,
u8 *key, size_t key_len)
{
- if (wpa_auth->cb.set_key == NULL)
+ if (wpa_auth->cb->set_key == NULL)
return -1;
- return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
- key, key_len);
+ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+ key, key_len);
}
static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
const u8 *addr, int idx, u8 *seq)
{
- if (wpa_auth->cb.get_seqnum == NULL)
+ if (wpa_auth->cb->get_seqnum == NULL)
return -1;
- return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
}
@@ -146,10 +155,10 @@ static inline int
wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *data, size_t data_len, int encrypt)
{
- if (wpa_auth->cb.send_eapol == NULL)
+ if (wpa_auth->cb->send_eapol == NULL)
return -1;
- return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len,
- encrypt);
+ return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len,
+ encrypt);
}
@@ -157,9 +166,9 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
const u8 *addr)
{
- if (wpa_auth->cb.start_ampe == NULL)
+ if (wpa_auth->cb->start_ampe == NULL)
return -1;
- return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+ return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr);
}
#endif /* CONFIG_MESH */
@@ -168,9 +177,9 @@ int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_state_machine *sm, void *ctx),
void *cb_ctx)
{
- if (wpa_auth->cb.for_each_sta == NULL)
+ if (wpa_auth->cb->for_each_sta == NULL)
return 0;
- return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx);
+ return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx);
}
@@ -178,18 +187,18 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_authenticator *a, void *ctx),
void *cb_ctx)
{
- if (wpa_auth->cb.for_each_auth == NULL)
+ if (wpa_auth->cb->for_each_auth == NULL)
return 0;
- return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
+ return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx);
}
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
logger_level level, const char *txt)
{
- if (wpa_auth->cb.logger == NULL)
+ if (wpa_auth->cb->logger == NULL)
return;
- wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt);
+ wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt);
}
@@ -200,7 +209,7 @@ void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
int maxlen;
va_list ap;
- if (wpa_auth->cb.logger == NULL)
+ if (wpa_auth->cb->logger == NULL)
return;
maxlen = os_strlen(fmt) + 100;
@@ -219,30 +228,13 @@ void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
- const u8 *addr)
+ const u8 *addr, u16 reason)
{
- if (wpa_auth->cb.disconnect == NULL)
+ if (wpa_auth->cb->disconnect == NULL)
return;
- wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
- wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-}
-
-
-static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
-{
- int ret = 0;
-#ifdef CONFIG_IEEE80211R
- if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
- ret = 1;
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
- if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
- ret = 1;
-#endif /* CONFIG_IEEE80211W */
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
- ret = 1;
- return ret;
+ wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)",
+ MAC2STR(addr), reason);
+ wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason);
}
@@ -409,7 +401,8 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
*/
struct wpa_authenticator * wpa_init(const u8 *addr,
struct wpa_auth_config *conf,
- struct wpa_auth_callbacks *cb)
+ const struct wpa_auth_callbacks *cb,
+ void *cb_ctx)
{
struct wpa_authenticator *wpa_auth;
@@ -418,7 +411,8 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
return NULL;
os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
- os_memcpy(&wpa_auth->cb, cb, sizeof(*cb));
+ wpa_auth->cb = cb;
+ wpa_auth->cb_ctx = cb_ctx;
if (wpa_auth_gen_wpa_ie(wpa_auth)) {
wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
@@ -443,7 +437,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
return NULL;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
if (wpa_auth->ft_pmk_cache == NULL) {
wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
@@ -453,7 +447,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
os_free(wpa_auth);
return NULL;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (wpa_auth->conf.wpa_gmk_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
@@ -506,17 +500,13 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
-#ifdef CONFIG_PEERKEY
- while (wpa_auth->stsl_negotiations)
- wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations);
-#endif /* CONFIG_PEERKEY */
-
pmksa_cache_auth_deinit(wpa_auth->pmksa);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
wpa_auth->ft_pmk_cache = NULL;
-#endif /* CONFIG_IEEE80211R */
+ wpa_ft_deinit(wpa_auth);
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
bitfield_free(wpa_auth->ip_pool);
@@ -599,16 +589,28 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
return -1;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sm->ft_completed) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"FT authentication already completed - do not "
"start 4-way handshake");
/* Go to PTKINITDONE state to allow GTK rekeying */
sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+ sm->Pair = TRUE;
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (sm->fils_completed) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "FILS authentication already completed - do not start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+ sm->Pair = TRUE;
return 0;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
if (sm->started) {
os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
@@ -660,10 +662,10 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = FALSE;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
os_free(sm->assoc_resp_ftie);
wpabuf_free(sm->ft_pending_req_ies);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
wpa_group_put(sm->wpa_auth, sm->group);
@@ -680,15 +682,19 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"strict rekeying - force GTK rekey since STA "
"is leaving");
- eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
- eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
- NULL);
+ if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
+ sm->wpa_auth, NULL) == -1)
+ eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
+ NULL);
}
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_ft_sta_deinit(sm);
+#endif /* CONFIG_IEEE80211R_AP */
if (sm->in_step_loop) {
/* Must not free state machine while wpa_sm_step() is running.
* Freeing will be completed in the end of wpa_sm_step(). */
@@ -739,7 +745,7 @@ static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
struct wpa_eapol_ie_parse *kde)
@@ -786,7 +792,7 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
return 0;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
@@ -828,29 +834,38 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
struct wpa_ptk PTK;
int ok = 0;
const u8 *pmk = NULL;
- unsigned int pmk_len;
+ size_t pmk_len;
+ os_memset(&PTK, 0, sizeof(PTK));
for (;;) {
- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+ !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
- sm->p2p_dev_addr, pmk);
+ sm->p2p_dev_addr, pmk, &pmk_len);
if (pmk == NULL)
break;
- pmk_len = PMK_LEN;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+ sm->xxkey_len = pmk_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
} else {
pmk = sm->PMK;
pmk_len = sm->pmk_len;
}
- wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK);
+ if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0)
+ break;
- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
- == 0) {
+ if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
+ data, data_len) == 0) {
ok = 1;
break;
}
- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
@@ -877,39 +892,41 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- struct wpa_eapol_key_192 *key192;
u16 key_info, key_data_length;
- enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
- SMK_M1, SMK_M3, SMK_ERROR } msg;
+ enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
char *msgtxt;
struct wpa_eapol_ie_parse kde;
- int ft;
- const u8 *eapol_key_ie, *key_data;
- size_t eapol_key_ie_len, keyhdrlen, mic_len;
+ const u8 *key_data;
+ size_t keyhdrlen, mic_len;
+ u8 *mic;
if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
return;
+ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
- mic_len = wpa_mic_len(sm->wpa_key_mgmt);
- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ keyhdrlen = sizeof(*key) + mic_len + 2;
- if (data_len < sizeof(*hdr) + keyhdrlen)
+ if (data_len < sizeof(*hdr) + keyhdrlen) {
+ wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
return;
+ }
hdr = (struct ieee802_1x_hdr *) data;
key = (struct wpa_eapol_key *) (hdr + 1);
- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+ mic = (u8 *) (key + 1);
key_info = WPA_GET_BE16(key->key_info);
- if (mic_len == 24) {
- key_data = (const u8 *) (key192 + 1);
- key_data_length = WPA_GET_BE16(key192->key_data_length);
- } else {
- key_data = (const u8 *) (key + 1);
- key_data_length = WPA_GET_BE16(key->key_data_length);
- }
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
- " key_info=0x%x type=%u key_data_length=%u",
- MAC2STR(sm->addr), key_info, key->type, key_data_length);
+ " key_info=0x%x type=%u mic_len=%u key_data_length=%u",
+ MAC2STR(sm->addr), key_info, key->type,
+ (unsigned int) mic_len, key_data_length);
+ wpa_hexdump(MSG_MSGDUMP,
+ "WPA: EAPOL-Key header (ending before Key MIC)",
+ key, sizeof(*key));
+ wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
+ mic, mic_len);
if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
"key_data overflow (%d > %lu)",
@@ -950,25 +967,20 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
* are set */
- if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) ==
- (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) {
- if (key_info & WPA_KEY_INFO_ERROR) {
- msg = SMK_ERROR;
- msgtxt = "SMK Error";
- } else {
- msg = SMK_M1;
- msgtxt = "SMK M1";
- }
- } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
- msg = SMK_M3;
- msgtxt = "SMK M3";
- } else if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+ wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message");
+ return;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
msg = REQUEST;
msgtxt = "Request";
} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
msg = GROUP_2;
msgtxt = "2/2 Group";
- } else if (key_data_length == 0) {
+ } else if (key_data_length == 0 ||
+ (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+ key_data_length == AES_BLOCK_SIZE)) {
msg = PAIRWISE_4;
msgtxt = "4/4 Pairwise";
} else {
@@ -976,15 +988,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
msgtxt = "2/4 Pairwise";
}
- /* TODO: key_info type validation for PeerKey */
if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
msg == GROUP_2) {
u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
if (sm->pairwise == WPA_CIPHER_CCMP ||
sm->pairwise == WPA_CIPHER_GCMP) {
- if (wpa_use_aes_cmac(sm) &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
- !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+ if (wpa_use_cmac(sm->wpa_key_mgmt) &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
@@ -994,7 +1004,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
return;
}
- if (!wpa_use_aes_cmac(sm) &&
+ if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
@@ -1004,7 +1015,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
}
- if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+ if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
@@ -1092,6 +1103,15 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
continue_processing:
+#ifdef CONFIG_FILS
+ if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
+ !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
+ return;
+ }
+#endif /* CONFIG_FILS */
+
switch (msg) {
case PAIRWISE_2:
if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
@@ -1119,70 +1139,10 @@ continue_processing:
"collect more entropy for random number "
"generation");
random_mark_pool_ready();
- wpa_sta_disconnect(wpa_auth, sm->addr);
- return;
- }
- if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
- "received EAPOL-Key msg 2/4 with "
- "invalid Key Data contents");
- return;
- }
- if (kde.rsn_ie) {
- eapol_key_ie = kde.rsn_ie;
- eapol_key_ie_len = kde.rsn_ie_len;
- } else if (kde.osen) {
- eapol_key_ie = kde.osen;
- eapol_key_ie_len = kde.osen_len;
- } else {
- eapol_key_ie = kde.wpa_ie;
- eapol_key_ie_len = kde.wpa_ie_len;
- }
- ft = sm->wpa == WPA_VERSION_WPA2 &&
- wpa_key_mgmt_ft(sm->wpa_key_mgmt);
- if (sm->wpa_ie == NULL ||
- wpa_compare_rsn_ie(ft,
- sm->wpa_ie, sm->wpa_ie_len,
- eapol_key_ie, eapol_key_ie_len)) {
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- "WPA IE from (Re)AssocReq did not "
- "match with msg 2/4");
- if (sm->wpa_ie) {
- wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
- sm->wpa_ie, sm->wpa_ie_len);
- }
- wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
- eapol_key_ie, eapol_key_ie_len);
- /* MLME-DEAUTHENTICATE.request */
- wpa_sta_disconnect(wpa_auth, sm->addr);
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
-#ifdef CONFIG_IEEE80211R
- if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
- wpa_sta_disconnect(wpa_auth, sm->addr);
- return;
- }
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_P2P
- if (kde.ip_addr_req && kde.ip_addr_req[0] &&
- wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
- int idx;
- wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
- "EAPOL-Key exchange");
- idx = bitfield_get_first_zero(wpa_auth->ip_pool);
- if (idx >= 0) {
- u32 start = WPA_GET_BE32(wpa_auth->conf.
- ip_addr_start);
- bitfield_set(wpa_auth->ip_pool, idx);
- WPA_PUT_BE32(sm->ip_addr, start + idx);
- wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
- "address %u.%u.%u.%u to " MACSTR,
- sm->ip_addr[0], sm->ip_addr[1],
- sm->ip_addr[2], sm->ip_addr[3],
- MAC2STR(sm->addr));
- }
- }
-#endif /* CONFIG_P2P */
break;
case PAIRWISE_4:
if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1204,28 +1164,6 @@ continue_processing:
return;
}
break;
-#ifdef CONFIG_PEERKEY
- case SMK_M1:
- case SMK_M3:
- case SMK_ERROR:
- if (!wpa_auth->conf.peerkey) {
- wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but "
- "PeerKey use disabled - ignoring message");
- return;
- }
- if (!sm->PTK_valid) {
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- "received EAPOL-Key msg SMK in "
- "invalid state - dropped");
- return;
- }
- break;
-#else /* CONFIG_PEERKEY */
- case SMK_M1:
- case SMK_M3:
- case SMK_ERROR:
- return; /* STSL disabled - ignore SMK messages */
-#endif /* CONFIG_PEERKEY */
case REQUEST:
break;
}
@@ -1239,22 +1177,42 @@ continue_processing:
return;
}
- if (!(key_info & WPA_KEY_INFO_MIC)) {
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ !(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC not set");
return;
}
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ (key_info & WPA_KEY_INFO_MIC)) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received invalid EAPOL-Key: Key MIC set");
+ return;
+ }
+#endif /* CONFIG_FILS */
+
sm->MICVerified = FALSE;
if (sm->PTK_valid && !sm->update_snonce) {
- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
- data_len) &&
+ if (mic_len &&
+ wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK,
+ data, data_len) &&
(msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
wpa_try_alt_snonce(sm, data, data_len))) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key with invalid MIC");
return;
}
+#ifdef CONFIG_FILS
+ if (!mic_len &&
+ wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
+ &key_data_length) < 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key with invalid MIC");
+ return;
+ }
+#endif /* CONFIG_FILS */
sm->MICVerified = TRUE;
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
sm->pending_1_of_4_timeout = 0;
@@ -1277,12 +1235,7 @@ continue_processing:
* even though MAC address KDE is not normally encrypted,
* supplicant is allowed to encrypt it.
*/
- if (msg == SMK_ERROR) {
-#ifdef CONFIG_PEERKEY
- wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
-#endif /* CONFIG_PEERKEY */
- return;
- } else if (key_info & WPA_KEY_INFO_ERROR) {
+ if (key_info & WPA_KEY_INFO_ERROR) {
if (wpa_receive_error_report(
wpa_auth, sm,
!(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
@@ -1292,11 +1245,6 @@ continue_processing:
"received EAPOL-Key Request for new "
"4-Way Handshake");
wpa_request_new_ptk(sm);
-#ifdef CONFIG_PEERKEY
- } else if (msg == SMK_M1) {
- wpa_smk_m1(wpa_auth, sm, key, key_data,
- key_data_length);
-#endif /* CONFIG_PEERKEY */
} else if (key_data_length > 0 &&
wpa_parse_kde_ies(key_data, key_data_length,
&kde) == 0 &&
@@ -1335,18 +1283,10 @@ continue_processing:
wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
}
-#ifdef CONFIG_PEERKEY
- if (msg == SMK_M3) {
- wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
- return;
- }
-#endif /* CONFIG_PEERKEY */
-
os_free(sm->last_rx_eapol_key);
- sm->last_rx_eapol_key = os_malloc(data_len);
+ sm->last_rx_eapol_key = os_memdup(data, data_len);
if (sm->last_rx_eapol_key == NULL)
return;
- os_memcpy(sm->last_rx_eapol_key, data, data_len);
sm->last_rx_eapol_key_len = data_len;
sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
@@ -1361,7 +1301,7 @@ continue_processing:
static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
const u8 *gnonce, u8 *gtk, size_t gtk_len)
{
- u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+ u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN];
u8 *pos;
int ret = 0;
@@ -1372,21 +1312,30 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
* is done only at the Authenticator and as such, does not need to be
* exactly same.
*/
+ os_memset(data, 0, sizeof(data));
os_memcpy(data, addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
pos = data + ETH_ALEN + WPA_NONCE_LEN;
wpa_get_ntp_timestamp(pos);
pos += 8;
- if (random_get_bytes(pos, 16) < 0)
+ if (random_get_bytes(pos, gtk_len) < 0)
ret = -1;
-#ifdef CONFIG_IEEE80211W
- sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
-#else /* CONFIG_IEEE80211W */
- if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
- < 0)
+#ifdef CONFIG_SHA384
+ if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
ret = -1;
-#endif /* CONFIG_IEEE80211W */
+#else /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA256
+ if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#else /* CONFIG_SHA256 */
+ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#endif /* CONFIG_SHA256 */
+#endif /* CONFIG_SHA384 */
return ret;
}
@@ -1412,26 +1361,24 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- struct wpa_eapol_key_192 *key192;
size_t len, mic_len, keyhdrlen;
int alg;
int key_data_len, pad_len = 0;
u8 *buf, *pos;
int version, pairwise;
int i;
- u8 *key_data;
+ u8 *key_mic, *key_data;
- mic_len = wpa_mic_len(sm->wpa_key_mgmt);
- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ keyhdrlen = sizeof(*key) + mic_len + 2;
len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
if (force_version)
version = force_version;
- else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+ else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
- else if (wpa_use_aes_cmac(sm))
+ else if (wpa_use_cmac(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise != WPA_CIPHER_TKIP)
version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
@@ -1453,8 +1400,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
key_data_len = kde_len;
if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
pad_len = key_data_len % 8;
if (pad_len)
@@ -1463,6 +1409,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
}
len += key_data_len;
+ if (!mic_len && encr)
+ len += AES_BLOCK_SIZE;
hdr = os_zalloc(len);
if (hdr == NULL)
@@ -1471,7 +1419,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
hdr->length = host_to_be16(len - sizeof(*hdr));
key = (struct wpa_eapol_key *) (hdr + 1);
- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+ key_mic = (u8 *) (key + 1);
key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
key->type = sm->wpa == WPA_VERSION_WPA2 ?
@@ -1484,11 +1432,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
WPA_PUT_BE16(key->key_info, key_info);
alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
- WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
- if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+ if (sm->wpa == WPA_VERSION_WPA2 && !pairwise)
WPA_PUT_BE16(key->key_length, 0);
+ else
+ WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
- /* FIX: STSL: what to use as key_replay_counter? */
for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
os_memcpy(sm->key_replay[i].counter,
@@ -1510,10 +1458,31 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
if (kde && !encr) {
os_memcpy(key_data, kde, kde_len);
- if (mic_len == 24)
- WPA_PUT_BE16(key192->key_data_length, kde_len);
- else
- WPA_PUT_BE16(key->key_data_length, kde_len);
+ WPA_PUT_BE16(key_mic + mic_len, kde_len);
+#ifdef CONFIG_FILS
+ } else if (!mic_len && kde) {
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
+ wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+ kde, kde_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
+ sm->PTK.kek, sm->PTK.kek_len);
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = (u8 *) hdr;
+ aad_len[0] = key_mic + 2 - (u8 *) hdr;
+ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
+ 1, aad, aad_len, key_mic + 2) < 0) {
+ wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+ key_mic + 2, AES_BLOCK_SIZE + kde_len);
+#endif /* CONFIG_FILS */
} else if (encr && kde) {
buf = os_zalloc(key_data_len);
if (buf == NULL) {
@@ -1530,24 +1499,24 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)",
+ (unsigned int) sm->PTK.kek_len);
if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
(key_data_len - 8) / 8, buf, key_data)) {
os_free(hdr);
os_free(buf);
return;
}
- if (mic_len == 24)
- WPA_PUT_BE16(key192->key_data_length,
- key_data_len);
- else
- WPA_PUT_BE16(key->key_data_length,
- key_data_len);
+ WPA_PUT_BE16(key_mic + mic_len, key_data_len);
#ifndef CONFIG_NO_RC4
} else if (sm->PTK.kek_len == 16) {
u8 ek[32];
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Encrypt Key Data using RC4");
os_memcpy(key->key_iv,
sm->group->Counter + WPA_NONCE_LEN - 16, 16);
inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
@@ -1555,12 +1524,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
os_memcpy(key_data, buf, key_data_len);
rc4_skip(ek, 32, 256, key_data, key_data_len);
- if (mic_len == 24)
- WPA_PUT_BE16(key192->key_data_length,
- key_data_len);
- else
- WPA_PUT_BE16(key->key_data_length,
- key_data_len);
+ WPA_PUT_BE16(key_mic + mic_len, key_data_len);
#endif /* CONFIG_NO_RC4 */
} else {
os_free(hdr);
@@ -1571,9 +1535,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
}
if (key_info & WPA_KEY_INFO_MIC) {
- u8 *key_mic;
-
- if (!sm->PTK_valid) {
+ if (!sm->PTK_valid || !mic_len) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PTK not valid when sending EAPOL-Key "
"frame");
@@ -1581,10 +1543,12 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
return;
}
- key_mic = key192->key_mic; /* same offset for key and key192 */
- wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
- sm->wpa_key_mgmt, version,
- (u8 *) hdr, len, key_mic);
+ if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+ sm->wpa_key_mgmt, version,
+ (u8 *) hdr, len, key_mic) < 0) {
+ os_free(hdr);
+ return;
+ }
#ifdef CONFIG_TESTING_OPTIONS
if (!pairwise &&
wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
@@ -1613,7 +1577,7 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
{
int timeout_ms;
int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
- int ctr;
+ u32 ctr;
if (sm == NULL)
return;
@@ -1627,41 +1591,43 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
eapol_key_timeout_first_group;
else
timeout_ms = eapol_key_timeout_subseq;
+ if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ (!pairwise || (key_info & WPA_KEY_INFO_MIC)))
+ timeout_ms = eapol_key_timeout_no_retrans;
if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
sm->pending_1_of_4_timeout = 1;
wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
- "counter %d)", timeout_ms, ctr);
+ "counter %u)", timeout_ms, ctr);
eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
- size_t data_len)
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+ u8 *data, size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- struct wpa_eapol_key_192 *key192;
u16 key_info;
int ret = 0;
- u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
- size_t mic_len = wpa_mic_len(akmp);
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
+ size_t mic_len = wpa_mic_len(akmp, pmk_len);
if (data_len < sizeof(*hdr) + sizeof(*key))
return -1;
hdr = (struct ieee802_1x_hdr *) data;
key = (struct wpa_eapol_key *) (hdr + 1);
- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+ mic_pos = (u8 *) (key + 1);
key_info = WPA_GET_BE16(key->key_info);
- os_memcpy(mic, key192->key_mic, mic_len);
- os_memset(key192->key_mic, 0, mic_len);
+ os_memcpy(mic, mic_pos, mic_len);
+ os_memset(mic_pos, 0, mic_len);
if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
key_info & WPA_KEY_INFO_TYPE_MASK,
- data, data_len, key192->key_mic) ||
- os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
+ data, data_len, mic_pos) ||
+ os_memcmp_const(mic, mic_pos, mic_len) != 0)
ret = -1;
- os_memcpy(key192->key_mic, mic, mic_len);
+ os_memcpy(mic_pos, mic, mic_len);
return ret;
}
@@ -1670,7 +1636,10 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
{
sm->PTK_valid = FALSE;
os_memset(&sm->PTK, 0, sizeof(sm->PTK));
- wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
+ if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
+ 0))
+ wpa_printf(MSG_DEBUG,
+ "RSN: PTK removal from the driver failed");
sm->pairwise_set = FALSE;
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
}
@@ -1734,7 +1703,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
sm->ReAuthenticationRequest = TRUE;
break;
case WPA_ASSOC_FT:
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
"after association");
wpa_ft_install_ptk(sm);
@@ -1742,22 +1711,37 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
/* Using FT protocol, not WPA auth state machine */
sm->ft_completed = 1;
return 0;
-#else /* CONFIG_IEEE80211R */
+#else /* CONFIG_IEEE80211R_AP */
+ break;
+#endif /* CONFIG_IEEE80211R_AP */
+ case WPA_ASSOC_FILS:
+#ifdef CONFIG_FILS
+ wpa_printf(MSG_DEBUG,
+ "FILS: TK configuration after association");
+ fils_set_tk(sm);
+ sm->fils_completed = 1;
+ return 0;
+#else /* CONFIG_FILS */
break;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
case WPA_DRV_STA_REMOVED:
sm->tk_already_set = FALSE;
return 0;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
sm->ft_completed = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
if (sm->mgmt_frame_prot && event == WPA_AUTH)
remove_ptk = 0;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ (event == WPA_AUTH || event == WPA_ASSOC))
+ remove_ptk = 0;
+#endif /* CONFIG_FILS */
if (remove_ptk) {
sm->PTK_valid = FALSE;
@@ -1802,7 +1786,9 @@ SM_STATE(WPA_PTK, INITIALIZE)
wpa_remove_ptk(sm);
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
sm->TimeoutCtr = 0;
- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_authorized, 0);
}
@@ -1811,9 +1797,14 @@ SM_STATE(WPA_PTK, INITIALIZE)
SM_STATE(WPA_PTK, DISCONNECT)
{
+ u16 reason = sm->disconnect_reason;
+
SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
sm->Disconnect = FALSE;
- wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+ sm->disconnect_reason = 0;
+ if (!reason)
+ reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason);
}
@@ -1922,17 +1913,25 @@ SM_STATE(WPA_PTK, INITPMK)
size_t len = 2 * PMK_LEN;
SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
sm->xxkey_len = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (sm->pmksa) {
wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_DPP
+ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PMKSA cache entry for STA - reject connection");
+ sm->Disconnect = TRUE;
+ sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
+ return;
+#endif /* CONFIG_DPP */
} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
unsigned int pmk_len;
- if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
pmk_len = PMK_LEN_SUITE_B_192;
else
pmk_len = PMK_LEN;
@@ -1948,15 +1947,20 @@ SM_STATE(WPA_PTK, INITPMK)
}
os_memcpy(sm->PMK, msk, pmk_len);
sm->pmk_len = pmk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (len >= 2 * PMK_LEN) {
- os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
- sm->xxkey_len = PMK_LEN;
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN);
+ sm->xxkey_len = SHA384_MAC_LEN;
+ } else {
+ os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ }
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
} else {
wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
- sm->wpa_auth->cb.get_msk);
+ sm->wpa_auth->cb->get_msk);
sm->Disconnect = TRUE;
return;
}
@@ -1978,16 +1982,26 @@ SM_STATE(WPA_PTK, INITPMK)
SM_STATE(WPA_PTK, INITPSK)
{
const u8 *psk;
+ size_t psk_len;
+
SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
- psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
+ psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
+ &psk_len);
if (psk) {
- os_memcpy(sm->PMK, psk, PMK_LEN);
- sm->pmk_len = PMK_LEN;
-#ifdef CONFIG_IEEE80211R
+ os_memcpy(sm->PMK, psk, psk_len);
+ sm->pmk_len = psk_len;
+#ifdef CONFIG_IEEE80211R_AP
os_memcpy(sm->xxkey, psk, PMK_LEN);
sm->xxkey_len = PMK_LEN;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+ }
+#ifdef CONFIG_SAE
+ if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+ wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
+ os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+ sm->pmk_len = sm->pmksa->pmk_len;
}
+#endif /* CONFIG_SAE */
sm->req_replay_counter_used = 0;
}
@@ -2003,7 +2017,7 @@ SM_STATE(WPA_PTK, PTKSTART)
sm->alt_snonce_valid = FALSE;
sm->TimeoutCtr++;
- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -2012,11 +2026,23 @@ SM_STATE(WPA_PTK, PTKSTART)
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 1/4 msg of 4-Way Handshake");
/*
- * TODO: Could add PMKID even with WPA2-PSK, but only if there is only
- * one possible PSK for this STA.
+ * For infrastructure BSS cases, it is better for the AP not to include
+ * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate
+ * offline search for the passphrase/PSK without having to be able to
+ * capture a 4-way handshake from a STA that has access to the network.
+ *
+ * For IBSS cases, addition of PMKID KDE could be considered even with
+ * WPA2-PSK cases that use multiple PSKs, but only if there is a single
+ * possible PSK for this STA. However, this should not be done unless
+ * there is support for using that information on the supplicant side.
+ * The concern about exposing PMKID unnecessarily in infrastructure BSS
+ * cases would also apply here, but at least in the IBSS case, this
+ * would cover a potential real use case.
*/
if (sm->wpa == WPA_VERSION_WPA2 &&
- wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+ (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
+ (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
pmkid = buf;
pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
@@ -2024,11 +2050,31 @@ SM_STATE(WPA_PTK, PTKSTART)
pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
if (sm->pmksa) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from PMKSA entry",
+ sm->pmksa->pmkid, PMKID_LEN);
os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
sm->pmksa->pmkid, PMKID_LEN);
} else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
/* No KCK available to derive PMKID */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No KCK available to derive PMKID for message 1/4");
pmkid = NULL;
+#ifdef CONFIG_SAE
+ } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+ if (sm->pmkid_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from SAE",
+ sm->pmkid, PMKID_LEN);
+ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+ sm->pmkid, PMKID_LEN);
+ } else {
+ /* No PMKID available */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No SAE PMKID available for message 1/4");
+ pmkid = NULL;
+ }
+#endif /* CONFIG_SAE */
} else {
/*
* Calculate PMKID since no PMKSA cache entry was
@@ -2036,7 +2082,10 @@ SM_STATE(WPA_PTK, PTKSTART)
*/
rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
- wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+ sm->wpa_key_mgmt);
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID derived from PMK",
+ &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
}
}
wpa_send_eapol(sm->wpa_auth, sm,
@@ -2049,10 +2098,10 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk)
{
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
@@ -2060,43 +2109,587 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
}
+#ifdef CONFIG_FILS
+
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *snonce, const u8 *anonce,
+ const u8 *dhss, size_t dhss_len,
+ struct wpabuf *g_sta, struct wpabuf *g_ap)
+{
+ u8 ick[FILS_ICK_MAX_LEN];
+ size_t ick_len;
+ int res;
+ u8 fils_ft[FILS_FT_MAX_LEN];
+ size_t fils_ft_len = 0;
+
+ res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
+ snonce, anonce, dhss, dhss_len,
+ &sm->PTK, ick, &ick_len,
+ sm->wpa_key_mgmt, sm->pairwise,
+ fils_ft, &fils_ft_len);
+ if (res < 0)
+ return res;
+ sm->PTK_valid = TRUE;
+ sm->tk_already_set = FALSE;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (fils_ft_len) {
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+ size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+
+ if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
+ conf->ssid, conf->ssid_len,
+ conf->mobility_domain,
+ conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ sm->addr, pmk_r0, pmk_r0_name,
+ use_sha384) < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
+ pmk_r0, pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
+ pmk_r0_name, WPA_PMK_NAME_LEN);
+ wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
+ os_memset(fils_ft, 0, sizeof(fils_ft));
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
+ sm->addr, sm->wpa_auth->addr,
+ g_sta ? wpabuf_head(g_sta) : NULL,
+ g_sta ? wpabuf_len(g_sta) : 0,
+ g_ap ? wpabuf_head(g_ap) : NULL,
+ g_ap ? wpabuf_len(g_ap) : 0,
+ sm->wpa_key_mgmt, sm->fils_key_auth_sta,
+ sm->fils_key_auth_ap,
+ &sm->fils_key_auth_len);
+ os_memset(ick, 0, sizeof(ick));
+
+ /* Store nonces for (Re)Association Request/Response frame processing */
+ os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
+ os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+
+ return res;
+}
+
+
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+ u8 *buf, size_t buf_len, u16 *_key_data_len)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u8 *pos;
+ u16 key_data_len;
+ u8 *tmp;
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ pos = (u8 *) (key + 1);
+ key_data_len = WPA_GET_BE16(pos);
+ if (key_data_len < AES_BLOCK_SIZE ||
+ key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "No room for AES-SIV data in the frame");
+ return -1;
+ }
+ pos += 2; /* Pointing at the Encrypted Key Data field */
+
+ tmp = os_malloc(key_data_len);
+ if (!tmp)
+ return -1;
+
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = buf;
+ aad_len[0] = pos - buf;
+ if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
+ 1, aad, aad_len, tmp) < 0) {
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "Invalid AES-SIV data in the frame");
+ bin_clear_free(tmp, key_data_len);
+ return -1;
+ }
+
+ /* AEAD decryption and validation completed successfully */
+ key_data_len -= AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+ tmp, key_data_len);
+
+ /* Replace Key Data field with the decrypted version */
+ os_memcpy(pos, tmp, key_data_len);
+ pos -= 2; /* Key Data Length field */
+ WPA_PUT_BE16(pos, key_data_len);
+ bin_clear_free(tmp, key_data_len);
+ if (_key_data_len)
+ *_key_data_len = key_data_len;
+ return 0;
+}
+
+
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *fils_session)
+{
+ const u8 *ie, *end;
+ const u8 *session = NULL;
+
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not a FILS AKM - reject association");
+ return NULL;
+ }
+
+ /* Verify Session element */
+ ie = ies;
+ end = ((const u8 *) ie) + ies_len;
+ while (ie + 1 < end) {
+ if (ie + 2 + ie[1] > end)
+ break;
+ if (ie[0] == WLAN_EID_EXTENSION &&
+ ie[1] >= 1 + FILS_SESSION_LEN &&
+ ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+ session = ie;
+ break;
+ }
+ ie += 2 + ie[1];
+ }
+
+ if (!session) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: %s: Could not find FILS Session element in Assoc Req - reject",
+ __func__);
+ return NULL;
+ }
+
+ if (!fils_session) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: %s: Could not find FILS Session element in STA entry - reject",
+ __func__);
+ return NULL;
+ }
+
+ if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+ fils_session, FILS_SESSION_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+ session + 3, FILS_SESSION_LEN);
+ return NULL;
+ }
+ return session;
+}
+
+
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len)
+{
+ struct ieee802_11_elems elems;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to parse decrypted elements");
+ return -1;
+ }
+
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ return -1;
+ }
+
+ if (!elems.fils_key_confirm) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+ return -1;
+ }
+
+ if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Unexpected Key-Auth length %d (expected %d)",
+ elems.fils_key_confirm_len,
+ (int) sm->fils_key_auth_len);
+ return -1;
+ }
+
+ if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+ sm->fils_key_auth_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+ elems.fils_key_confirm, elems.fils_key_confirm_len);
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+ sm->fils_key_auth_sta, sm->fils_key_auth_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+ const struct ieee80211_mgmt *mgmt, size_t frame_len,
+ u8 *pos, size_t left)
+{
+ u16 fc, stype;
+ const u8 *end, *ie_start, *ie, *session, *crypt;
+ const u8 *aad[5];
+ size_t aad_len[5];
+
+ if (!sm || !sm->PTK_valid) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No KEK to decrypt Assocication Request frame");
+ return -1;
+ }
+
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not a FILS AKM - reject association");
+ return -1;
+ }
+
+ end = ((const u8 *) mgmt) + frame_len;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ if (stype == WLAN_FC_STYPE_REASSOC_REQ)
+ ie_start = mgmt->u.reassoc_req.variable;
+ else
+ ie_start = mgmt->u.assoc_req.variable;
+ ie = ie_start;
+
+ /*
+ * Find FILS Session element which is the last unencrypted element in
+ * the frame.
+ */
+ session = wpa_fils_validate_fils_session(sm, ie, end - ie,
+ fils_session);
+ if (!session) {
+ wpa_printf(MSG_DEBUG, "FILS: Session validation failed");
+ return -1;
+ }
+
+ crypt = session + 2 + session[1];
+
+ if (end - crypt < AES_BLOCK_SIZE) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Too short frame to include AES-SIV data");
+ return -1;
+ }
+
+ /* AES-SIV AAD vectors */
+
+ /* The STA's MAC address */
+ aad[0] = mgmt->sa;
+ aad_len[0] = ETH_ALEN;
+ /* The AP's BSSID */
+ aad[1] = mgmt->da;
+ aad_len[1] = ETH_ALEN;
+ /* The STA's nonce */
+ aad[2] = sm->SNonce;
+ aad_len[2] = FILS_NONCE_LEN;
+ /* The AP's nonce */
+ aad[3] = sm->ANonce;
+ aad_len[3] = FILS_NONCE_LEN;
+ /*
+ * The (Re)Association Request frame from the Capability Information
+ * field to the FILS Session element (both inclusive).
+ */
+ aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
+ aad_len[4] = crypt - aad[4];
+
+ if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
+ 5, aad, aad_len, pos + (crypt - ie_start)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Invalid AES-SIV data in the frame");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+ pos, left - AES_BLOCK_SIZE);
+
+ if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed");
+ return -1;
+ }
+
+ return left - AES_BLOCK_SIZE;
+}
+
+
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+ size_t current_len, size_t max_len,
+ const struct wpabuf *hlp)
+{
+ u8 *end = buf + max_len;
+ u8 *pos = buf + current_len;
+ struct ieee80211_mgmt *mgmt;
+ struct wpabuf *plain;
+ const u8 *aad[5];
+ size_t aad_len[5];
+
+ if (!sm || !sm->PTK_valid)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG,
+ "FILS: Association Response frame before FILS processing",
+ buf, current_len);
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+
+ /* AES-SIV AAD vectors */
+
+ /* The AP's BSSID */
+ aad[0] = mgmt->sa;
+ aad_len[0] = ETH_ALEN;
+ /* The STA's MAC address */
+ aad[1] = mgmt->da;
+ aad_len[1] = ETH_ALEN;
+ /* The AP's nonce */
+ aad[2] = sm->ANonce;
+ aad_len[2] = FILS_NONCE_LEN;
+ /* The STA's nonce */
+ aad[3] = sm->SNonce;
+ aad_len[3] = FILS_NONCE_LEN;
+ /*
+ * The (Re)Association Response frame from the Capability Information
+ * field (the same offset in both Association and Reassociation
+ * Response frames) to the FILS Session element (both inclusive).
+ */
+ aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+ aad_len[4] = pos - aad[4];
+
+ /* The following elements will be encrypted with AES-SIV */
+ plain = fils_prepare_plainbuf(sm, hlp);
+ if (!plain) {
+ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+ return -1;
+ }
+
+ if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not enough room for FILS elements");
+ wpabuf_free(plain);
+ return -1;
+ }
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
+ plain);
+
+ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
+ wpabuf_head(plain), wpabuf_len(plain),
+ 5, aad, aad_len, pos) < 0) {
+ wpabuf_free(plain);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG,
+ "FILS: Encrypted Association Response elements",
+ pos, AES_BLOCK_SIZE + wpabuf_len(plain));
+ current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
+ wpabuf_free(plain);
+
+ sm->fils_completed = 1;
+
+ return current_len;
+}
+
+
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+ const struct wpabuf *hlp)
+{
+ struct wpabuf *plain;
+ u8 *len, *tmp, *tmp2;
+ u8 hdr[2];
+ u8 *gtk, dummy_gtk[32];
+ size_t gtk_len;
+ struct wpa_group *gsm;
+
+ plain = wpabuf_alloc(1000);
+ if (!plain)
+ return NULL;
+
+ /* TODO: FILS Public Key */
+
+ /* FILS Key Confirmation */
+ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+ wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
+
+ /* FILS HLP Container */
+ if (hlp)
+ wpabuf_put_buf(plain, hlp);
+
+ /* TODO: FILS IP Address Assignment */
+
+ /* Key Delivery */
+ gsm = sm->group;
+ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+ len = wpabuf_put(plain, 1);
+ wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
+ wpabuf_put(plain, WPA_KEY_RSC_LEN));
+ /* GTK KDE */
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+ gtk = dummy_gtk;
+ }
+ hdr[0] = gsm->GN & 0x03;
+ hdr[1] = 0;
+ tmp = wpabuf_put(plain, 0);
+ tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gtk_len);
+ wpabuf_put(plain, tmp2 - tmp);
+
+ /* IGTK KDE */
+ tmp = wpabuf_put(plain, 0);
+ tmp2 = ieee80211w_kde_add(sm, tmp);
+ wpabuf_put(plain, tmp2 - tmp);
+
+ *len = (u8 *) wpabuf_put(plain, 0) - len - 1;
+ return plain;
+}
+
+
+int fils_set_tk(struct wpa_state_machine *sm)
+{
+ enum wpa_alg alg;
+ int klen;
+
+ if (!sm || !sm->PTK_valid) {
+ wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK");
+ return -1;
+ }
+ if (sm->tk_already_set) {
+ wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver");
+ return -1;
+ }
+
+ alg = wpa_cipher_to_alg(sm->pairwise);
+ klen = wpa_cipher_key_len(sm->pairwise);
+
+ wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
+ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+ sm->PTK.tk, klen)) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
+ return -1;
+ }
+ sm->tk_already_set = TRUE;
+
+ return 0;
+}
+
+
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
+ const u8 *fils_session, struct wpabuf *hlp)
+{
+ struct wpabuf *plain;
+ u8 *pos = buf;
+
+ /* FILS Session */
+ *pos++ = WLAN_EID_EXTENSION; /* Element ID */
+ *pos++ = 1 + FILS_SESSION_LEN; /* Length */
+ *pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+ os_memcpy(pos, fils_session, FILS_SESSION_LEN);
+ pos += FILS_SESSION_LEN;
+
+ plain = fils_prepare_plainbuf(sm, hlp);
+ if (!plain) {
+ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+ return NULL;
+ }
+
+ os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain));
+ pos += wpabuf_len(plain);
+
+ wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__,
+ (unsigned int) wpabuf_len(plain));
+ wpabuf_free(plain);
+ sm->fils_completed = 1;
+ return pos;
+}
+
+#endif /* CONFIG_FILS */
+
+
SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
struct wpa_ptk PTK;
int ok = 0, psk_found = 0;
const u8 *pmk = NULL;
- unsigned int pmk_len;
+ size_t pmk_len;
+ int ft;
+ const u8 *eapol_key_ie, *key_data, *mic;
+ u16 key_data_length;
+ size_t mic_len, eapol_key_ie_len;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ struct wpa_eapol_ie_parse kde;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
sm->update_snonce = FALSE;
+ os_memset(&PTK, 0, sizeof(PTK));
+
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
/* WPA with IEEE 802.1X: use the derived PMK from EAP
* WPA-PSK: iterate through possible PSKs and select the one matching
* the packet */
for (;;) {
- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+ !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
- sm->p2p_dev_addr, pmk);
+ sm->p2p_dev_addr, pmk, &pmk_len);
if (pmk == NULL)
break;
psk_found = 1;
- pmk_len = PMK_LEN;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+ sm->xxkey_len = pmk_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
} else {
pmk = sm->PMK;
pmk_len = sm->pmk_len;
}
- wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
+ if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
+ break;
- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+ if (mic_len &&
+ wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
sm->last_rx_eapol_key,
sm->last_rx_eapol_key_len) == 0) {
ok = 1;
break;
}
- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+#ifdef CONFIG_FILS
+ if (!mic_len &&
+ wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len, NULL) == 0) {
+ ok = 1;
+ break;
+ }
+#endif /* CONFIG_FILS */
+
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
@@ -2108,7 +2701,79 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
return;
}
-#ifdef CONFIG_IEEE80211R
+ /*
+ * Note: last_rx_eapol_key length fields have already been validated in
+ * wpa_receive().
+ */
+ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ mic = (u8 *) (key + 1);
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
+ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+ sizeof(*key) - mic_len - 2)
+ return;
+
+ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key msg 2/4 with invalid Key Data contents");
+ return;
+ }
+ if (kde.rsn_ie) {
+ eapol_key_ie = kde.rsn_ie;
+ eapol_key_ie_len = kde.rsn_ie_len;
+ } else if (kde.osen) {
+ eapol_key_ie = kde.osen;
+ eapol_key_ie_len = kde.osen_len;
+ } else {
+ eapol_key_ie = kde.wpa_ie;
+ eapol_key_ie_len = kde.wpa_ie_len;
+ }
+ ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+ if (sm->wpa_ie == NULL ||
+ wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
+ eapol_key_ie, eapol_key_ie_len)) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "WPA IE from (Re)AssocReq did not match with msg 2/4");
+ if (sm->wpa_ie) {
+ wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+ sm->wpa_ie, sm->wpa_ie_len);
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+ eapol_key_ie, eapol_key_ie_len);
+ /* MLME-DEAUTHENTICATE.request */
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+ if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+ wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+ int idx;
+ wpa_printf(MSG_DEBUG,
+ "P2P: IP address requested in EAPOL-Key exchange");
+ idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+ if (idx >= 0) {
+ u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
+ bitfield_set(wpa_auth->ip_pool, idx);
+ WPA_PUT_BE32(sm->ip_addr, start + idx);
+ wpa_printf(MSG_DEBUG,
+ "P2P: Assigned IP address %u.%u.%u.%u to "
+ MACSTR, sm->ip_addr[0], sm->ip_addr[1],
+ sm->ip_addr[2], sm->ip_addr[3],
+ MAC2STR(sm->addr));
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
* Verify that PMKR1Name from EAPOL-Key message 2/4 matches
@@ -2127,7 +2792,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
return;
}
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@@ -2186,7 +2851,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
else
os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random IGTK to each STA to prevent use of
* IGTK in the BSS.
@@ -2229,7 +2895,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
sm->TimeoutEvt = FALSE;
sm->TimeoutCtr++;
- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->TimeoutCtr > 1) {
+ /* Do not allow retransmission of EAPOL-Key msg 3/4 */
+ return;
+ }
+ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -2259,7 +2930,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -2297,12 +2969,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
kde_len += 300; /* FTIE + 2 * TIE */
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr) > 0)
kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
@@ -2314,7 +2986,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
pos = kde;
os_memcpy(pos, wpa_ie, wpa_ie_len);
pos += wpa_ie_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
size_t elen;
@@ -2330,7 +3002,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
pos -= wpa_ie_len;
pos += elen;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (gtk) {
u8 hdr[2];
hdr[0] = keyidx & 0x03;
@@ -2340,7 +3012,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
}
pos = ieee80211w_kde_add(sm, pos);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
struct wpa_auth_config *conf;
@@ -2352,7 +3024,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
2 + sm->assoc_resp_ftie[1]);
res = 2 + sm->assoc_resp_ftie[1];
} else {
- res = wpa_write_ftie(conf, conf->r0_key_holder,
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
+ res = wpa_write_ftie(conf, use_sha384,
+ conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
kde + kde_len - pos,
@@ -2377,10 +3052,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
- WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
pos += 4;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr) > 0) {
u8 addr[3 * 4];
@@ -2393,7 +3068,9 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
#endif /* CONFIG_P2P */
wpa_send_eapol(sm->wpa_auth, sm,
- (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
+ (secure ? WPA_KEY_INFO_SECURE : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
@@ -2410,7 +3087,8 @@ SM_STATE(WPA_PTK, PTKINITDONE)
int klen = wpa_cipher_key_len(sm->pairwise);
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
sm->PTK.tk, klen)) {
- wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
@@ -2423,7 +3101,9 @@ SM_STATE(WPA_PTK, PTKINITDONE)
sm->wpa_auth, sm);
}
- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_authorized, 1);
}
@@ -2449,9 +3129,9 @@ SM_STATE(WPA_PTK, PTKINITDONE)
"pairwise key handshake completed (%s)",
sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
}
@@ -2495,15 +3175,22 @@ SM_STEP(WPA_PTK)
wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_keyRun) > 0)
SM_ENTER(WPA_PTK, INITPMK);
- else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
+ else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE
/* FIX: && 802.1X::keyRun */)
SM_ENTER(WPA_PTK, INITPSK);
+ else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+ SM_ENTER(WPA_PTK, INITPMK);
break;
case WPA_PTK_INITPMK:
if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
- WPA_EAPOL_keyAvailable) > 0)
+ WPA_EAPOL_keyAvailable) > 0) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+#ifdef CONFIG_DPP
+ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) {
SM_ENTER(WPA_PTK, PTKSTART);
- else {
+#endif /* CONFIG_DPP */
+ } else {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"INITPMK - keyAvailable = false");
@@ -2512,9 +3199,13 @@ SM_STEP(WPA_PTK)
break;
case WPA_PTK_INITPSK:
if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
- NULL))
+ NULL, NULL)) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+#ifdef CONFIG_SAE
+ } else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
SM_ENTER(WPA_PTK, PTKSTART);
- else {
+#endif /* CONFIG_SAE */
+ } else {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"no PSK configured for the STA");
wpa_auth->dot11RSNA4WayHandshakeFailures++;
@@ -2526,11 +3217,12 @@ SM_STEP(WPA_PTK)
sm->EAPOLKeyPairwise)
SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
else if (sm->TimeoutCtr >
- (int) dot11RSNAConfigPairwiseUpdateCount) {
+ sm->wpa_auth->conf.wpa_pairwise_update_count) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "PTKSTART: Retry limit %d reached",
- dot11RSNAConfigPairwiseUpdateCount);
+ wpa_auth_vlogger(
+ sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKSTART: Retry limit %u reached",
+ sm->wpa_auth->conf.wpa_pairwise_update_count);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKSTART);
@@ -2554,12 +3246,14 @@ SM_STEP(WPA_PTK)
sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK, PTKINITDONE);
else if (sm->TimeoutCtr >
- (int) dot11RSNAConfigPairwiseUpdateCount) {
+ sm->wpa_auth->conf.wpa_pairwise_update_count ||
+ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->TimeoutCtr > 1)) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "PTKINITNEGOTIATING: Retry limit %d "
- "reached",
- dot11RSNAConfigPairwiseUpdateCount);
+ wpa_auth_vlogger(
+ sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKINITNEGOTIATING: Retry limit %u reached",
+ sm->wpa_auth->conf.wpa_pairwise_update_count);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
@@ -2594,7 +3288,12 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
sm->GTimeoutCtr++;
- if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
+ if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->GTimeoutCtr > 1) {
+ /* Do not allow retransmission of EAPOL-Key group msg 1/2 */
+ return;
+ }
+ if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -2611,7 +3310,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
"sending 1/2 msg of Group Key Handshake");
gtk = gsm->GTK[gsm->GN - 1];
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -2640,10 +3340,12 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
}
wpa_send_eapol(sm->wpa_auth, sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK |
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
- rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
+ rsc, NULL, kde, kde_len, gsm->GN, 1);
os_free(kde_buf);
}
@@ -2672,6 +3374,10 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = FALSE;
sm->Disconnect = TRUE;
+ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "group key handshake failed (%s) after %u tries",
+ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN",
+ sm->wpa_auth->conf.wpa_group_update_count);
}
@@ -2691,7 +3397,9 @@ SM_STEP(WPA_PTK_GROUP)
!sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
else if (sm->GTimeoutCtr >
- (int) dot11RSNAConfigGroupUpdateCount)
+ sm->wpa_auth->conf.wpa_group_update_count ||
+ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->GTimeoutCtr > 1))
SM_ENTER(WPA_PTK_GROUP, KEYERROR);
else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
@@ -2794,7 +3502,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
}
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
/* update GTK when exiting WNM-Sleep Mode */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
{
@@ -2873,7 +3581,7 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
return pos - start;
}
#endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
@@ -3151,8 +3859,8 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
RSN_VERSION,
!!wpa_auth->conf.wpa_strict_rekey,
- dot11RSNAConfigGroupUpdateCount,
- dot11RSNAConfigPairwiseUpdateCount,
+ wpa_auth->conf.wpa_group_update_count,
+ wpa_auth->conf.wpa_pairwise_update_count,
wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
dot11RSNAConfigPMKLifetime,
dot11RSNAConfigPMKReauthThreshold,
@@ -3279,6 +3987,14 @@ int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm)
}
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm)
+{
+ if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+ return 0;
+ return sm->tk_already_set;
+}
+
+
int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
struct rsn_pmksa_cache_entry *entry)
{
@@ -3320,7 +4036,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
sm->wpa_auth->conf.disable_pmksa_caching)
return -1;
- if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
if (pmk_len > PMK_LEN_SUITE_B_192)
pmk_len = PMK_LEN_SUITE_B_192;
} else if (pmk_len > PMK_LEN) {
@@ -3372,6 +4088,29 @@ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
}
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
+{
+ os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+ sm->pmkid_set = 1;
+}
+
+
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp)
+{
+ if (wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+ NULL, 0, wpa_auth->addr, addr, session_timeout,
+ NULL, akmp))
+ return 0;
+
+ return -1;
+}
+
+
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr)
{
@@ -3404,12 +4143,65 @@ void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
}
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ char *buf, size_t len)
+{
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return 0;
+
+ return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len);
+}
+
+
struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+ const u8 *pmkid, int expiration)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa,
+ spa, 0, NULL, WPA_KEY_MGMT_SAE);
+ if (!entry)
+ return NULL;
+
+ os_get_reltime(&now);
+ entry->expiration = now.sec + expiration;
+ return entry;
+}
+
+
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ int ret;
+
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return -1;
+
+ ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry);
+ if (ret < 0)
+ wpa_printf(MSG_DEBUG,
+ "RSN: Failed to store external PMKSA cache for "
+ MACSTR, MAC2STR(entry->spa));
+
+ return ret;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *pmkid)
{
if (!wpa_auth || !wpa_auth->pmksa)
return NULL;
- return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+ return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
}
@@ -3662,6 +4454,14 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
(timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->eapol_status_cb) {
+ sm->eapol_status_cb(sm->eapol_status_cb_ctx1,
+ sm->eapol_status_cb_ctx2);
+ sm->eapol_status_cb = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
}
@@ -3708,3 +4508,343 @@ void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
for (group = wpa_auth->group; group; group = group->next)
wpa_group_config_group_keys(wpa_auth, group);
}
+
+
+#ifdef CONFIG_FILS
+
+struct wpa_auth_fils_iter_data {
+ struct wpa_authenticator *auth;
+ const u8 *cache_id;
+ struct rsn_pmksa_cache_entry *pmksa;
+ const u8 *spa;
+ const u8 *pmkid;
+};
+
+
+static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx)
+{
+ struct wpa_auth_fils_iter_data *data = ctx;
+
+ if (a == data->auth || !a->conf.fils_cache_id_set ||
+ os_memcmp(a->conf.fils_cache_id, data->cache_id,
+ FILS_CACHE_ID_LEN) != 0)
+ return 0;
+ data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+ return data->pmksa != NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, const u8 *pmkid)
+{
+ struct wpa_auth_fils_iter_data idata;
+
+ if (!wpa_auth->conf.fils_cache_id_set)
+ return NULL;
+ idata.auth = wpa_auth;
+ idata.cache_id = wpa_auth->conf.fils_cache_id;
+ idata.pmksa = NULL;
+ idata.spa = sta_addr;
+ idata.pmkid = pmkid;
+ wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata);
+ return idata.pmksa;
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+ u8 *buf, size_t len)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+
+ return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ NULL, NULL, buf, len, NULL, 0);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+ u8 *fils_anonce, u8 *fils_snonce,
+ u8 *fils_kek, size_t *fils_kek_len)
+{
+ os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN);
+ os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN);
+ os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN);
+ *fils_kek_len = sm->PTK.kek_len;
+}
+
+#endif /* CONFIG_FILS */
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ const u8 *anonce = sm->ANonce;
+ u8 anonce_buf[WPA_NONCE_LEN];
+
+ if (change_anonce) {
+ if (random_get_bytes(anonce_buf, WPA_NONCE_LEN))
+ return -1;
+ anonce = anonce_buf;
+ }
+
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 1/4 msg of 4-Way Handshake (TESTING)");
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+ anonce, NULL, 0, 0, 0);
+ return 0;
+}
+
+
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+#ifdef CONFIG_IEEE80211W
+ u8 *opos;
+#endif /* CONFIG_IEEE80211W */
+ size_t gtk_len, kde_len;
+ struct wpa_group *gsm = sm->group;
+ u8 *wpa_ie;
+ int wpa_ie_len, secure, keyidx, encr = 0;
+
+ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+ GTK[GN], IGTK, [FTIE], [TIE * 2])
+ */
+
+ /* Use 0 RSC */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+ wpa_ie = sm->wpa_auth->wpa_ie;
+ wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+ if (sm->wpa == WPA_VERSION_WPA &&
+ (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+ /* WPA-only STA, remove RSN IE and possible MDIE */
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ wpa_ie_len = wpa_ie[1] + 2;
+ }
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 3/4 msg of 4-Way Handshake (TESTING)");
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ /* WPA2 send GTK in the 4-way handshake */
+ secure = 1;
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ keyidx = gsm->GN;
+ _rsc = rsc;
+ encr = 1;
+ } else {
+ /* WPA does not include GTK in msg 3/4 */
+ secure = 0;
+ gtk = NULL;
+ gtk_len = 0;
+ keyidx = 0;
+ _rsc = NULL;
+ if (sm->rx_eapol_key_secure) {
+ /*
+ * It looks like Windows 7 supplicant tries to use
+ * Secure bit in msg 2/4 after having reported Michael
+ * MIC failure and it then rejects the 4-way handshake
+ * if msg 3/4 does not set Secure bit. Work around this
+ * by setting the Secure bit here even in the case of
+ * WPA if the supplicant used it first.
+ */
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "STA used Secure bit in WPA msg 2/4 - "
+ "set Secure for 3/4 as workaround");
+ secure = 1;
+ }
+ }
+
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+ if (gtk)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+ kde_len += 300; /* FTIE + 2 * TIE */
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ kde = os_malloc(kde_len);
+ if (kde == NULL)
+ return -1;
+
+ pos = kde;
+ os_memcpy(pos, wpa_ie, wpa_ie_len);
+ pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ size_t elen;
+
+ elen = pos - kde;
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to insert "
+ "PMKR1Name into RSN IE in EAPOL-Key data");
+ os_free(kde);
+ return -1;
+ }
+ pos -= wpa_ie_len;
+ pos += elen;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ if (gtk) {
+ u8 hdr[2];
+ hdr[0] = keyidx & 0x03;
+ hdr[1] = 0;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gtk_len);
+ }
+#ifdef CONFIG_IEEE80211W
+ opos = pos;
+ pos = ieee80211w_kde_add(sm, pos);
+ if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+ /* skip KDE header and keyid */
+ opos += 2 + RSN_SELECTOR_LEN + 2;
+ os_memset(opos, 0, 6); /* clear PN */
+ }
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ struct wpa_auth_config *conf;
+
+ conf = &sm->wpa_auth->conf;
+ if (sm->assoc_resp_ftie &&
+ kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+ os_memcpy(pos, sm->assoc_resp_ftie,
+ 2 + sm->assoc_resp_ftie[1]);
+ res = 2 + sm->assoc_resp_ftie[1];
+ } else {
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
+ res = wpa_write_ftie(conf, use_sha384,
+ conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ NULL, NULL, pos,
+ kde + kde_len - pos,
+ NULL, 0);
+ }
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+ "into EAPOL-Key Key Data");
+ os_free(kde);
+ return -1;
+ }
+ pos += res;
+
+ /* TIE[ReassociationDeadline] (TU) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+ WPA_PUT_LE32(pos, conf->reassociation_deadline);
+ pos += 4;
+
+ /* TIE[KeyLifetime] (seconds) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
+ pos += 4;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ (secure ? WPA_KEY_INFO_SECURE : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_KEY_TYPE,
+ _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+ os_free(kde);
+ return 0;
+}
+
+
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ u8 rsc[WPA_KEY_RSC_LEN];
+ struct wpa_group *gsm = sm->group;
+ const u8 *kde;
+ u8 *kde_buf = NULL, *pos, hdr[2];
+#ifdef CONFIG_IEEE80211W
+ u8 *opos;
+#endif /* CONFIG_IEEE80211W */
+ size_t kde_len;
+ u8 *gtk;
+
+ /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ /* Use 0 RSC */
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 1/2 msg of Group Key Handshake (TESTING)");
+
+ gtk = gsm->GTK[gsm->GN - 1];
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+ ieee80211w_kde_len(sm);
+ kde_buf = os_malloc(kde_len);
+ if (kde_buf == NULL)
+ return -1;
+
+ kde = pos = kde_buf;
+ hdr[0] = gsm->GN & 0x03;
+ hdr[1] = 0;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gsm->GTK_len);
+#ifdef CONFIG_IEEE80211W
+ opos = pos;
+ pos = ieee80211w_kde_add(sm, pos);
+ if (pos - opos >=
+ 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+ /* skip KDE header and keyid */
+ opos += 2 + RSN_SELECTOR_LEN + 2;
+ os_memset(opos, 0, 6); /* clear PN */
+ }
+#endif /* CONFIG_IEEE80211W */
+ kde_len = pos - kde;
+ } else {
+ kde = gtk;
+ kde_len = gsm->GTK_len;
+ }
+
+ sm->eapol_status_cb = cb;
+ sm->eapol_status_cb_ctx1 = ctx1;
+ sm->eapol_status_cb_ctx2 = ctx2;
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_SECURE |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK |
+ (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+ rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+ os_free(kde_buf);
+ return 0;
+}
+
+
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
+{
+ if (!wpa_auth)
+ return -1;
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+ return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 97461b029d24..fad5536f740e 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-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 "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
+struct vlan_description;
+
#define MAX_OWN_IE_OVERRIDE 256
#ifdef _MSC_VER
@@ -37,74 +39,100 @@ struct ft_rrb_frame {
#define FT_PACKET_REQUEST 0
#define FT_PACKET_RESPONSE 1
-/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
-#define FT_PACKET_R0KH_R1KH_PULL 200
-#define FT_PACKET_R0KH_R1KH_RESP 201
-#define FT_PACKET_R0KH_R1KH_PUSH 202
-
-#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
-#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
- WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \
- ETH_ALEN)
-#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8)
-
-struct ft_r0kh_r1kh_pull_frame {
- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
- u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
- le16 data_length; /* little endian length of data (44) */
- u8 ap_address[ETH_ALEN];
- u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
- u8 pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 r1kh_id[FT_R1KH_ID_LEN];
- u8 s1kh_id[ETH_ALEN];
- u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */
- u8 key_wrap_extra[8];
-} STRUCT_PACKED;
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These
+ * use OUI Extended EtherType as the encapsulating format. */
+#define FT_PACKET_R0KH_R1KH_PULL 0x01
+#define FT_PACKET_R0KH_R1KH_RESP 0x02
+#define FT_PACKET_R0KH_R1KH_PUSH 0x03
+#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
+#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
+
+/* packet layout
+ * IEEE 802 extended OUI ethertype frame header
+ * u16 authlen (little endian)
+ * multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
+ * multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
+ * blocksize length)
+ *
+ * AES-SIV AAD;
+ * source MAC address (6)
+ * authenticated-only TLVs (authlen)
+ * subtype (1; FT_PACKET_*)
+ */
-#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
- FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \
- WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_resp_frame {
- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
- u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
- le16 data_length; /* little endian length of data (78) */
- u8 ap_address[ETH_ALEN];
+#define FT_RRB_NONCE_LEN 16
- u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
- u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
- u8 s1kh_id[ETH_ALEN]; /* copied from pull */
- u8 pmk_r1[PMK_LEN];
- u8 pmk_r1_name[WPA_PMK_NAME_LEN];
- le16 pairwise;
- u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */
- u8 key_wrap_extra[8];
-} STRUCT_PACKED;
+#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */
-#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \
- WPA_PMK_NAME_LEN + PMK_LEN + \
- WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_push_frame {
- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
- u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
- le16 data_length; /* little endian length of data (82) */
- u8 ap_address[ETH_ALEN];
+#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */
+#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */
+#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */
+
+#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */
+#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */
+#define FT_RRB_S1KH_ID 6 /* ETH_ALEN */
+
+#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R0 8 /* PMK_LEN */
+#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R1 10 /* PMK_LEN */
+
+#define FT_RRB_PAIRWISE 11 /* le16 */
+#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */
+
+#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */
+#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */
- /* Encrypted with AES key-wrap */
- u8 timestamp[4]; /* current time in seconds since unix epoch, little
- * endian */
- u8 r1kh_id[FT_R1KH_ID_LEN];
- u8 s1kh_id[ETH_ALEN];
- u8 pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 pmk_r1[PMK_LEN];
- u8 pmk_r1_name[WPA_PMK_NAME_LEN];
- le16 pairwise;
- u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */
- u8 key_wrap_extra[8];
+#define FT_RRB_IDENTITY 15
+#define FT_RRB_RADIUS_CUI 16
+#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */
+
+struct ft_rrb_tlv {
+ le16 type;
+ le16 len;
+ /* followed by data of length len */
+} STRUCT_PACKED;
+
+struct ft_rrb_seq {
+ le32 dom;
+ le32 seq;
+ le32 ts;
} STRUCT_PACKED;
+/* session TLVs:
+ * required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI,
+ * SESSION_TIMEOUT
+ *
+ * pull frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: PMK_R0_NAME, S1KH_ID
+ *
+ * response frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: S1KH_ID
+ * optional: session TLVs
+ *
+ * push frame TLVs:
+ * auth:
+ * required: SEQ, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: S1KH_ID, PMK_R0_NAME, session TLVs
+ *
+ * sequence number request frame TLVs:
+ * auth:
+ * required: R0KH_ID, R1KH_ID, NONCE
+ *
+ * sequence number response frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ */
+
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
@@ -116,6 +144,7 @@ struct wpa_authenticator;
struct wpa_state_machine;
struct rsn_pmksa_cache_entry;
struct eapol_state_machine;
+struct ft_remote_seq;
struct ft_remote_r0kh {
@@ -123,7 +152,8 @@ struct ft_remote_r0kh {
u8 addr[ETH_ALEN];
u8 id[FT_R0KH_ID_MAX_LEN];
size_t id_len;
- u8 key[16];
+ u8 key[32];
+ struct ft_remote_seq *seq;
};
@@ -131,7 +161,8 @@ struct ft_remote_r1kh {
struct ft_remote_r1kh *next;
u8 addr[ETH_ALEN];
u8 id[FT_R1KH_ID_LEN];
- u8 key[16];
+ u8 key[32];
+ struct ft_remote_seq *seq;
};
@@ -144,10 +175,12 @@ struct wpa_auth_config {
int wpa_strict_rekey;
int wpa_gmk_rekey;
int wpa_ptk_rekey;
+ u32 wpa_group_update_count;
+ u32 wpa_pairwise_update_count;
+ int wpa_disable_eapol_key_retries;
int rsn_pairwise;
int rsn_preauth;
int eapol_version;
- int peerkey;
int wmm_enabled;
int wmm_uapsd;
int disable_pmksa_caching;
@@ -156,21 +189,28 @@ struct wpa_auth_config {
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
int group_mgmt_cipher;
+ int sae_require_mfp;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
size_t r0_key_holder_len;
u8 r1_key_holder[FT_R1KH_ID_LEN];
- u32 r0_key_lifetime;
+ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
+ int rkh_pos_timeout;
+ int rkh_neg_timeout;
+ int rkh_pull_timeout; /* ms */
+ int rkh_pull_retries;
+ int r1_max_key_lifetime;
u32 reassociation_deadline;
- struct ft_remote_r0kh *r0kh_list;
- struct ft_remote_r1kh *r1kh_list;
+ struct ft_remote_r0kh **r0kh_list;
+ struct ft_remote_r1kh **r1kh_list;
int pmk_r1_push;
int ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+ int ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
int disable_gtk;
int ap_mlme;
#ifdef CONFIG_TESTING_OPTIONS
@@ -184,6 +224,10 @@ struct wpa_auth_config {
u8 ip_addr_start[4];
u8 ip_addr_end[4];
#endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+ unsigned int fils_cache_id_set:1;
+ u8 fils_cache_id[FILS_CACHE_ID_LEN];
+#endif /* CONFIG_FILS */
};
typedef enum {
@@ -197,7 +241,6 @@ typedef enum {
} wpa_eapol_variable;
struct wpa_auth_callbacks {
- void *ctx;
void (*logger)(void *ctx, const u8 *addr, logger_level level,
const char *txt);
void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
@@ -207,7 +250,7 @@ struct wpa_auth_callbacks {
int value);
int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
- const u8 *prev_psk);
+ const u8 *prev_psk, size_t *psk_len);
int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
const u8 *addr, int idx, u8 *key, size_t key_len);
@@ -220,13 +263,29 @@ struct wpa_auth_callbacks {
void *ctx), void *cb_ctx);
int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
size_t data_len);
-#ifdef CONFIG_IEEE80211R
+ int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
+ size_t data_len);
+#ifdef CONFIG_IEEE80211R_AP
struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
+ int (*set_vlan)(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan);
+ int (*get_vlan)(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan);
+ int (*set_identity)(void *ctx, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len);
+ size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf);
+ int (*set_radius_cui)(void *ctx, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len);
+ size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf);
+ void (*set_session_timeout)(void *ctx, const u8 *sta_addr,
+ int session_timeout);
+ int (*get_session_timeout)(void *ctx, const u8 *sta_addr);
+
int (*send_ft_action)(void *ctx, const u8 *dst,
const u8 *data, size_t data_len);
int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
size_t tspec_ielen);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_MESH
int (*start_ampe)(void *ctx, const u8 *sta_addr);
#endif /* CONFIG_MESH */
@@ -234,7 +293,8 @@ struct wpa_auth_callbacks {
struct wpa_authenticator * wpa_init(const u8 *addr,
struct wpa_auth_config *conf,
- struct wpa_auth_callbacks *cb);
+ const struct wpa_auth_callbacks *cb,
+ void *cb_ctx);
int wpa_init_keys(struct wpa_authenticator *wpa_auth);
void wpa_deinit(struct wpa_authenticator *wpa_auth);
int wpa_reconfig(struct wpa_authenticator *wpa_auth,
@@ -244,13 +304,14 @@ enum {
WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
- WPA_INVALID_MDIE, WPA_INVALID_PROTO
+ WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID
};
-
+
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len,
- const u8 *mdie, size_t mdie_len);
+ const u8 *mdie, size_t mdie_len,
+ const u8 *owe_dh, size_t owe_dh_len);
int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
@@ -267,7 +328,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
u8 *data, size_t data_len);
enum wpa_event {
WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
- WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_DRV_STA_REMOVED
+ WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED
};
void wpa_remove_ptk(struct wpa_state_machine *sm);
int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
@@ -281,6 +342,7 @@ int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
struct rsn_pmksa_cache_entry *entry);
struct rsn_pmksa_cache_entry *
@@ -297,13 +359,28 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
struct eapol_state_machine *eapol);
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, const u8 *pmkid);
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
size_t len);
void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ char *buf, size_t len);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+ const u8 *pmkid, int expiration);
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+ struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *pmkid);
struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr);
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, const u8 *pmkid);
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
@@ -312,7 +389,7 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int ack);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
size_t max_len, int auth_alg,
const u8 *req_ies, size_t req_ies_len);
@@ -327,8 +404,13 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
const u8 *data, size_t data_len);
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+ size_t data_len);
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
-#endif /* CONFIG_IEEE80211R */
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
+#endif /* CONFIG_IEEE80211R_AP */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
@@ -347,5 +429,44 @@ void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *snonce, const u8 *anonce,
+ const u8 *dhss, size_t dhss_len,
+ struct wpabuf *g_sta, struct wpabuf *g_ap);
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+ const struct ieee80211_mgmt *mgmt, size_t frame_len,
+ u8 *pos, size_t left);
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+ size_t current_len, size_t max_len,
+ const struct wpabuf *hlp);
+int fils_set_tk(struct wpa_state_machine *sm);
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid,
+ const u8 *fils_session,
+ struct wpabuf *fils_hlp_resp);
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *fils_session);
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len);
+
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+ u8 *buf, size_t len);
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+ u8 *fils_anonce, u8 *fils_snonce,
+ u8 *fils_kek, size_t *fils_kek_len);
+u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
+ u8 *pos, size_t max_len,
+ const u8 *req_ies, size_t req_ies_len);
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2);
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2);
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2);
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index e63b99ad2034..f6792e00f32d 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2015, Jouni Malinen <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.
@@ -13,7 +13,10 @@
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
#include "crypto/aes_wrap.h"
+#include "crypto/sha384.h"
#include "crypto/random.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -22,41 +25,692 @@
#include "wpa_auth_i.h"
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
+
+const unsigned int ftRRBseqTimeout = 10;
+const unsigned int ftRRBmaxQueueLen = 100;
+
static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
const u8 *current_ap, const u8 *sta_addr,
u16 status, const u8 *resp_ies,
size_t resp_ies_len);
+static void ft_finish_pull(struct wpa_state_machine *sm);
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
+
+struct tlv_list {
+ u16 type;
+ size_t len;
+ const u8 *data;
+};
+
+
+/**
+ * wpa_ft_rrb_decrypt - Decrypt FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @enc: Pointer to encrypted TLVs
+ * @enc_len: Length of encrypted TLVs in octets
+ * @auth: Pointer to authenticated TLVs
+ * @auth_len: Length of authenticated TLVs in octets
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @plain: Pointer to return the pointer to the allocated plaintext buffer;
+ * needs to be freed by the caller if not NULL;
+ * will only be returned on success
+ * @plain_len: Pointer to return the length of the allocated plaintext buffer
+ * in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
+ const u8 *enc, const size_t enc_len,
+ const u8 *auth, const size_t auth_len,
+ const u8 *src_addr, u8 type,
+ u8 **plain, size_t *plain_size)
+{
+ const u8 *ad[3] = { src_addr, auth, &type };
+ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
+
+ if (!key) { /* skip decryption */
+ *plain = os_memdup(enc, enc_len);
+ if (enc_len > 0 && !*plain)
+ goto err;
+
+ *plain_size = enc_len;
+
+ return 0;
+ }
+
+ *plain = NULL;
+
+ /* SIV overhead */
+ if (enc_len < AES_BLOCK_SIZE)
+ goto err;
+
+ *plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
+ if (!*plain)
+ goto err;
+
+ if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
+ *plain) < 0)
+ goto err;
+
+ *plain_size = enc_len - AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
+ *plain, *plain_size);
+ return 0;
+err:
+ os_free(*plain);
+ *plain = NULL;
+ *plain_size = 0;
+
+ wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
+
+ return -1;
+}
+
+
+/* get first tlv record in packet matching type
+ * @data (decrypted) packet
+ * @return 0 on success else -1
+ */
+static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
+ u16 type, size_t *tlv_len, const u8 **tlv_data)
+{
+ const struct ft_rrb_tlv *f;
+ size_t left;
+ le16 type16;
+ size_t len;
+
+ left = plain_len;
+ type16 = host_to_le16(type);
+
+ while (left >= sizeof(*f)) {
+ f = (const struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+ len = le_to_host16(f->len);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+ break;
+ }
+
+ if (f->type == type16) {
+ *tlv_len = len;
+ *tlv_data = plain;
+ return 0;
+ }
+
+ left -= len;
+ plain += len;
+ }
+
+ return -1;
+}
+
+
+static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
+{
+ const struct ft_rrb_tlv *f;
+ size_t left;
+ size_t len;
+
+ left = plain_len;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB dump message");
+ while (left >= sizeof(*f)) {
+ f = (const struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+ len = le_to_host16(f->len);
+
+ wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
+ le_to_host16(f->type), len);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB message truncated: left %zu bytes, need %zu",
+ left, len);
+ break;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
+
+ left -= len;
+ plain += len;
+ }
+
+ if (left > 0)
+ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
+
+ wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
+}
+
+
+static int cmp_int(const void *a, const void *b)
+{
+ int x, y;
+
+ x = *((int *) a);
+ y = *((int *) b);
+ return x - y;
+}
+
+
+static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
+ struct vlan_description *vlan)
+{
+ struct ft_rrb_tlv *f;
+ size_t left;
+ size_t len;
+ int taggedidx;
+ int vlan_id;
+ int type;
+
+ left = plain_len;
+ taggedidx = 0;
+ os_memset(vlan, 0, sizeof(*vlan));
+
+ while (left >= sizeof(*f)) {
+ f = (struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+
+ len = le_to_host16(f->len);
+ type = le_to_host16(f->type);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+ return -1;
+ }
+
+ if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
+ goto skip;
+
+ if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN_UNTAGGED invalid length");
+ return -1;
+ }
+
+ if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN_TAGGED invalid length");
+ return -1;
+ }
+
+ while (len >= sizeof(le16)) {
+ vlan_id = WPA_GET_LE16(plain);
+ plain += sizeof(le16);
+ left -= sizeof(le16);
+ len -= sizeof(le16);
+
+ if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN ID invalid %d",
+ vlan_id);
+ continue;
+ }
+
+ if (type == FT_RRB_VLAN_UNTAGGED)
+ vlan->untagged = vlan_id;
+
+ if (type == FT_RRB_VLAN_TAGGED &&
+ taggedidx < MAX_NUM_TAGGED_VLAN) {
+ vlan->tagged[taggedidx] = vlan_id;
+ taggedidx++;
+ } else if (type == FT_RRB_VLAN_TAGGED) {
+ wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
+ }
+ }
+
+ skip:
+ left -= len;
+ plain += len;
+ }
+
+ if (taggedidx)
+ qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
+
+ vlan->notempty = vlan->untagged || vlan->tagged[0];
+
+ return 0;
+}
+
+
+static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
+{
+ size_t tlv_len = 0;
+ int i;
+
+ if (!tlvs)
+ return 0;
+
+ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ tlv_len += tlvs[i].len;
+ }
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
+ u8 *endpos)
+{
+ int i;
+ size_t tlv_len;
+ struct ft_rrb_tlv *hdr;
+ u8 *pos;
+
+ if (!tlvs)
+ return 0;
+
+ tlv_len = 0;
+ pos = start;
+ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+ if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
+ return tlv_len;
+ tlv_len += sizeof(*hdr);
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(tlvs[i].type);
+ hdr->len = host_to_le16(tlvs[i].len);
+ pos = start + tlv_len;
+
+ if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
+ return tlv_len;
+ if (tlvs[i].len == 0)
+ continue;
+ tlv_len += tlvs[i].len;
+ os_memcpy(pos, tlvs[i].data, tlvs[i].len);
+ pos = start + tlv_len;
+ }
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
+{
+ size_t tlv_len = 0;
+ int i;
+
+ if (!vlan || !vlan->notempty)
+ return 0;
+
+ if (vlan->untagged) {
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ tlv_len += sizeof(le16);
+ }
+ if (vlan->tagged[0])
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
+ tlv_len += sizeof(le16);
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
+ u8 *start, u8 *endpos)
+{
+ size_t tlv_len;
+ int i, len;
+ struct ft_rrb_tlv *hdr;
+ u8 *pos = start;
+
+ if (!vlan || !vlan->notempty)
+ return 0;
+
+ tlv_len = 0;
+ if (vlan->untagged) {
+ tlv_len += sizeof(*hdr);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
+ hdr->len = host_to_le16(sizeof(le16));
+ pos = start + tlv_len;
+
+ tlv_len += sizeof(le16);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ WPA_PUT_LE16(pos, vlan->untagged);
+ pos = start + tlv_len;
+ }
+
+ if (!vlan->tagged[0])
+ return tlv_len;
+
+ tlv_len += sizeof(*hdr);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
+ len = 0; /* len is computed below */
+ pos = start + tlv_len;
+
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
+ tlv_len += sizeof(le16);
+ if (start + tlv_len > endpos)
+ break;
+ len += sizeof(le16);
+ WPA_PUT_LE16(pos, vlan->tagged[i]);
+ pos = start + tlv_len;
+ }
+
+ hdr->len = host_to_le16(len);
+
+ return tlv_len;
+}
+static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
+ const struct tlv_list *tlvs2,
+ const struct vlan_description *vlan,
+ u8 **plain, size_t *plain_len)
+{
+ u8 *pos, *endpos;
+ size_t tlv_len;
+
+ tlv_len = wpa_ft_tlv_len(tlvs1);
+ tlv_len += wpa_ft_tlv_len(tlvs2);
+ tlv_len += wpa_ft_vlan_len(vlan);
+
+ *plain_len = tlv_len;
+ *plain = os_zalloc(tlv_len);
+ if (!*plain) {
+ wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
+ goto err;
+ }
+
+ pos = *plain;
+ endpos = *plain + tlv_len;
+ pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
+ pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+ pos += wpa_ft_vlan_lin(vlan, pos, endpos);
+
+ /* sanity check */
+ if (pos != endpos) {
+ wpa_printf(MSG_ERROR, "FT: Length error building RRB");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ os_free(*plain);
+ *plain = NULL;
+ *plain_len = 0;
+ return -1;
+}
+
+
+static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
+ const u8 *plain, const size_t plain_len,
+ const u8 *auth, const size_t auth_len,
+ const u8 *src_addr, u8 type, u8 *enc)
+{
+ const u8 *ad[3] = { src_addr, auth, &type };
+ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
+ plain, plain_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
+
+ if (!key) {
+ /* encryption not needed, return plaintext as packet */
+ os_memcpy(enc, plain, plain_len);
+ } else if (aes_siv_encrypt(key, key_len, plain, plain_len,
+ 3, ad, ad_len, enc) < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_rrb_build - Build and encrypt an FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @tlvs_enc0: First set of to-be-encrypted TLVs
+ * @tlvs_enc1: Second set of to-be-encrypted TLVs
+ * @tlvs_auth: Set of to-be-authenticated TLVs
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @packet Pointer to return the pointer to the allocated packet buffer;
+ * needs to be freed by the caller if not null;
+ * will only be returned on success
+ * @packet_len: Pointer to return the length of the allocated buffer in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
+ const struct tlv_list *tlvs_enc0,
+ const struct tlv_list *tlvs_enc1,
+ const struct tlv_list *tlvs_auth,
+ const struct vlan_description *vlan,
+ const u8 *src_addr, u8 type,
+ u8 **packet, size_t *packet_len)
+{
+ u8 *plain = NULL, *auth = NULL, *pos;
+ size_t plain_len = 0, auth_len = 0;
+ int ret = -1;
+
+ *packet = NULL;
+ if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
+ goto out;
+
+ if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
+ goto out;
+
+ *packet_len = sizeof(u16) + auth_len + plain_len;
+ if (key)
+ *packet_len += AES_BLOCK_SIZE;
+ *packet = os_zalloc(*packet_len);
+ if (!*packet)
+ goto out;
+
+ pos = *packet;
+ WPA_PUT_LE16(pos, auth_len);
+ pos += 2;
+ os_memcpy(pos, auth, auth_len);
+ pos += auth_len;
+ if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
+ auth_len, src_addr, type, pos) < 0)
+ goto out;
+
+ ret = 0;
+
+out:
+ bin_clear_free(plain, plain_len);
+ os_free(auth);
+
+ if (ret) {
+ wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
+ os_free(*packet);
+ *packet = NULL;
+ *packet_len = 0;
+ }
+
+ return ret;
+}
+
+
+#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
+ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+ &f_##field##_len, &f_##field) < 0 || \
+ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+ wpa_printf(MSG_INFO, "FT: Missing required " #field \
+ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+ wpa_ft_rrb_dump(srcfield, srcfield##_len); \
+ goto out; \
+ } \
+} while (0)
+
+#define RRB_GET(type, field, txt, checklength) \
+ RRB_GET_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_AUTH(type, field, txt, checklength) \
+ RRB_GET_SRC(auth, type, field, txt, checklength)
+
+#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
+ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+ &f_##field##_len, &f_##field) < 0 || \
+ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+ wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
+ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+ f_##field##_len = 0; \
+ f_##field = NULL; \
+ } \
+} while (0)
+
+#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
+ RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
+ RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
+
static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
const u8 *data, size_t data_len)
{
- if (wpa_auth->cb.send_ether == NULL)
+ if (wpa_auth->cb->send_ether == NULL)
return -1;
wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
- return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB,
- data, data_len);
+ return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB,
+ data, data_len);
+}
+
+
+static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
+ const u8 *dst, u8 oui_suffix,
+ const u8 *data, size_t data_len)
+{
+ if (!wpa_auth->cb->send_oui)
+ return -1;
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR,
+ oui_suffix, MAC2STR(dst));
+ return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
+ data_len);
}
static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
const u8 *dst, const u8 *data, size_t data_len)
{
- if (wpa_auth->cb.send_ft_action == NULL)
+ if (wpa_auth->cb->send_ft_action == NULL)
return -1;
- return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst,
- data, data_len);
+ return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst,
+ data, data_len);
+}
+
+
+static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk)
+{
+ if (wpa_auth->cb->get_psk == NULL)
+ return NULL;
+ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+ prev_psk, NULL);
}
static struct wpa_state_machine *
wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
{
- if (wpa_auth->cb.add_sta == NULL)
+ if (wpa_auth->cb->add_sta == NULL)
return NULL;
- return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);
+ return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
+}
+
+
+static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, struct vlan_description *vlan)
+{
+ if (!wpa_auth->cb->set_vlan)
+ return -1;
+ return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, struct vlan_description *vlan)
+{
+ if (!wpa_auth->cb->get_vlan)
+ return -1;
+ return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int
+wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len)
+{
+ if (!wpa_auth->cb->set_identity)
+ return -1;
+ return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
+ identity_len);
+}
+
+
+static size_t
+wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 **buf)
+{
+ *buf = NULL;
+ if (!wpa_auth->cb->get_identity)
+ return 0;
+ return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static int
+wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ if (!wpa_auth->cb->set_radius_cui)
+ return -1;
+ return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
+ radius_cui, radius_cui_len);
+}
+
+
+static size_t
+wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 **buf)
+{
+ *buf = NULL;
+ if (!wpa_auth->cb->get_radius_cui)
+ return 0;
+ return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static void
+wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, int session_timeout)
+{
+ if (!wpa_auth->cb->set_session_timeout)
+ return;
+ wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
+ session_timeout);
+}
+
+
+static int
+wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr)
+{
+ if (!wpa_auth->cb->get_session_timeout)
+ return 0;
+ return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
}
@@ -64,12 +718,12 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr,
u8 *tspec_ie, size_t tspec_ielen)
{
- if (wpa_auth->cb.add_tspec == NULL) {
+ if (wpa_auth->cb->add_tspec == NULL) {
wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
return -1;
}
- return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
- tspec_ielen);
+ return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
+ tspec_ielen);
}
@@ -93,30 +747,44 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
}
-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
- size_t r0kh_id_len,
+int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+ const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len)
{
u8 *pos = buf, *ielen;
- struct rsn_ftie *hdr;
+ size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
+ sizeof(struct rsn_ftie);
- if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+ if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
subelem_len)
return -1;
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ielen = pos++;
- hdr = (struct rsn_ftie *) pos;
- os_memset(hdr, 0, sizeof(*hdr));
- pos += sizeof(*hdr);
- WPA_PUT_LE16(hdr->mic_control, 0);
- if (anonce)
- os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
- if (snonce)
- os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ WPA_PUT_LE16(hdr->mic_control, 0);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ WPA_PUT_LE16(hdr->mic_control, 0);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ }
/* Optional Parameters */
*pos++ = FTIE_SUBELEM_R1KH_ID;
@@ -142,35 +810,432 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
}
+/* A packet to be handled after seq response */
+struct ft_remote_item {
+ struct dl_list list;
+
+ u8 nonce[FT_RRB_NONCE_LEN];
+ struct os_reltime nonce_ts;
+
+ u8 src_addr[ETH_ALEN];
+ u8 *enc;
+ size_t enc_len;
+ u8 *auth;
+ size_t auth_len;
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer);
+};
+
+
+static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
+{
+ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
+ dl_list_del(&item->list);
+ bin_clear_free(item->enc, item->enc_len);
+ os_free(item->auth);
+ os_free(item);
+}
+
+
+static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, int cb)
+{
+ struct ft_remote_item *item, *n;
+
+ dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
+ struct ft_remote_item, list) {
+ if (cb && item->cb)
+ item->cb(wpa_auth, item->src_addr, item->enc,
+ item->enc_len, item->auth, item->auth_len, 1);
+ wpa_ft_rrb_seq_free(item);
+ }
+}
+
+
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct ft_remote_item *item = timeout_ctx;
+
+ wpa_ft_rrb_seq_free(item);
+}
+
+
+static int
+wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+ const u8 *f_r1kh_id, const u8 *key, size_t key_len,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer))
+{
+ struct ft_remote_item *item = NULL;
+ u8 *packet = NULL;
+ size_t packet_len;
+ struct tlv_list seq_req_auth[] = {
+ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+ .data = NULL /* to be filled: item->nonce */ },
+ { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
+ .data = f_r0kh_id },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = f_r1kh_id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
+ wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
+ goto err;
+ }
+
+ item = os_zalloc(sizeof(*item));
+ if (!item)
+ goto err;
+
+ os_memcpy(item->src_addr, src_addr, ETH_ALEN);
+ item->cb = cb;
+
+ if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
+ goto err;
+ }
+
+ if (os_get_reltime(&item->nonce_ts) < 0)
+ goto err;
+
+ if (enc && enc_len > 0) {
+ item->enc = os_memdup(enc, enc_len);
+ item->enc_len = enc_len;
+ if (!item->enc)
+ goto err;
+ }
+
+ if (auth && auth_len > 0) {
+ item->auth = os_memdup(auth, auth_len);
+ item->auth_len = auth_len;
+ if (!item->auth)
+ goto err;
+ }
+
+ eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
+ wpa_auth, item);
+
+ seq_req_auth[0].data = item->nonce;
+
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
+ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ &packet, &packet_len) < 0) {
+ item = NULL; /* some other seq resp might still accept this */
+ goto err;
+ }
+
+ dl_list_add(&rkh_seq->rx.queue, &item->list);
+
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ packet, packet_len);
+
+ os_free(packet);
+
+ return 0;
+err:
+ wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
+ if (item) {
+ os_free(item->auth);
+ bin_clear_free(item->enc, item->enc_len);
+ os_free(item);
+ }
+
+ return -1;
+}
+
+
+#define FT_RRB_SEQ_OK 0
+#define FT_RRB_SEQ_DROP 1
+#define FT_RRB_SEQ_DEFER 2
+
+static int
+wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype, int no_defer)
+{
+ const u8 *f_seq;
+ size_t f_seq_len;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_seq, msg_off, rkh_off;
+ struct os_reltime now;
+ unsigned int i;
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+ wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ if (rkh_seq->rx.num_last == 0) {
+ /* first packet from remote */
+ goto defer;
+ }
+
+ if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
+ /* remote might have rebooted */
+ goto defer;
+ }
+
+ if (os_get_reltime(&now) == 0) {
+ u32 msg_ts_now_remote, msg_ts_off;
+ struct os_reltime now_remote;
+
+ os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
+ msg_ts_now_remote = now_remote.sec;
+ msg_ts_off = le_to_host32(msg_both->ts) -
+ (msg_ts_now_remote - ftRRBseqTimeout);
+ if (msg_ts_off > 2 * ftRRBseqTimeout)
+ goto defer;
+ }
+
+ msg_seq = le_to_host32(msg_both->seq);
+ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+ msg_off = msg_seq - rkh_off;
+ if (msg_off > 0xC0000000)
+ goto out; /* too old message, drop it */
+
+ if (msg_off <= 0x40000000) {
+ for (i = 0; i < rkh_seq->rx.num_last; i++) {
+ if (rkh_seq->rx.last[i] == msg_seq)
+ goto out; /* duplicate message, drop it */
+ }
+
+ return FT_RRB_SEQ_OK;
+ }
+
+defer:
+ if (no_defer)
+ goto out;
+
+ wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
+ MACSTR, msgtype, MAC2STR(src_addr));
+
+ return FT_RRB_SEQ_DEFER;
+out:
+ wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
+ msgtype, MAC2STR(src_addr));
+
+ return FT_RRB_SEQ_DROP;
+}
+
+
+static void
+wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype)
+{
+ const u8 *f_seq;
+ size_t f_seq_len;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_seq, msg_off, min_off, rkh_off;
+ int minidx = 0;
+ unsigned int i;
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ msg_seq = le_to_host32(msg_both->seq);
+
+ if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
+ rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
+ rkh_seq->rx.num_last++;
+ return;
+ }
+
+ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+ for (i = 0; i < rkh_seq->rx.num_last; i++) {
+ msg_off = rkh_seq->rx.last[i] - rkh_off;
+ min_off = rkh_seq->rx.last[minidx] - rkh_off;
+ if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
+ minidx = i;
+ }
+ rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
+ rkh_seq->rx.offsetidx = minidx;
+
+ return;
+out:
+ /* RRB_GET_AUTH should never fail here as
+ * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
+ wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
+}
+
+
+static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
+ struct ft_rrb_seq *f_seq)
+{
+ struct os_reltime now;
+
+ if (os_get_reltime(&now) < 0)
+ return -1;
+
+ if (!rkh_seq->tx.dom) {
+ if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
+ sizeof(rkh_seq->tx.seq))) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to get random data for sequence number initialization");
+ rkh_seq->tx.seq = now.usec;
+ }
+ if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
+ sizeof(rkh_seq->tx.dom))) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to get random data for sequence number initialization");
+ rkh_seq->tx.dom = now.usec;
+ }
+ rkh_seq->tx.dom |= 1;
+ }
+
+ f_seq->dom = host_to_le32(rkh_seq->tx.dom);
+ f_seq->seq = host_to_le32(rkh_seq->tx.seq);
+ f_seq->ts = host_to_le32(now.sec);
+
+ rkh_seq->tx.seq++;
+
+ return 0;
+}
+
+
struct wpa_ft_pmk_r0_sa {
- struct wpa_ft_pmk_r0_sa *next;
- u8 pmk_r0[PMK_LEN];
+ struct dl_list list;
+ u8 pmk_r0[PMK_LEN_MAX];
+ size_t pmk_r0_len;
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
u8 spa[ETH_ALEN];
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
- /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+ struct vlan_description *vlan;
+ os_time_t expiration; /* 0 for no expiration */
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_cui;
+ size_t radius_cui_len;
+ os_time_t session_timeout; /* 0 for no expiration */
+ /* TODO: radius_class, EAP type */
int pmk_r1_pushed;
};
struct wpa_ft_pmk_r1_sa {
- struct wpa_ft_pmk_r1_sa *next;
- u8 pmk_r1[PMK_LEN];
+ struct dl_list list;
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 spa[ETH_ALEN];
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
- /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+ struct vlan_description *vlan;
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_cui;
+ size_t radius_cui_len;
+ os_time_t session_timeout; /* 0 for no expiration */
+ /* TODO: radius_class, EAP type */
};
struct wpa_ft_pmk_cache {
- struct wpa_ft_pmk_r0_sa *pmk_r0;
- struct wpa_ft_pmk_r1_sa *pmk_r1;
+ struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
+ struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
};
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
+{
+ if (!r0)
+ return;
+
+ dl_list_del(&r0->list);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+
+ os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
+ os_free(r0->vlan);
+ os_free(r0->identity);
+ os_free(r0->radius_cui);
+ os_free(r0);
+}
+
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
+ struct os_reltime now;
+ int expires_in;
+ int session_timeout;
+
+ os_get_reltime(&now);
+
+ if (!r0)
+ return;
+
+ expires_in = r0->expiration - now.sec;
+ session_timeout = r0->session_timeout - now.sec;
+ /* conditions to remove from cache:
+ * a) r0->expiration is set and hit
+ * -or-
+ * b) r0->session_timeout is set and hit
+ */
+ if ((!r0->expiration || expires_in > 0) &&
+ (!r0->session_timeout || session_timeout > 0)) {
+ wpa_printf(MSG_ERROR,
+ "FT: %s() called for non-expired entry %p",
+ __func__, r0);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+ if (r0->expiration && expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+ if (r0->session_timeout && session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+ return;
+ }
+
+ wpa_ft_free_pmk_r0(r0);
+}
+
+
+static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
+{
+ if (!r1)
+ return;
+
+ dl_list_del(&r1->list);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
+
+ os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
+ os_free(r1->vlan);
+ os_free(r1->identity);
+ os_free(r1->radius_cui);
+ os_free(r1);
+}
+
+
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
+
+ wpa_ft_free_pmk_r1(r1);
+}
+
+
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
{
struct wpa_ft_pmk_cache *cache;
cache = os_zalloc(sizeof(*cache));
+ if (cache) {
+ dl_list_init(&cache->pmk_r0);
+ dl_list_init(&cache->pmk_r1);
+ }
return cache;
}
@@ -181,21 +1246,13 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
struct wpa_ft_pmk_r0_sa *r0, *r0prev;
struct wpa_ft_pmk_r1_sa *r1, *r1prev;
- r0 = cache->pmk_r0;
- while (r0) {
- r0prev = r0;
- r0 = r0->next;
- os_memset(r0prev->pmk_r0, 0, PMK_LEN);
- os_free(r0prev);
- }
+ dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
+ struct wpa_ft_pmk_r0_sa, list)
+ wpa_ft_free_pmk_r0(r0);
- r1 = cache->pmk_r1;
- while (r1) {
- r1prev = r1;
- r1 = r1->next;
- os_memset(r1prev->pmk_r1, 0, PMK_LEN);
- os_free(r1prev);
- }
+ dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
+ struct wpa_ft_pmk_r1_sa, list)
+ wpa_ft_free_pmk_r1(r1);
os_free(cache);
}
@@ -203,24 +1260,63 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r0,
- const u8 *pmk_r0_name, int pairwise)
+ size_t pmk_r0_len,
+ const u8 *pmk_r0_name, int pairwise,
+ const struct vlan_description *vlan,
+ int expires_in, int session_timeout,
+ const u8 *identity, size_t identity_len,
+ const u8 *radius_cui, size_t radius_cui_len)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0;
+ struct os_reltime now;
- /* TODO: add expiration and limit on number of entries in cache */
+ /* TODO: add limit on number of entries in cache */
+ os_get_reltime(&now);
r0 = os_zalloc(sizeof(*r0));
if (r0 == NULL)
return -1;
- os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN);
+ os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
+ r0->pmk_r0_len = pmk_r0_len;
os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
os_memcpy(r0->spa, spa, ETH_ALEN);
r0->pairwise = pairwise;
+ if (expires_in > 0)
+ r0->expiration = now.sec + expires_in;
+ if (vlan && vlan->notempty) {
+ r0->vlan = os_zalloc(sizeof(*vlan));
+ if (!r0->vlan) {
+ bin_clear_free(r0, sizeof(*r0));
+ return -1;
+ }
+ *r0->vlan = *vlan;
+ }
+ if (identity) {
+ r0->identity = os_malloc(identity_len);
+ if (r0->identity) {
+ os_memcpy(r0->identity, identity, identity_len);
+ r0->identity_len = identity_len;
+ }
+ }
+ if (radius_cui) {
+ r0->radius_cui = os_malloc(radius_cui_len);
+ if (r0->radius_cui) {
+ os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
+ r0->radius_cui_len = radius_cui_len;
+ }
+ }
+ if (session_timeout > 0)
+ r0->session_timeout = now.sec + session_timeout;
- r0->next = cache->pmk_r0;
- cache->pmk_r0 = r0;
+ dl_list_add(&cache->pmk_r0, &r0->list);
+ if (expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
+ r0, NULL);
+ if (session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
return 0;
}
@@ -228,49 +1324,89 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r0_name,
- u8 *pmk_r0, int *pairwise)
+ const struct wpa_ft_pmk_r0_sa **r0_out)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0;
+ struct os_reltime now;
- r0 = cache->pmk_r0;
- while (r0) {
+ os_get_reltime(&now);
+ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
WPA_PMK_NAME_LEN) == 0) {
- os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
- if (pairwise)
- *pairwise = r0->pairwise;
+ *r0_out = r0;
return 0;
}
-
- r0 = r0->next;
}
+ *r0_out = NULL;
return -1;
}
static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r1,
- const u8 *pmk_r1_name, int pairwise)
+ size_t pmk_r1_len,
+ const u8 *pmk_r1_name, int pairwise,
+ const struct vlan_description *vlan,
+ int expires_in, int session_timeout,
+ const u8 *identity, size_t identity_len,
+ const u8 *radius_cui, size_t radius_cui_len)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
struct wpa_ft_pmk_r1_sa *r1;
+ struct os_reltime now;
- /* TODO: add expiration and limit on number of entries in cache */
+ /* TODO: limit on number of entries in cache */
+ os_get_reltime(&now);
+
+ if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
+ expires_in = max_expires_in;
r1 = os_zalloc(sizeof(*r1));
if (r1 == NULL)
return -1;
- os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN);
+ os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
+ r1->pmk_r1_len = pmk_r1_len;
os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
os_memcpy(r1->spa, spa, ETH_ALEN);
r1->pairwise = pairwise;
+ if (vlan && vlan->notempty) {
+ r1->vlan = os_zalloc(sizeof(*vlan));
+ if (!r1->vlan) {
+ bin_clear_free(r1, sizeof(*r1));
+ return -1;
+ }
+ *r1->vlan = *vlan;
+ }
+ if (identity) {
+ r1->identity = os_malloc(identity_len);
+ if (r1->identity) {
+ os_memcpy(r1->identity, identity, identity_len);
+ r1->identity_len = identity_len;
+ }
+ }
+ if (radius_cui) {
+ r1->radius_cui = os_malloc(radius_cui_len);
+ if (r1->radius_cui) {
+ os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
+ r1->radius_cui_len = radius_cui_len;
+ }
+ }
+ if (session_timeout > 0)
+ r1->session_timeout = now.sec + session_timeout;
+
+ dl_list_add(&cache->pmk_r1, &r1->list);
- r1->next = cache->pmk_r1;
- cache->pmk_r1 = r1;
+ if (expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
+ r1, NULL);
+ if (session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r1, r1, NULL);
return 0;
}
@@ -278,94 +1414,616 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r1_name,
- u8 *pmk_r1, int *pairwise)
+ u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui, size_t *radius_cui_len,
+ int *session_timeout)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r1_sa *r1;
+ struct os_reltime now;
- r1 = cache->pmk_r1;
- while (r1) {
+ os_get_reltime(&now);
+
+ dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
WPA_PMK_NAME_LEN) == 0) {
- os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
+ os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
+ *pmk_r1_len = r1->pmk_r1_len;
if (pairwise)
*pairwise = r1->pairwise;
+ if (vlan && r1->vlan)
+ *vlan = *r1->vlan;
+ if (vlan && !r1->vlan)
+ os_memset(vlan, 0, sizeof(*vlan));
+ if (identity && identity_len) {
+ *identity = r1->identity;
+ *identity_len = r1->identity_len;
+ }
+ if (radius_cui && radius_cui_len) {
+ *radius_cui = r1->radius_cui;
+ *radius_cui_len = r1->radius_cui_len;
+ }
+ if (session_timeout && r1->session_timeout > now.sec)
+ *session_timeout = r1->session_timeout -
+ now.sec;
+ else if (session_timeout && r1->session_timeout)
+ *session_timeout = 1;
+ else if (session_timeout)
+ *session_timeout = 0;
return 0;
}
-
- r1 = r1->next;
}
return -1;
}
-static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
- const u8 *ies, size_t ies_len,
- const u8 *pmk_r0_name)
+static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
+{
+ if (r0kh->seq)
+ return 0;
+
+ r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
+ if (!r0kh->seq) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
+ return -1;
+ }
+
+ dl_list_init(&r0kh->seq->rx.queue);
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+ struct ft_remote_r0kh **r0kh_out,
+ struct ft_remote_r0kh **r0kh_wildcard)
{
struct ft_remote_r0kh *r0kh;
- struct ft_r0kh_r1kh_pull_frame frame, f;
- r0kh = sm->wpa_auth->conf.r0kh_list;
- while (r0kh) {
- if (r0kh->id_len == sm->r0kh_id_len &&
- os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
- 0)
+ *r0kh_wildcard = NULL;
+ *r0kh_out = NULL;
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ for (; r0kh; r0kh = r0kh->next) {
+ if (r0kh->id_len == 1 && r0kh->id[0] == '*')
+ *r0kh_wildcard = r0kh;
+ if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
+ os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
+ *r0kh_out = r0kh;
+ }
+
+ if (!*r0kh_out && !*r0kh_wildcard)
+ wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
+
+ if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
+ *r0kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
+{
+ if (r1kh->seq)
+ return 0;
+
+ r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
+ if (!r1kh->seq) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
+ return -1;
+ }
+
+ dl_list_init(&r1kh->seq->rx.queue);
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r1kh_id,
+ struct ft_remote_r1kh **r1kh_out,
+ struct ft_remote_r1kh **r1kh_wildcard)
+{
+ struct ft_remote_r1kh *r1kh;
+
+ *r1kh_wildcard = NULL;
+ *r1kh_out = NULL;
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ for (; r1kh; r1kh = r1kh->next) {
+ if (is_zero_ether_addr(r1kh->addr) &&
+ is_zero_ether_addr(r1kh->id))
+ *r1kh_wildcard = r1kh;
+ if (f_r1kh_id &&
+ os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
+ *r1kh_out = r1kh;
+ }
+
+ if (!*r1kh_out && !*r1kh_wildcard)
+ wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
+
+ if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
+ *r1kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+ if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
+ os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
+ f_r0kh_id_len) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r1kh_id)
+{
+ if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct ft_remote_r0kh *r0kh, *prev = NULL;
+
+ if (!wpa_auth->conf.r0kh_list)
+ return;
+
+ for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
+ if (r0kh == timeout_ctx)
+ break;
+ prev = r0kh;
+ }
+ if (!r0kh)
+ return;
+ if (prev)
+ prev->next = r0kh->next;
+ else
+ *wpa_auth->conf.r0kh_list = r0kh->next;
+ if (r0kh->seq)
+ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+ os_free(r0kh->seq);
+ os_free(r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh, int timeout)
+{
+ if (timeout > 0)
+ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh, int timeout)
+{
+ eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+}
+
+
+static struct ft_remote_r0kh *
+wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh_wildcard,
+ const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
+ int timeout)
+{
+ struct ft_remote_r0kh *r0kh;
+
+ if (!wpa_auth->conf.r0kh_list)
+ return NULL;
+
+ r0kh = os_zalloc(sizeof(*r0kh));
+ if (!r0kh)
+ return NULL;
+
+ if (src_addr)
+ os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
+
+ if (id_len > FT_R0KH_ID_MAX_LEN)
+ id_len = FT_R0KH_ID_MAX_LEN;
+ os_memcpy(r0kh->id, r0kh_id, id_len);
+ r0kh->id_len = id_len;
+
+ os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
+
+ r0kh->next = *wpa_auth->conf.r0kh_list;
+ *wpa_auth->conf.r0kh_list = r0kh;
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+
+ if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
+ return NULL;
+
+ return r0kh;
+}
+
+
+static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct ft_remote_r1kh *r1kh, *prev = NULL;
+
+ if (!wpa_auth->conf.r1kh_list)
+ return;
+
+ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+ if (r1kh == timeout_ctx)
break;
- r0kh = r0kh->next;
+ prev = r1kh;
+ }
+ if (!r1kh)
+ return;
+ if (prev)
+ prev->next = r1kh->next;
+ else
+ *wpa_auth->conf.r1kh_list = r1kh->next;
+ if (r1kh->seq)
+ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+ os_free(r1kh->seq);
+ os_free(r1kh);
+}
+
+
+static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r1kh *r1kh, int timeout)
+{
+ if (timeout > 0)
+ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+ wpa_auth, r1kh);
+}
+
+
+static struct ft_remote_r1kh *
+wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r1kh *r1kh_wildcard,
+ const u8 *src_addr, const u8 *r1kh_id, int timeout)
+{
+ struct ft_remote_r1kh *r1kh;
+
+ if (!wpa_auth->conf.r1kh_list)
+ return NULL;
+
+ r1kh = os_zalloc(sizeof(*r1kh));
+ if (!r1kh)
+ return NULL;
+
+ os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
+ os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
+ os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
+ r1kh->next = *wpa_auth->conf.r1kh_list;
+ *wpa_auth->conf.r1kh_list = r1kh;
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+ wpa_auth, r1kh);
+
+ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+ return NULL;
+
+ return r1kh;
+}
+
+
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
+{
+ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+}
+
+
+static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
+{
+ struct ft_remote_r0kh *r0kh;
+ struct ft_remote_r1kh *r1kh;
+
+ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ for (; r0kh; r0kh = r0kh->next) {
+ if (!r0kh->seq)
+ continue;
+ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+ os_free(r0kh->seq);
+ r0kh->seq = NULL;
+ }
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ for (; r1kh; r1kh = r1kh->next) {
+ if (!r1kh->seq)
+ continue;
+ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+ os_free(r1kh->seq);
+ r1kh->seq = NULL;
+ }
+}
+
+
+static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
+ struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ while (r0kh) {
+ r0kh_next = r0kh->next;
+ if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
+ r0kh) > 0) {
+ if (r0kh_prev)
+ r0kh_prev->next = r0kh_next;
+ else
+ *wpa_auth->conf.r0kh_list = r0kh_next;
+ os_free(r0kh);
+ } else {
+ r0kh_prev = r0kh;
+ }
+ r0kh = r0kh_next;
+ }
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ while (r1kh) {
+ r1kh_next = r1kh->next;
+ if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
+ r1kh) > 0) {
+ if (r1kh_prev)
+ r1kh_prev->next = r1kh_next;
+ else
+ *wpa_auth->conf.r1kh_list = r1kh_next;
+ os_free(r1kh);
+ } else {
+ r1kh_prev = r1kh;
+ }
+ r1kh = r1kh_next;
+ }
+}
+
+
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
+{
+ wpa_ft_deinit_seq(wpa_auth);
+ wpa_ft_deinit_rkh_tmp(wpa_auth);
+}
+
+
+static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+
+ if (!wpa_auth->conf.rkh_neg_timeout)
+ return;
+
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+
+ if (!r0kh_wildcard) {
+ /* r0kh removed after neg_timeout and might need re-adding */
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID",
+ f_r0kh_id, f_r0kh_id_len);
+
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_neg_timeout);
+ os_memset(r0kh->addr, 0, ETH_ALEN);
+ } else
+ wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
+ f_r0kh_id_len,
+ wpa_auth->conf.rkh_neg_timeout);
+}
+
+
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_state_machine *sm = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
+ MAC2STR(sm->addr));
+ if (sm->ft_pending_pull_left_retries <= 0)
+ wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
+
+ /* cancel multiple timeouts */
+ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+ ft_finish_pull(sm);
+}
+
+
+static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *pmk_r0_name)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+ u8 *packet = NULL;
+ const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
+ size_t packet_len, key_len;
+ struct ft_rrb_seq f_seq;
+ int tsecs, tusecs, first;
+ struct wpabuf *ft_pending_req_ies;
+ int r0kh_timeout;
+ struct tlv_list req_enc[] = {
+ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+ .data = pmk_r0_name },
+ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+ .data = sm->addr },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+ struct tlv_list req_auth[] = {
+ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+ .data = sm->ft_pending_pull_nonce },
+ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+ .data = (u8 *) &f_seq },
+ { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
+ .data = sm->r0kh_id },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = f_r1kh_id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (sm->ft_pending_pull_left_retries <= 0)
+ return -1;
+ first = sm->ft_pending_pull_left_retries ==
+ sm->wpa_auth->conf.rkh_pull_retries;
+ sm->ft_pending_pull_left_retries--;
+
+ wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+
+ /* Keep r0kh sufficiently long in the list for seq num check */
+ r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
+ 1 + ftRRBseqTimeout;
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
+ } else if (r0kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+ /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
+ r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
+ r0kh_wildcard->addr,
+ sm->r0kh_id, sm->r0kh_id_len,
+ r0kh_timeout);
}
if (r0kh == NULL) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
+ if (is_zero_ether_addr(r0kh->addr)) {
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+ if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: R0KH-ID points to self - no matching key available");
+ return -1;
+ }
+
+ key = r0kh->key;
+ key_len = sizeof(r0kh->key);
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
"address " MACSTR, MAC2STR(r0kh->addr));
- os_memset(&frame, 0, sizeof(frame));
- frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
- frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
- frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
- os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
+ if (r0kh->seq->rx.num_last == 0) {
+ /* A sequence request will be sent out anyway when pull
+ * response is received. Send it out now to avoid one RTT. */
+ wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
+ r0kh->id, r0kh->id_len, f_r1kh_id, key,
+ key_len, NULL, 0, NULL, 0, NULL);
+ }
- /* aes_wrap() does not support inplace encryption, so use a temporary
- * buffer for the data. */
- if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
+ if (first &&
+ random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce");
return -1;
}
- os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
- FT_R0KH_R1KH_PULL_NONCE_LEN);
- os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
- os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
- os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
- os_memset(f.pad, 0, sizeof(f.pad));
- if (aes_wrap(r0kh->key, sizeof(r0kh->key),
- (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
- f.nonce, frame.nonce) < 0)
+ if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
+ }
+
+ if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
+ sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
+ &packet, &packet_len) < 0)
return -1;
+ ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
wpabuf_free(sm->ft_pending_req_ies);
- sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
- if (sm->ft_pending_req_ies == NULL)
+ sm->ft_pending_req_ies = ft_pending_req_ies;
+ if (!sm->ft_pending_req_ies) {
+ os_free(packet);
return -1;
+ }
+
+ tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
+ tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
+ eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
+
+ wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
+ packet, packet_len);
- wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+ os_free(packet);
return 0;
}
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
+ const u8 *pmk_r0, const u8 *pmk_r0_name)
+{
+ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len, radius_cui_len;
+ int session_timeout;
+ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
+ SHA384_MAC_LEN : PMK_LEN;
+
+ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+ MAC2STR(sm->addr));
+ return -1;
+ }
+
+ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+ &radius_cui);
+ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+ return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+ pmk_r0_name, sm->pairwise, &vlan, expires_in,
+ session_timeout, identity, identity_len,
+ radius_cui, radius_cui_len);
+}
+
+
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
struct wpa_ptk *ptk)
{
- u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 pmk_r1[PMK_LEN];
+ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
+ SHA384_MAC_LEN : PMK_LEN;
+ size_t pmk_r1_len = pmk_r0_len;
+ u8 pmk_r1[PMK_LEN_MAX];
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
@@ -373,6 +2031,12 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
const u8 *ssid = sm->wpa_auth->conf.ssid;
size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+ int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
+ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len, radius_cui_len;
+ int session_timeout;
if (sm->xxkey_len == 0) {
wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -380,23 +2044,45 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
return -1;
}
- wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
- r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
- sm->pairwise);
+ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+ MAC2STR(sm->addr));
+ return -1;
+ }
- wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
- pmk_r1, sm->pmk_r1_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+ &radius_cui);
+ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+ if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+ r0kh, r0kh_len, sm->addr,
+ pmk_r0, pmk_r0_name,
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+ if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+ wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+ pmk_r0_name,
+ sm->pairwise, &vlan, expires_in,
+ session_timeout, identity, identity_len,
+ radius_cui, radius_cui_len);
+
+ if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
+ pmk_r1, sm->pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
WPA_PMK_NAME_LEN);
- wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
- sm->pairwise);
-
- return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
- sm->wpa_auth->addr, sm->pmk_r1_name,
+ if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+ wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
+ sm->pmk_r1_name, sm->pairwise, &vlan,
+ expires_in, session_timeout, identity,
+ identity_len, radius_cui, radius_cui_len);
+
+ return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
}
@@ -404,9 +2090,9 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
const u8 *addr, int idx, u8 *seq)
{
- if (wpa_auth->cb.get_seqnum == NULL)
+ if (wpa_auth->cb->get_seqnum == NULL)
return -1;
- return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
}
@@ -418,6 +2104,16 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
const u8 *key;
size_t key_len;
u8 keybuf[32];
+ const u8 *kek;
+ size_t kek_len;
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
key_len = gsm->GTK_len;
if (key_len > sizeof(keybuf))
@@ -456,8 +2152,10 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
subelem[4] = gsm->GTK_len;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
- subelem + 13)) {
+ if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: GTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
os_free(subelem);
return NULL;
}
@@ -473,10 +2171,23 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
u8 *subelem, *pos;
struct wpa_group *gsm = sm->group;
size_t subelem_len;
+ const u8 *kek;
+ size_t kek_len;
+ size_t igtk_len;
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
+
+ igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
* Key[16+8] */
- subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
+ subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
subelem = os_zalloc(subelem_len);
if (subelem == NULL)
return NULL;
@@ -488,9 +2199,12 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
pos += 2;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
pos += 6;
- *pos++ = WPA_IGTK_LEN;
- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
+ *pos++ = igtk_len;
+ if (aes_wrap(kek, kek_len, igtk_len / 8,
gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: IGTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
os_free(subelem);
return NULL;
}
@@ -637,17 +2351,21 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
const u8 *req_ies, size_t req_ies_len)
{
u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+ u8 *fte_mic, *elem_count;
size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
int res;
struct wpa_auth_config *conf;
- struct rsn_ftie *_ftie;
struct wpa_ft_ies parse;
u8 *ric_start;
u8 *anonce, *snonce;
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384;
if (sm == NULL)
return pos;
+ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
conf = &sm->wpa_auth->conf;
if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
@@ -662,7 +2380,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
*/
res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
if (res < 0)
- return pos;
+ return NULL;
rsnie = pos;
rsnie_len = res;
pos += res;
@@ -671,7 +2389,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
/* Mobility Domain Information */
res = wpa_write_mdie(conf, pos, end - pos);
if (res < 0)
- return pos;
+ return NULL;
mdie = pos;
mdie_len = res;
pos += res;
@@ -679,6 +2397,11 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
/* Fast BSS Transition Information */
if (auth_alg == WLAN_AUTH_FT) {
subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+ if (!subelem) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add GTK subelement");
+ return NULL;
+ }
r0kh_id = sm->r0kh_id;
r0kh_id_len = sm->r0kh_id_len;
anonce = sm->ANonce;
@@ -690,14 +2413,16 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
u8 *nbuf;
igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
if (igtk == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add IGTK subelement");
os_free(subelem);
- return pos;
+ return NULL;
}
nbuf = os_realloc(subelem, subelem_len + igtk_len);
if (nbuf == NULL) {
os_free(subelem);
os_free(igtk);
- return pos;
+ return NULL;
}
subelem = nbuf;
os_memcpy(subelem + subelem_len, igtk, igtk_len);
@@ -711,44 +2436,66 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
anonce = NULL;
snonce = NULL;
}
- res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
- end - pos, subelem, subelem_len);
+ res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
+ anonce, snonce, pos, end - pos,
+ subelem, subelem_len);
os_free(subelem);
if (res < 0)
- return pos;
+ return NULL;
ftie = pos;
ftie_len = res;
pos += res;
- _ftie = (struct rsn_ftie *) (ftie + 2);
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *_ftie =
+ (struct rsn_ftie_sha384 *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ } else {
+ struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ }
if (auth_alg == WLAN_AUTH_FT)
- _ftie->mic_control[1] = 3; /* Information element count */
+ *elem_count = 3; /* Information element count */
ric_start = pos;
- if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
+ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
+ && parse.ric) {
pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
parse.ric_len);
if (auth_alg == WLAN_AUTH_FT)
- _ftie->mic_control[1] +=
+ *elem_count +=
ieee802_11_ie_count(ric_start,
pos - ric_start);
}
if (ric_start == pos)
ric_start = NULL;
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kck = sm->PTK.kck2;
+ kck_len = sm->PTK.kck2_len;
+ } else {
+ kck = sm->PTK.kck;
+ kck_len = sm->PTK.kck_len;
+ }
if (auth_alg == WLAN_AUTH_FT &&
- wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
- sm->wpa_auth->addr, 6,
+ wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
mdie, mdie_len, ftie, ftie_len,
rsnie, rsnie_len,
ric_start, ric_start ? pos - ric_start : 0,
- _ftie->mic) < 0)
+ fte_mic) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return NULL;
+ }
os_free(sm->assoc_resp_ftie);
sm->assoc_resp_ftie = os_malloc(ftie_len);
- if (sm->assoc_resp_ftie)
- os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+ if (!sm->assoc_resp_ftie)
+ return NULL;
+ os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
return pos;
}
@@ -759,10 +2506,10 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
enum wpa_alg alg, const u8 *addr, int idx,
u8 *key, size_t key_len)
{
- if (wpa_auth->cb.set_key == NULL)
+ if (wpa_auth->cb->set_key == NULL)
return -1;
- return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
- key, key_len);
+ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+ key, key_len);
}
@@ -804,20 +2551,219 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
}
+/* Derive PMK-R1 from PSK, check all available PSK */
+static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *req_pmk_r1_name,
+ u8 *out_pmk_r1, int *out_pairwise,
+ struct vlan_description *out_vlan,
+ const u8 **out_identity, size_t *out_identity_len,
+ const u8 **out_radius_cui,
+ size_t *out_radius_cui_len,
+ int *out_session_timeout)
+{
+ const u8 *pmk = NULL;
+ u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ const u8 *mdid = wpa_auth->conf.mobility_domain;
+ const u8 *r0kh = sm->r0kh_id;
+ size_t r0kh_len = sm->r0kh_id_len;
+ const u8 *r1kh = wpa_auth->conf.r1_key_holder;
+ const u8 *ssid = wpa_auth->conf.ssid;
+ size_t ssid_len = wpa_auth->conf.ssid_len;
+ int pairwise;
+
+ pairwise = sm->pairwise;
+
+ for (;;) {
+ pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+ pmk);
+ if (pmk == NULL)
+ break;
+
+ if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
+ r0kh_len, sm->addr,
+ pmk_r0, pmk_r0_name, 0) < 0 ||
+ wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
+ sm->addr, pmk_r1, pmk_r1_name) < 0 ||
+ os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0)
+ continue;
+
+ /* We found a PSK that matches the requested pmk_r1_name */
+ wpa_printf(MSG_DEBUG,
+ "FT: Found PSK to generate PMK-R1 locally");
+ os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
+ if (out_pairwise)
+ *out_pairwise = pairwise;
+ if (out_vlan &&
+ wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
+ MACSTR, MAC2STR(sm->addr));
+ return -1;
+ }
+
+ if (out_identity && out_identity_len) {
+ *out_identity_len = wpa_ft_get_identity(
+ sm->wpa_auth, sm->addr, out_identity);
+ }
+
+ if (out_radius_cui && out_radius_cui_len) {
+ *out_radius_cui_len = wpa_ft_get_radius_cui(
+ sm->wpa_auth, sm->addr, out_radius_cui);
+ }
+
+ if (out_session_timeout) {
+ *out_session_timeout = wpa_ft_get_session_timeout(
+ sm->wpa_auth, sm->addr);
+ }
+
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "FT: Did not find PSK to generate PMK-R1 locally");
+ return -1;
+}
+
+
+/* Detect the configuration the station asked for.
+ * Required to detect FT-PSK and pairwise cipher.
+ */
+static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
+ struct wpa_ft_ies *parse)
+{
+ int key_mgmt, ciphers;
+
+ if (sm->wpa_key_mgmt)
+ return 0;
+
+ key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
+ if (!key_mgmt) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
+ MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
+ return -1;
+ }
+ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+ else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_FILS
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_FILS */
+ ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
+ if (!ciphers) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
+ MACSTR,
+ parse->pairwise_cipher, MAC2STR(sm->addr));
+ return -1;
+ }
+ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+
+ return 0;
+}
+
+
+static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *req_pmk_r0_name,
+ const u8 *req_pmk_r1_name,
+ u8 *out_pmk_r1, int *out_pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui,
+ size_t *radius_cui_len,
+ int *out_session_timeout)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ const struct wpa_ft_pmk_r0_sa *r0;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ int expires_in = 0;
+ int session_timeout = 0;
+ struct os_reltime now;
+
+ if (conf->r0_key_holder_len != r0kh_id_len ||
+ os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
+ 0)
+ return -1; /* not our R0KH-ID */
+
+ wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
+ if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
+ 0)
+ return -1; /* no matching PMKR0Name in local cache */
+
+ wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
+
+ if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
+ conf->r1_key_holder,
+ sm->addr, out_pmk_r1, pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ os_get_reltime(&now);
+ if (r0->expiration)
+ expires_in = r0->expiration - now.sec;
+
+ if (r0->session_timeout)
+ session_timeout = r0->session_timeout - now.sec;
+
+ wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
+ pmk_r1_name,
+ sm->pairwise, r0->vlan, expires_in, session_timeout,
+ r0->identity, r0->identity_len,
+ r0->radius_cui, r0->radius_cui_len);
+
+ *out_pairwise = sm->pairwise;
+ if (vlan) {
+ if (r0->vlan)
+ *vlan = *r0->vlan;
+ else
+ os_memset(vlan, 0, sizeof(*vlan));
+ }
+
+ if (identity && identity_len) {
+ *identity = r0->identity;
+ *identity_len = r0->identity_len;
+ }
+
+ if (radius_cui && radius_cui_len) {
+ *radius_cui = r0->radius_cui;
+ *radius_cui_len = r0->radius_cui_len;
+ }
+
+ *out_session_timeout = session_timeout;
+
+ return 0;
+}
+
+
static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
u8 **resp_ies, size_t *resp_ies_len)
{
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
- u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
u8 ptk_name[WPA_PMK_NAME_LEN];
struct wpa_auth_config *conf;
struct wpa_ft_ies parse;
size_t buflen;
int ret;
u8 *pos, *end;
- int pairwise;
+ int pairwise, session_timeout = 0;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len = 0, radius_cui_len = 0;
+ int use_sha384;
+ size_t pmk_r1_len;
*resp_ies = NULL;
*resp_ies_len = 0;
@@ -828,10 +2774,12 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
+ use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
+ pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
mdie = (struct rsn_mdie *) parse.mdie;
if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
@@ -842,13 +2790,27 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
return WLAN_STATUS_INVALID_MDIE;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
- }
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
- os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ }
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
@@ -865,26 +2827,58 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
return WLAN_STATUS_INVALID_PMKID;
}
+ if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
parse.rsn_pmkid, WPA_PMK_NAME_LEN);
- wpa_derive_pmk_r1_name(parse.rsn_pmkid,
- sm->wpa_auth->conf.r1_key_holder, sm->addr,
- pmk_r1_name);
+ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder, sm->addr,
+ pmk_r1_name, use_sha384) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
pmk_r1_name, WPA_PMK_NAME_LEN);
- if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
- &pairwise) < 0) {
+ if (conf->ft_psk_generate_local &&
+ wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout) < 0)
+ return WLAN_STATUS_INVALID_PMKID;
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 for FT-PSK locally");
+ } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+ pmk_r1, &pmk_r1_len, &pairwise, &vlan,
+ &identity, &identity_len, &radius_cui,
+ &radius_cui_len, &session_timeout) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
+ if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
+ parse.r0kh_id, parse.r0kh_id_len,
+ parse.rsn_pmkid,
+ pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 based on local PMK-R0");
+ goto pmk_r1_derived;
+ }
+
if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Did not have matching "
- "PMK-R1 and unknown R0KH-ID");
+ wpa_printf(MSG_DEBUG,
+ "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
return WLAN_STATUS_INVALID_PMKID;
}
return -1; /* Status pending */
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
}
- wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
+pmk_r1_derived:
+ wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
sm->pmk_r1_name_valid = 1;
os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
@@ -899,8 +2893,8 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
sm->ANonce, WPA_NONCE_LEN);
- if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
- sm->wpa_auth->addr, pmk_r1_name,
+ if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr, pmk_r1_name,
&sm->PTK, ptk_name, sm->wpa_key_mgmt,
pairwise) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -910,44 +2904,51 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
sm->tk_already_set = FALSE;
wpa_ft_install_ptk(sm);
+ if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
+ identity, identity_len) < 0 ||
+ wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
+ radius_cui, radius_cui_len) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
+
buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
2 + FT_R1KH_ID_LEN + 200;
*resp_ies = os_zalloc(buflen);
- if (*resp_ies == NULL) {
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
+ if (*resp_ies == NULL)
+ goto fail;
pos = *resp_ies;
end = *resp_ies + buflen;
ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
- if (ret < 0) {
- os_free(*resp_ies);
- *resp_ies = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
+ if (ret < 0)
+ goto fail;
pos += ret;
ret = wpa_write_mdie(conf, pos, end - pos);
- if (ret < 0) {
- os_free(*resp_ies);
- *resp_ies = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
+ if (ret < 0)
+ goto fail;
pos += ret;
- ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
+ ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
- if (ret < 0) {
- os_free(*resp_ies);
- *resp_ies = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
+ if (ret < 0)
+ goto fail;
pos += ret;
*resp_ies_len = pos - *resp_ies;
return WLAN_STATUS_SUCCESS;
+fail:
+ os_free(*resp_ies);
+ *resp_ies = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -975,6 +2976,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
sm->ft_pending_cb = cb;
sm->ft_pending_cb_ctx = ctx;
sm->ft_pending_auth_transaction = auth_transaction;
+ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
&resp_ies_len);
if (res < 0) {
@@ -998,17 +3000,23 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
{
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
size_t mic_len = 16;
unsigned int count;
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384;
+ const u8 *anonce, *snonce, *fte_mic;
+ u8 fte_elem_count;
if (sm == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -1039,34 +3047,56 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return WLAN_STATUS_INVALID_MDIE;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
}
- if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- ftie->snonce, WPA_NONCE_LEN);
+ snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->SNonce, WPA_NONCE_LEN);
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
- if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- ftie->anonce, WPA_NONCE_LEN);
+ anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->ANonce, WPA_NONCE_LEN);
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
@@ -1078,12 +3108,12 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
parse.r0kh_id, parse.r0kh_id_len);
wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
@@ -1094,7 +3124,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.rsn_pmkid == NULL ||
@@ -1102,21 +3132,27 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
{
wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
"RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
- return -1;
+ return WLAN_STATUS_INVALID_PMKID;
}
count = 3;
if (parse.ric)
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
- if (ftie->mic_control[1] != count) {
+ if (fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- ftie->mic_control[1], count);
- return -1;
+ fte_elem_count, count);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
- sm->wpa_auth->addr, 5,
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kck = sm->PTK.kck2;
+ kck_len = sm->PTK.kck2_len;
+ } else {
+ kck = sm->PTK.kck;
+ kck_len = sm->PTK.kck_len;
+ }
+ if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
@@ -1126,12 +3162,12 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
+ if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
- ftie->mic, mic_len);
+ fte_mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
parse.mdie - 2, parse.mdie_len + 2);
@@ -1199,6 +3235,11 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
+ if (!sm->wpa_auth->conf.ft_over_ds) {
+ wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject");
+ return -1;
+ }
+
/* RRB - Forward action frame to the target AP */
frame = os_malloc(sizeof(*frame) + len);
if (frame == NULL)
@@ -1251,6 +3292,7 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
sm->ft_pending_cb_ctx = sm;
os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
&resp_ies_len);
if (res < 0) {
@@ -1316,112 +3358,431 @@ static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
}
+static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
+ const struct tlv_list *tlvs,
+ const struct wpa_ft_pmk_r0_sa *pmk_r0,
+ const u8 *r1kh_id, const u8 *s1kh_id,
+ const struct tlv_list *tlv_auth,
+ const u8 *src_addr, u8 type,
+ u8 **packet, size_t *packet_len)
+{
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len = pmk_r0->pmk_r0_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 f_pairwise[sizeof(le16)];
+ u8 f_expires_in[sizeof(le16)];
+ u8 f_session_timeout[sizeof(le32)];
+ int expires_in;
+ int session_timeout;
+ struct os_reltime now;
+ int ret;
+ struct tlv_list sess_tlv[] = {
+ { .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
+ .data = pmk_r1 },
+ { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
+ .data = pmk_r1_name },
+ { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
+ .data = f_pairwise },
+ { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
+ .data = f_expires_in },
+ { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
+ .data = pmk_r0->identity },
+ { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
+ .data = pmk_r0->radius_cui },
+ { .type = FT_RRB_SESSION_TIMEOUT,
+ .len = sizeof(f_session_timeout),
+ .data = f_session_timeout },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
+ pmk_r0->pmk_r0_name, r1kh_id,
+ s1kh_id, pmk_r1, pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)",
+ pmk_r1, pmk_r1_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)",
+ pmk_r1_name, WPA_PMK_NAME_LEN);
+ WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
+
+ os_get_reltime(&now);
+ if (pmk_r0->expiration > now.sec)
+ expires_in = pmk_r0->expiration - now.sec;
+ else if (pmk_r0->expiration)
+ expires_in = 1;
+ else
+ expires_in = 0;
+ WPA_PUT_LE16(f_expires_in, expires_in);
+
+ if (pmk_r0->session_timeout > now.sec)
+ session_timeout = pmk_r0->session_timeout - now.sec;
+ else if (pmk_r0->session_timeout)
+ session_timeout = 1;
+ else
+ session_timeout = 0;
+ WPA_PUT_LE32(f_session_timeout, session_timeout);
+
+ ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
+ pmk_r0->vlan, src_addr, type,
+ packet, packet_len);
+
+ os_memset(pmk_r1, 0, sizeof(pmk_r1));
+
+ return ret;
+}
+
+
static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
- const u8 *data, size_t data_len)
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
{
- struct ft_r0kh_r1kh_pull_frame f;
- const u8 *crypt;
- u8 *plain;
- struct ft_remote_r1kh *r1kh;
- struct ft_r0kh_r1kh_resp_frame resp, r;
- u8 pmk_r0[PMK_LEN];
- int pairwise;
+ const char *msgtype = "pull request";
+ u8 *plain = NULL, *packet = NULL;
+ size_t plain_len = 0, packet_len = 0;
+ struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
+ const u8 *key;
+ size_t key_len;
+ int seq_ret;
+ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
+ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
+ size_t f_pmk_r0_name_len;
+ const struct wpa_ft_pmk_r0_sa *r0;
+ int ret;
+ struct tlv_list resp[2];
+ struct tlv_list resp_auth[5];
+ struct ft_rrb_seq f_seq;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
- if (data_len < sizeof(f))
- return -1;
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
- r1kh = wpa_auth->conf.r1kh_list;
- while (r1kh) {
- if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r1kh = r1kh->next;
+ if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
+ goto out;
}
- if (r1kh == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
- "PMK-R1 pull source address " MACSTR,
- MAC2STR(src_addr));
- return -1;
+
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
+ if (r1kh) {
+ key = r1kh->key;
+ key_len = sizeof(r1kh->key);
+ } else if (r1kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
+ key = r1kh_wildcard->key;
+ key_len = sizeof(r1kh_wildcard->key);
+ } else {
+ goto out;
}
- crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
- os_memset(&f, 0, sizeof(f));
- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
- /* aes_unwrap() does not support inplace decryption, so use a temporary
- * buffer for the data. */
- if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
- (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
- crypt, plain) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
- "request from " MACSTR, MAC2STR(src_addr));
- return -1;
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
+
+ seq_ret = FT_RRB_SEQ_DROP;
+ if (r1kh)
+ seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
+ auth, auth_len, msgtype, no_defer);
+ if (!no_defer && r1kh_wildcard &&
+ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ /* wildcard: r1kh-id unknown or changed addr -> do a seq req */
+ seq_ret = FT_RRB_SEQ_DEFER;
}
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
- f.nonce, sizeof(f.nonce));
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
- f.pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
- MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
-
- os_memset(&resp, 0, sizeof(resp));
- resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
- resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
- resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
- os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
-
- /* aes_wrap() does not support inplace encryption, so use a temporary
- * buffer for the data. */
- os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
- os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
- os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
- if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
- &pairwise) < 0) {
- wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
- "PMK-R1 pull");
- return -1;
+ if (seq_ret == FT_RRB_SEQ_DROP)
+ goto out;
+
+ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+ src_addr, FT_PACKET_R0KH_R1KH_PULL,
+ &plain, &plain_len) < 0)
+ goto out;
+
+ if (!r1kh)
+ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
+ f_r1kh_id,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (!r1kh)
+ goto out;
+
+ if (seq_ret == FT_RRB_SEQ_DEFER) {
+ wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
+ f_r0kh_id_len, f_r1kh_id, key, key_len,
+ enc, enc_len, auth, auth_len,
+ &wpa_ft_rrb_rx_pull);
+ goto out;
}
- wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
- r.pmk_r1, r.pmk_r1_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
- WPA_PMK_NAME_LEN);
- r.pairwise = host_to_le16(pairwise);
- os_memset(r.pad, 0, sizeof(r.pad));
+ wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
+ msgtype);
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+ wpa_auth->conf.rkh_pos_timeout);
- if (aes_wrap(r1kh->key, sizeof(r1kh->key),
- (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
- r.nonce, resp.nonce) < 0) {
- os_memset(pmk_r0, 0, PMK_LEN);
- return -1;
+ RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
+ f_pmk_r0_name_len);
+
+ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ goto out;
}
- os_memset(pmk_r0, 0, PMK_LEN);
+ resp[0].type = FT_RRB_S1KH_ID;
+ resp[0].len = f_s1kh_id_len;
+ resp[0].data = f_s1kh_id;
+ resp[1].type = FT_RRB_LAST_EMPTY;
+ resp[1].len = 0;
+ resp[1].data = NULL;
+
+ resp_auth[0].type = FT_RRB_NONCE;
+ resp_auth[0].len = f_nonce_len;
+ resp_auth[0].data = f_nonce;
+ resp_auth[1].type = FT_RRB_SEQ;
+ resp_auth[1].len = sizeof(f_seq);
+ resp_auth[1].data = (u8 *) &f_seq;
+ resp_auth[2].type = FT_RRB_R0KH_ID;
+ resp_auth[2].len = f_r0kh_id_len;
+ resp_auth[2].data = f_r0kh_id;
+ resp_auth[3].type = FT_RRB_R1KH_ID;
+ resp_auth[3].len = f_r1kh_id_len;
+ resp_auth[3].data = f_r1kh_id;
+ resp_auth[4].type = FT_RRB_LAST_EMPTY;
+ resp_auth[4].len = 0;
+ resp_auth[4].data = NULL;
+
+ if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
+ ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
+ NULL, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_RESP,
+ &packet, &packet_len);
+ } else {
+ ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
+ f_s1kh_id, resp_auth, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_RESP,
+ &packet, &packet_len);
+ }
- wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
+ if (!ret)
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+ FT_PACKET_R0KH_R1KH_RESP, packet,
+ packet_len);
+
+out:
+ os_free(plain);
+ os_free(packet);
return 0;
}
-static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+/* @returns 0 on success
+ * -1 on error
+ * -2 if FR_RRB_PAIRWISE is missing
+ */
+static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr, u8 type,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype, u8 *s1kh_id_out,
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer))
+{
+ u8 *plain = NULL;
+ size_t plain_len = 0;
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+ const u8 *key;
+ size_t key_len;
+ int seq_ret;
+ const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
+ const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+ const u8 *f_expires_in;
+ size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+ const u8 *f_identity, *f_radius_cui;
+ const u8 *f_session_timeout;
+ size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+ size_t f_expires_in_len;
+ size_t f_identity_len, f_radius_cui_len;
+ size_t f_session_timeout_len;
+ int pairwise;
+ int ret = -1;
+ int expires_in;
+ int session_timeout;
+ struct vlan_description vlan;
+ size_t pmk_r1_len;
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+ if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
+ goto out;
+ }
+
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
+ &r0kh_wildcard);
+ if (r0kh) {
+ key = r0kh->key;
+ key_len = sizeof(r0kh->key);
+ } else if (r0kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+ key = r0kh_wildcard->key;
+ key_len = sizeof(r0kh_wildcard->key);
+ } else {
+ goto out;
+ }
+
+ seq_ret = FT_RRB_SEQ_DROP;
+ if (r0kh) {
+ seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
+ auth, auth_len, msgtype,
+ cb ? 0 : 1);
+ }
+ if (cb && r0kh_wildcard &&
+ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ /* wildcard: r0kh-id unknown or changed addr -> do a seq req */
+ seq_ret = FT_RRB_SEQ_DEFER;
+ }
+
+ if (seq_ret == FT_RRB_SEQ_DROP)
+ goto out;
+
+ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+ src_addr, type, &plain, &plain_len) < 0)
+ goto out;
+
+ if (!r0kh)
+ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
+ f_r0kh_id, f_r0kh_id_len,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (!r0kh)
+ goto out;
+
+ if (seq_ret == FT_RRB_SEQ_DEFER) {
+ wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
+ f_r0kh_id_len, f_r1kh_id, key, key_len,
+ enc, enc_len, auth, auth_len, cb);
+ goto out;
+ }
+
+ wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
+ msgtype);
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_pos_timeout);
+
+ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+ if (s1kh_id_out)
+ os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
+
+ ret = -2;
+ RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
+ wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
+
+ ret = -1;
+ RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+ f_pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ pmk_r1_len = PMK_LEN;
+ if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
+ &f_pmk_r1) == 0 &&
+ (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN))
+ pmk_r1_len = f_pmk_r1_len;
+ RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
+
+ pairwise = WPA_GET_LE16(f_pairwise);
+
+ RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
+ sizeof(le16));
+ if (f_expires_in)
+ expires_in = WPA_GET_LE16(f_expires_in);
+ else
+ expires_in = 0;
+
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
+ expires_in);
+
+ if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
+ wpa_ft_rrb_dump(plain, plain_len);
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
+ le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
+
+ RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
+ if (f_identity)
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
+ f_identity_len);
+
+ RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
+ if (f_radius_cui)
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
+ f_radius_cui_len);
+
+ RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
+ sizeof(le32));
+ if (f_session_timeout)
+ session_timeout = WPA_GET_LE32(f_session_timeout);
+ else
+ session_timeout = 0;
+ wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
+
+ if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
+ f_pmk_r1_name,
+ pairwise, &vlan, expires_in, session_timeout,
+ f_identity, f_identity_len, f_radius_cui,
+ f_radius_cui_len) < 0)
+ goto out;
+
+ ret = 0;
+out:
+ if (plain) {
+ os_memset(plain, 0, plain_len);
+ os_free(plain);
+ }
+
+ return ret;
+
+}
+
+
+static void ft_finish_pull(struct wpa_state_machine *sm)
{
- struct wpa_state_machine *sm = eloop_ctx;
int res;
u8 *resp_ies;
size_t resp_ies_len;
u16 status;
+ if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
+ return;
+
res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
wpabuf_len(sm->ft_pending_req_ies),
&resp_ies, &resp_ies_len);
+ if (res < 0) {
+ /* this loop is broken by ft_pending_pull_left_retries */
+ wpa_printf(MSG_DEBUG,
+ "FT: Callback postponed until response is available");
+ return;
+ }
wpabuf_free(sm->ft_pending_req_ies);
sm->ft_pending_req_ies = NULL;
- if (res < 0)
- res = WLAN_STATUS_UNSPECIFIED_FAILURE;
status = res;
wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
" - status %u", MAC2STR(sm->addr), status);
@@ -1433,171 +3794,383 @@ static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
}
-static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
+struct ft_get_sta_ctx {
+ const u8 *nonce;
+ const u8 *s1kh_id;
+ struct wpa_state_machine *sm;
+};
+
+
+static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
{
- struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+ struct ft_get_sta_ctx *info = ctx;
- if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
- return 0;
- if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
- FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
- return 0;
- if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+ if ((info->s1kh_id &&
+ os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+ os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
+ FT_RRB_NONCE_LEN) != 0 ||
+ sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
return 0;
- wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
- MACSTR " - process from timeout", MAC2STR(sm->addr));
- eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+ info->sm = sm;
+
return 1;
}
static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
- const u8 *data, size_t data_len)
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
{
- struct ft_r0kh_r1kh_resp_frame f;
- const u8 *crypt;
- u8 *plain;
- struct ft_remote_r0kh *r0kh;
- int pairwise, res;
+ const char *msgtype = "pull response";
+ int nak, ret = -1;
+ struct ft_get_sta_ctx ctx;
+ u8 s1kh_id[ETH_ALEN];
+ const u8 *f_nonce;
+ size_t f_nonce_len;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
- if (data_len < sizeof(f))
- return -1;
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
- r0kh = wpa_auth->conf.r0kh_list;
- while (r0kh) {
- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r0kh = r0kh->next;
- }
- if (r0kh == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
- "PMK-R0 pull response source address " MACSTR,
- MAC2STR(src_addr));
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.nonce = f_nonce;
+ if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+ /* nonce not found */
+ wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
return -1;
}
- crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
- os_memset(&f, 0, sizeof(f));
- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
- /* aes_unwrap() does not support inplace decryption, so use a temporary
- * buffer for the data. */
- if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
- (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
- crypt, plain) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
- "response from " MACSTR, MAC2STR(src_addr));
- return -1;
+ ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
+ enc, enc_len, auth, auth_len, msgtype, s1kh_id,
+ no_defer ? NULL : &wpa_ft_rrb_rx_resp);
+ if (ret == -2) {
+ ret = 0;
+ nak = 1;
+ } else {
+ nak = 0;
}
-
- if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
- FT_R1KH_ID_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
- "matching R1KH-ID");
+ if (ret < 0)
return -1;
- }
- pairwise = le_to_host16(f.pairwise);
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
- f.nonce, sizeof(f.nonce));
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
- MACSTR " pairwise=0x%x",
- MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
- f.pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
- f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
- res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
- pairwise);
- wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
- wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
- os_memset(f.pmk_r1, 0, PMK_LEN);
+ ctx.s1kh_id = s1kh_id;
+ if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Response to a pending pull request for " MACSTR,
+ MAC2STR(ctx.sm->addr));
+ eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
+ if (nak)
+ ctx.sm->ft_pending_pull_left_retries = 0;
+ ft_finish_pull(ctx.sm);
+ }
- return res ? 0 : -1;
+out:
+ return ret;
}
static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
- const u8 *data, size_t data_len)
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len, int no_defer)
{
- struct ft_r0kh_r1kh_push_frame f;
- const u8 *crypt;
- u8 *plain;
- struct ft_remote_r0kh *r0kh;
- struct os_time now;
- os_time_t tsend;
- int pairwise;
+ const char *msgtype = "push";
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
- if (data_len < sizeof(f))
+ if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
+ enc, enc_len, auth, auth_len, msgtype, NULL,
+ no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
return -1;
- r0kh = wpa_auth->conf.r0kh_list;
- while (r0kh) {
- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r0kh = r0kh->next;
+ return 0;
+}
+
+
+static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr, int type,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ struct ft_remote_seq **rkh_seq,
+ u8 **key, size_t *key_len,
+ struct ft_remote_r0kh **r0kh_out,
+ struct ft_remote_r1kh **r1kh_out,
+ struct ft_remote_r0kh **r0kh_wildcard_out,
+ struct ft_remote_r1kh **r1kh_wildcard_out)
+{
+ struct ft_remote_r0kh *r0kh = NULL;
+ struct ft_remote_r1kh *r1kh = NULL;
+ const u8 *f_r0kh_id, *f_r1kh_id;
+ size_t f_r0kh_id_len, f_r1kh_id_len;
+ int to_r0kh, to_r1kh;
+ u8 *plain = NULL;
+ size_t plain_len = 0;
+ struct ft_remote_r0kh *r0kh_wildcard;
+ struct ft_remote_r1kh *r1kh_wildcard;
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+ to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
+ to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
+
+ if (to_r0kh && to_r1kh) {
+ wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
+ goto out;
}
- if (r0kh == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
- "PMK-R0 push source address " MACSTR,
- MAC2STR(src_addr));
- return -1;
+
+ if (!to_r0kh && !to_r1kh) {
+ wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
+ goto out;
}
- crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
- os_memset(&f, 0, sizeof(f));
- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
- timestamp);
- /* aes_unwrap() does not support inplace decryption, so use a temporary
- * buffer for the data. */
- if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
- (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
- crypt, plain) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
- MACSTR, MAC2STR(src_addr));
- return -1;
+ if (!to_r0kh) {
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+ if (!r0kh_wildcard &&
+ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+ f_r0kh_id, f_r0kh_id_len);
+ goto out;
+ }
+ if (r0kh) {
+ *key = r0kh->key;
+ *key_len = sizeof(r0kh->key);
+ } else {
+ *key = r0kh_wildcard->key;
+ *key_len = sizeof(r0kh_wildcard->key);
+ }
}
- os_get_time(&now);
- tsend = WPA_GET_LE32(f.timestamp);
- if ((now.sec > tsend && now.sec - tsend > 60) ||
- (now.sec < tsend && tsend - now.sec > 60)) {
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
- "timestamp: sender time %d own time %d\n",
- (int) tsend, (int) now.sec);
- return -1;
+ if (!to_r1kh) {
+ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
+ &r1kh_wildcard);
+ if (!r1kh_wildcard &&
+ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
+ f_r1kh_id, FT_R1KH_ID_LEN);
+ goto out;
+ }
+ if (r1kh) {
+ *key = r1kh->key;
+ *key_len = sizeof(r1kh->key);
+ } else {
+ *key = r1kh_wildcard->key;
+ *key_len = sizeof(r1kh_wildcard->key);
+ }
}
- if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
- FT_R1KH_ID_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
- "R1KH-ID (received " MACSTR " own " MACSTR ")",
- MAC2STR(f.r1kh_id),
- MAC2STR(wpa_auth->conf.r1_key_holder));
- return -1;
+ if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
+ src_addr, type, &plain, &plain_len) < 0)
+ goto out;
+
+ os_free(plain);
+
+ if (!to_r0kh) {
+ if (!r0kh)
+ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
+ src_addr, f_r0kh_id,
+ f_r0kh_id_len,
+ ftRRBseqTimeout);
+ if (!r0kh)
+ goto out;
+
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
+ *rkh_seq = r0kh->seq;
+ if (r0kh_out)
+ *r0kh_out = r0kh;
+ if (r0kh_wildcard_out)
+ *r0kh_wildcard_out = r0kh_wildcard;
+ }
+
+ if (!to_r1kh) {
+ if (!r1kh)
+ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
+ src_addr, f_r1kh_id,
+ ftRRBseqTimeout);
+ if (!r1kh)
+ goto out;
+
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
+ *rkh_seq = r1kh->seq;
+ if (r1kh_out)
+ *r1kh_out = r1kh;
+ if (r1kh_wildcard_out)
+ *r1kh_wildcard_out = r1kh_wildcard;
+ }
+
+ return 0;
+out:
+ return -1;
+}
+
+
+static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ int ret = -1;
+ struct ft_rrb_seq f_seq;
+ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
+ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
+ struct ft_remote_seq *rkh_seq = NULL;
+ u8 *packet = NULL, *key = NULL;
+ size_t packet_len = 0, key_len = 0;
+ struct tlv_list seq_resp_auth[5];
+
+ wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
+
+ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ enc, enc_len, auth, auth_len, &rkh_seq, &key,
+ &key_len, NULL, NULL, NULL, NULL) < 0)
+ goto out;
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+ if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ goto out;
+ }
+
+ seq_resp_auth[0].type = FT_RRB_NONCE;
+ seq_resp_auth[0].len = f_nonce_len;
+ seq_resp_auth[0].data = f_nonce;
+ seq_resp_auth[1].type = FT_RRB_SEQ;
+ seq_resp_auth[1].len = sizeof(f_seq);
+ seq_resp_auth[1].data = (u8 *) &f_seq;
+ seq_resp_auth[2].type = FT_RRB_R0KH_ID;
+ seq_resp_auth[2].len = f_r0kh_id_len;
+ seq_resp_auth[2].data = f_r0kh_id;
+ seq_resp_auth[3].type = FT_RRB_R1KH_ID;
+ seq_resp_auth[3].len = FT_R1KH_ID_LEN;
+ seq_resp_auth[3].data = f_r1kh_id;
+ seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
+ seq_resp_auth[4].len = 0;
+ seq_resp_auth[4].data = NULL;
+
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
+ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ &packet, &packet_len) < 0)
+ goto out;
+
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+ FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
+ packet_len);
+
+out:
+ os_free(packet);
+
+ return ret;
+}
+
+
+static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ u8 *key = NULL;
+ size_t key_len = 0;
+ struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
+ struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
+ const u8 *f_nonce, *f_seq;
+ size_t f_nonce_len, f_seq_len;
+ struct ft_remote_seq *rkh_seq = NULL;
+ struct ft_remote_item *item;
+ struct os_reltime now, now_remote;
+ int seq_ret, found;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_dom, msg_seq;
+
+ wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
+
+ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ enc, enc_len, auth, auth_len, &rkh_seq, &key,
+ &key_len, &r0kh, &r1kh, &r0kh_wildcard,
+ &r1kh_wildcard) < 0)
+ goto out;
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
+ f_nonce_len);
+
+ found = 0;
+ dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
+ list) {
+ if (os_memcmp_const(f_nonce, item->nonce,
+ FT_RRB_NONCE_LEN) != 0 ||
+ os_get_reltime(&now) < 0 ||
+ os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
+ continue;
+
+ found = 1;
+ break;
+ }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
+ goto out;
}
- pairwise = le_to_host16(f.pairwise);
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
- MACSTR " pairwise=0x%x",
- MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1",
- f.pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
- f.pmk_r1_name, WPA_PMK_NAME_LEN);
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (r0kh_wildcard)
+ os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
+ }
- wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
- pairwise);
- os_memset(f.pmk_r1, 0, PMK_LEN);
+ if (r1kh) {
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (r1kh_wildcard)
+ os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
+ }
+
+ seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
+ auth_len, "seq response", 1);
+ if (seq_ret == FT_RRB_SEQ_OK) {
+ wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
+ wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
+ auth_len, "seq response");
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
+ sizeof(*msg_both));
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ msg_dom = le_to_host32(msg_both->dom);
+ msg_seq = le_to_host32(msg_both->seq);
+ now_remote.sec = le_to_host32(msg_both->ts);
+ now_remote.usec = 0;
+
+ rkh_seq->rx.num_last = 2;
+ rkh_seq->rx.dom = msg_dom;
+ rkh_seq->rx.offsetidx = 0;
+ /* Accept some older, possibly cached packets as well */
+ rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
+ dl_list_len(&rkh_seq->rx.queue);
+ rkh_seq->rx.last[1] = msg_seq;
+
+ /* local time - offset = remote time
+ * <=> local time - remote time = offset */
+ os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
+ }
+
+ wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
return 0;
+out:
+ return -1;
}
@@ -1642,13 +4215,6 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
return -1;
}
- if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
- return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
- if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
- return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
- if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
- return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
-
wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
if (alen < 1 + 1 + 2 * ETH_ALEN) {
@@ -1726,65 +4292,137 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
}
-static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
- struct wpa_ft_pmk_r0_sa *pmk_r0,
- struct ft_remote_r1kh *r1kh,
- const u8 *s1kh_id, int pairwise)
-{
- struct ft_r0kh_r1kh_push_frame frame, f;
- struct os_time now;
- const u8 *plain;
- u8 *crypt;
-
- os_memset(&frame, 0, sizeof(frame));
- frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
- frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
- frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
- os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
-
- /* aes_wrap() does not support inplace encryption, so use a temporary
- * buffer for the data. */
- os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
- os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
- os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
- s1kh_id, f.pmk_r1, f.pmk_r1_name);
- wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
- WPA_PMK_NAME_LEN);
- os_get_time(&now);
- WPA_PUT_LE32(f.timestamp, now.sec);
- f.pairwise = host_to_le16(pairwise);
- os_memset(f.pad, 0, sizeof(f.pad));
- plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
- timestamp);
- crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
- timestamp);
- if (aes_wrap(r1kh->key, sizeof(r1kh->key),
- (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
- plain, crypt) < 0)
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+ size_t data_len)
+{
+ const u8 *auth, *enc;
+ size_t alen, elen;
+ int no_defer = 0;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP "
+ MACSTR, MAC2STR(src_addr));
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix);
+
+ if (is_multicast_ether_addr(src_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB-OUI received frame from multicast address "
+ MACSTR, MAC2STR(src_addr));
return;
+ }
+
+ if (is_multicast_ether_addr(dst_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB-OUI received frame from remote AP " MACSTR
+ " to multicast address " MACSTR,
+ MAC2STR(src_addr), MAC2STR(dst_addr));
+ no_defer = 1;
+ }
+
+ if (data_len < sizeof(u16)) {
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+ return;
+ }
- wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
+ alen = WPA_GET_LE16(data);
+ if (data_len < sizeof(u16) + alen) {
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+ return;
+ }
+
+ auth = data + sizeof(u16);
+ enc = data + sizeof(u16) + alen;
+ elen = data_len - sizeof(u16) - alen;
+
+ switch (oui_suffix) {
+ case FT_PACKET_R0KH_R1KH_PULL:
+ wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_RESP:
+ wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_PUSH:
+ wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+ wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+ wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
+ alen, no_defer);
+ break;
+ }
+}
+
+
+static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+ struct wpa_ft_pmk_r0_sa *pmk_r0,
+ struct ft_remote_r1kh *r1kh,
+ const u8 *s1kh_id)
+{
+ u8 *packet;
+ size_t packet_len;
+ struct ft_rrb_seq f_seq;
+ struct tlv_list push[] = {
+ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+ .data = s1kh_id },
+ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+ .data = pmk_r0->pmk_r0_name },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+ struct tlv_list push_auth[] = {
+ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+ .data = (u8 *) &f_seq },
+ { .type = FT_RRB_R0KH_ID,
+ .len = wpa_auth->conf.r0_key_holder_len,
+ .data = wpa_auth->conf.r0_key_holder },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = r1kh->id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
+ }
+
+ if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
+ r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_PUSH,
+ &packet, &packet_len) < 0)
+ return -1;
+
+ wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
+ packet, packet_len);
+
+ os_free(packet);
+ return 0;
}
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
{
- struct wpa_ft_pmk_r0_sa *r0;
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
struct ft_remote_r1kh *r1kh;
if (!wpa_auth->conf.pmk_r1_push)
return;
+ if (!wpa_auth->conf.r1kh_list)
+ return;
- r0 = wpa_auth->ft_pmk_cache->pmk_r0;
- while (r0) {
- if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
+ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+ if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+ r0found = r0;
break;
- r0 = r0->next;
+ }
}
+ r0 = r0found;
if (r0 == NULL || r0->pmk_r1_pushed)
return;
r0->pmk_r1_pushed = 1;
@@ -1792,11 +4430,14 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
"for STA " MACSTR, MAC2STR(addr));
- r1kh = wpa_auth->conf.r1kh_list;
- while (r1kh) {
- wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
- r1kh = r1kh->next;
+ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+ if (is_zero_ether_addr(r1kh->addr) ||
+ is_zero_ether_addr(r1kh->id))
+ continue;
+ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+ continue;
+ wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
}
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 21424147e443..812740301c8b 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -9,6 +9,8 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "common/wpa_ctrl.h"
@@ -17,6 +19,7 @@
#include "eapol_auth/eapol_auth_sm_i.h"
#include "eap_server/eap.h"
#include "l2_packet/l2_packet.h"
+#include "eth_p_oui.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "preauth_auth.h"
@@ -24,6 +27,7 @@
#include "tkip_countermeasures.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
+#include "pmksa_cache_auth.h"
#include "wpa_auth.h"
#include "wpa_auth_glue.h"
@@ -41,10 +45,13 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
+ wconf->wpa_group_update_count = conf->wpa_group_update_count;
+ wconf->wpa_disable_eapol_key_retries =
+ conf->wpa_disable_eapol_key_retries;
+ wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
wconf->rsn_pairwise = conf->rsn_pairwise;
wconf->rsn_preauth = conf->rsn_preauth;
wconf->eapol_version = conf->eapol_version;
- wconf->peerkey = conf->peerkey;
wconf->wmm_enabled = conf->wmm_enabled;
wconf->wmm_uapsd = conf->wmm_uapsd;
wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
@@ -52,8 +59,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
+ wconf->sae_require_mfp = conf->sae_require_mfp;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wconf->ssid_len = conf->ssid.ssid_len;
if (wconf->ssid_len > SSID_MAX_LEN)
wconf->ssid_len = SSID_MAX_LEN;
@@ -68,12 +76,18 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
}
os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
wconf->r0_key_lifetime = conf->r0_key_lifetime;
+ wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime;
wconf->reassociation_deadline = conf->reassociation_deadline;
- wconf->r0kh_list = conf->r0kh_list;
- wconf->r1kh_list = conf->r1kh_list;
+ wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
+ wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
+ wconf->rkh_pull_timeout = conf->rkh_pull_timeout;
+ wconf->rkh_pull_retries = conf->rkh_pull_retries;
+ wconf->r0kh_list = &conf->r0kh_list;
+ wconf->r1kh_list = &conf->r1kh_list;
wconf->pmk_r1_push = conf->pmk_r1_push;
wconf->ft_over_ds = conf->ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+ wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_HS20
wconf->disable_gtk = conf->disable_dgaf;
if (conf->osen) {
@@ -107,6 +121,11 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
#endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+ wconf->fils_cache_id_set = conf->fils_cache_id_set;
+ os_memcpy(wconf->fils_cache_id, conf->fils_cache_id,
+ FILS_CACHE_ID_LEN);
+#endif /* CONFIG_FILS */
}
@@ -223,20 +242,47 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
const u8 *p2p_dev_addr,
- const u8 *prev_psk)
+ const u8 *prev_psk, size_t *psk_len)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = ap_get_sta(hapd, addr);
const u8 *psk;
+ if (psk_len)
+ *psk_len = PMK_LEN;
+
#ifdef CONFIG_SAE
if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
if (!sta->sae || prev_psk)
return NULL;
return sta->sae->pmk;
}
+ if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG,
+ "No PSK for STA trying to use SAE with PMKSA caching");
+ return NULL;
+ }
#endif /* CONFIG_SAE */
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ sta && sta->owe_pmk) {
+ if (psk_len)
+ *psk_len = sta->owe_pmk_len;
+ return sta->owe_pmk;
+ }
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) {
+ struct rsn_pmksa_cache_entry *sa;
+
+ sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (sa && sa->akmp == WPA_KEY_MGMT_OWE) {
+ if (psk_len)
+ *psk_len = sa->pmk_len;
+ return sa->pmk;
+ }
+ }
+#endif /* CONFIG_OWE */
+
psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
/*
* This is about to iterate over all psks, prev_psk gives the last
@@ -307,6 +353,37 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
return -1;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (addr && !is_broadcast_ether_addr(addr)) {
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta) {
+ sta->last_tk_alg = alg;
+ sta->last_tk_key_idx = idx;
+ if (key)
+ os_memcpy(sta->last_tk, key, key_len);
+ sta->last_tk_len = key_len;
+ }
+#ifdef CONFIG_IEEE80211W
+ } else if (alg == WPA_ALG_IGTK ||
+ alg == WPA_ALG_BIP_GMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_256 ||
+ alg == WPA_ALG_BIP_CMAC_256) {
+ hapd->last_igtk_alg = alg;
+ hapd->last_igtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_igtk, key, key_len);
+ hapd->last_igtk_len = key_len;
+#endif /* CONFIG_IEEE80211W */
+ } else {
+ hapd->last_gtk_alg = alg;
+ hapd->last_gtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_gtk, key, key_len);
+ hapd->last_gtk_len = key_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
key, key_len);
}
@@ -401,7 +478,32 @@ static int hostapd_wpa_auth_for_each_auth(
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
+
+struct wpa_ft_rrb_rx_later_data {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ size_t data_len;
+ /* followed by data_len octets of data */
+};
+
+static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct wpa_ft_rrb_rx_later_data *data, *n;
+
+ dl_list_for_each_safe(data, n, &hapd->l2_queue,
+ struct wpa_ft_rrb_rx_later_data, list) {
+ if (hapd->wpa_auth) {
+ wpa_ft_rrb_rx(hapd->wpa_auth, data->addr,
+ (const u8 *) (data + 1),
+ data->data_len);
+ }
+ dl_list_del(&data->list);
+ os_free(data);
+ }
+}
+
struct wpa_auth_ft_iface_iter_data {
struct hostapd_data *src_hapd;
@@ -414,33 +516,54 @@ struct wpa_auth_ft_iface_iter_data {
static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
{
struct wpa_auth_ft_iface_iter_data *idata = ctx;
+ struct wpa_ft_rrb_rx_later_data *data;
struct hostapd_data *hapd;
size_t j;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
- if (hapd == idata->src_hapd)
- continue;
- if (!hapd->wpa_auth)
+ if (hapd == idata->src_hapd ||
+ !hapd->wpa_auth ||
+ os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
continue;
- if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
- wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
- "locally managed BSS " MACSTR "@%s -> "
- MACSTR "@%s",
- MAC2STR(idata->src_hapd->own_addr),
- idata->src_hapd->conf->iface,
- MAC2STR(hapd->own_addr), hapd->conf->iface);
- wpa_ft_rrb_rx(hapd->wpa_auth,
- idata->src_hapd->own_addr,
- idata->data, idata->data_len);
+
+ wpa_printf(MSG_DEBUG,
+ "FT: Send RRB data directly to locally managed BSS "
+ MACSTR "@%s -> " MACSTR "@%s",
+ MAC2STR(idata->src_hapd->own_addr),
+ idata->src_hapd->conf->iface,
+ MAC2STR(hapd->own_addr), hapd->conf->iface);
+
+ /* Defer wpa_ft_rrb_rx() until next eloop step as this is
+ * when it would be triggered when reading from a socket.
+ * This avoids
+ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+ * that is calling hapd0:recv handler from within
+ * hapd0:send directly.
+ */
+ data = os_zalloc(sizeof(*data) + idata->data_len);
+ if (!data)
return 1;
- }
+
+ os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN);
+ os_memcpy(data + 1, idata->data, idata->data_len);
+ data->data_len = idata->data_len;
+
+ dl_list_add(&hapd->l2_queue, &data->list);
+
+ if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later,
+ hapd, NULL))
+ eloop_register_timeout(0, 0,
+ hostapd_wpa_ft_rrb_rx_later,
+ hapd, NULL);
+
+ return 1;
}
return 0;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
@@ -465,7 +588,7 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
}
#endif /* CONFIG_TESTING_OPTIONS */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (proto == ETH_P_RRB && hapd->iface->interfaces &&
hapd->iface->interfaces->for_each_interface) {
int res;
@@ -480,7 +603,7 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
if (res == 1)
return data_len;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (hapd->driver && hapd->driver->send_ether)
return hapd->driver->send_ether(hapd->drv_priv, dst,
@@ -503,7 +626,157 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_ETH_P_OUI
+static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd,
+ u8 oui_suffix)
+{
+ switch (oui_suffix) {
+#ifdef CONFIG_IEEE80211R_AP
+ case FT_PACKET_R0KH_R1KH_PULL:
+ return hapd->oui_pull;
+ case FT_PACKET_R0KH_R1KH_RESP:
+ return hapd->oui_resp;
+ case FT_PACKET_R0KH_R1KH_PUSH:
+ return hapd->oui_push;
+ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+ return hapd->oui_sreq;
+ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+ return hapd->oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+ default:
+ return NULL;
+ }
+}
+#endif /* CONFIG_ETH_P_OUI */
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+struct oui_deliver_later_data {
+ struct dl_list list;
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+ size_t data_len;
+ u8 oui_suffix;
+ /* followed by data_len octets of data */
+};
+
+static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct oui_deliver_later_data *data, *n;
+ struct eth_p_oui_ctx *oui_ctx;
+
+ dl_list_for_each_safe(data, n, &hapd->l2_oui_queue,
+ struct oui_deliver_later_data, list) {
+ oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix);
+ if (hapd->wpa_auth && oui_ctx) {
+ eth_p_oui_deliver(oui_ctx, data->src_addr,
+ data->dst_addr,
+ (const u8 *) (data + 1),
+ data->data_len);
+ }
+ dl_list_del(&data->list);
+ os_free(data);
+ }
+}
+
+
+struct wpa_auth_oui_iface_iter_data {
+ struct hostapd_data *src_hapd;
+ const u8 *dst_addr;
+ const u8 *data;
+ size_t data_len;
+ u8 oui_suffix;
+};
+
+static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx)
+{
+ struct wpa_auth_oui_iface_iter_data *idata = ctx;
+ struct oui_deliver_later_data *data;
+ struct hostapd_data *hapd;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hapd == idata->src_hapd)
+ continue;
+ if (!is_multicast_ether_addr(idata->dst_addr) &&
+ os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+ continue;
+
+ /* defer eth_p_oui_deliver until next eloop step as this is
+ * when it would be triggerd from reading from sock
+ * This avoids
+ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+ * that is calling hapd0:recv handler from within
+ * hapd0:send directly.
+ */
+ data = os_zalloc(sizeof(*data) + idata->data_len);
+ if (!data)
+ return 1;
+
+ os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN);
+ os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN);
+ os_memcpy(data + 1, idata->data, idata->data_len);
+ data->data_len = idata->data_len;
+ data->oui_suffix = idata->oui_suffix;
+
+ dl_list_add(&hapd->l2_oui_queue, &data->list);
+
+ if (!eloop_is_timeout_registered(hostapd_oui_deliver_later,
+ hapd, NULL))
+ eloop_register_timeout(0, 0,
+ hostapd_oui_deliver_later,
+ hapd, NULL);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix,
+ const u8 *data, size_t data_len)
+{
+#ifdef CONFIG_ETH_P_OUI
+ struct hostapd_data *hapd = ctx;
+ struct eth_p_oui_ctx *oui_ctx;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (hapd->iface->interfaces &&
+ hapd->iface->interfaces->for_each_interface) {
+ struct wpa_auth_oui_iface_iter_data idata;
+ int res;
+
+ idata.src_hapd = hapd;
+ idata.dst_addr = dst;
+ idata.data = data;
+ idata.data_len = data_len;
+ idata.oui_suffix = oui_suffix;
+ res = hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, hostapd_wpa_auth_oui_iter,
+ &idata);
+ if (res == 1)
+ return data_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix);
+ if (!oui_ctx)
+ return -1;
+
+ return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len);
+#else /* CONFIG_ETH_P_OUI */
+ return -1;
+#endif /* CONFIG_ETH_P_OUI */
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
const u8 *data, size_t data_len)
@@ -563,6 +836,244 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
}
+static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta || !sta->wpa_sm)
+ return -1;
+
+ if (vlan->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from FT",
+ vlan->untagged, vlan->tagged[0] ? "+" : "");
+ return -1;
+ }
+
+ if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+ return -1;
+ /* Configure wpa_group for GTK but ignore error due to driver not
+ * knowing this STA. */
+ ap_sta_bind_vlan(hapd, sta);
+
+ if (sta->vlan_id)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ if (sta->vlan_desc)
+ *vlan = *sta->vlan_desc;
+ else
+ os_memset(vlan, 0, sizeof(*vlan));
+
+ return 0;
+}
+
+
+static int
+hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ os_free(sta->identity);
+ sta->identity = NULL;
+
+ if (sta->eapol_sm) {
+ os_free(sta->eapol_sm->identity);
+ sta->eapol_sm->identity = NULL;
+ sta->eapol_sm->identity_len = 0;
+ }
+
+ if (!identity_len)
+ return 0;
+
+ /* sta->identity is NULL terminated */
+ sta->identity = os_zalloc(identity_len + 1);
+ if (!sta->identity)
+ return -1;
+ os_memcpy(sta->identity, identity, identity_len);
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->identity = os_zalloc(identity_len);
+ if (!sta->eapol_sm->identity)
+ return -1;
+ os_memcpy(sta->eapol_sm->identity, identity, identity_len);
+ sta->eapol_sm->identity_len = identity_len;
+ }
+
+ return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ size_t len;
+ char *identity;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return 0;
+
+ *buf = ieee802_1x_get_identity(sta->eapol_sm, &len);
+ if (*buf && len)
+ return len;
+
+ if (!sta->identity) {
+ *buf = NULL;
+ return 0;
+ }
+
+ identity = sta->identity;
+ len = os_strlen(identity);
+ *buf = (u8 *) identity;
+
+ return len;
+}
+
+
+static int
+hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ os_free(sta->radius_cui);
+ sta->radius_cui = NULL;
+
+ if (sta->eapol_sm) {
+ wpabuf_free(sta->eapol_sm->radius_cui);
+ sta->eapol_sm->radius_cui = NULL;
+ }
+
+ if (!radius_cui)
+ return 0;
+
+ /* sta->radius_cui is NULL terminated */
+ sta->radius_cui = os_zalloc(radius_cui_len + 1);
+ if (!sta->radius_cui)
+ return -1;
+ os_memcpy(sta->radius_cui, radius_cui, radius_cui_len);
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+ radius_cui_len);
+ if (!sta->eapol_sm->radius_cui)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct wpabuf *b;
+ size_t len;
+ char *radius_cui;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return 0;
+
+ b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+ if (b) {
+ len = wpabuf_len(b);
+ *buf = wpabuf_head(b);
+ return len;
+ }
+
+ if (!sta->radius_cui) {
+ *buf = NULL;
+ return 0;
+ }
+
+ radius_cui = sta->radius_cui;
+ len = os_strlen(radius_cui);
+ *buf = (u8 *) radius_cui;
+
+ return len;
+}
+
+
+static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr,
+ int session_timeout)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return;
+
+ if (session_timeout) {
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
+ sta->session_timeout_set = 1;
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ } else {
+ sta->session_timeout_set = 0;
+ ap_sta_no_session_timeout(hapd, sta);
+ }
+}
+
+
+static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct os_reltime now, remaining;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta || !sta->session_timeout_set)
+ return 0;
+
+ os_get_reltime(&now);
+ if (os_reltime_before(&sta->session_timeout, &now)) {
+ /* already expired, return >0 as timeout was set */
+ return 1;
+ }
+
+ os_reltime_sub(&sta->session_timeout, &now, &remaining);
+
+ return (remaining.sec > 0) ? remaining.sec : 1;
+}
+
+
static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
@@ -581,6 +1092,22 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
}
+static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+ MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
+ if (!is_multicast_ether_addr(dst_addr) &&
+ os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+ return;
+ wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
+ len);
+}
+
+
static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
u8 *tspec_ie, size_t tspec_ielen)
{
@@ -588,13 +1115,94 @@ static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
}
-#endif /* CONFIG_IEEE80211R */
+
+
+static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
+ const char *ft_iface)
+{
+ hapd->oui_pull = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_PULL,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_pull)
+ return -1;
+
+ hapd->oui_resp = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_RESP,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_resp)
+ return -1;
+
+ hapd->oui_push = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_PUSH,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_push)
+ return -1;
+
+ hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_sreq)
+ return -1;
+
+ hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_sresp)
+ return -1;
+
+ return 0;
+}
+
+
+static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd)
+{
+ eth_p_oui_unregister(hapd->oui_pull);
+ hapd->oui_pull = NULL;
+ eth_p_oui_unregister(hapd->oui_resp);
+ hapd->oui_resp = NULL;
+ eth_p_oui_unregister(hapd->oui_push);
+ hapd->oui_push = NULL;
+ eth_p_oui_unregister(hapd->oui_sreq);
+ hapd->oui_sreq = NULL;
+ eth_p_oui_unregister(hapd->oui_sresp);
+ hapd->oui_sresp = NULL;
+}
+#endif /* CONFIG_IEEE80211R_AP */
int hostapd_setup_wpa(struct hostapd_data *hapd)
{
struct wpa_auth_config _conf;
- struct wpa_auth_callbacks cb;
+ static const struct wpa_auth_callbacks cb = {
+ .logger = hostapd_wpa_auth_logger,
+ .disconnect = hostapd_wpa_auth_disconnect,
+ .mic_failure_report = hostapd_wpa_auth_mic_failure_report,
+ .psk_failure_report = hostapd_wpa_auth_psk_failure_report,
+ .set_eapol = hostapd_wpa_auth_set_eapol,
+ .get_eapol = hostapd_wpa_auth_get_eapol,
+ .get_psk = hostapd_wpa_auth_get_psk,
+ .get_msk = hostapd_wpa_auth_get_msk,
+ .set_key = hostapd_wpa_auth_set_key,
+ .get_seqnum = hostapd_wpa_auth_get_seqnum,
+ .send_eapol = hostapd_wpa_auth_send_eapol,
+ .for_each_sta = hostapd_wpa_auth_for_each_sta,
+ .for_each_auth = hostapd_wpa_auth_for_each_auth,
+ .send_ether = hostapd_wpa_auth_send_ether,
+ .send_oui = hostapd_wpa_auth_send_oui,
+#ifdef CONFIG_IEEE80211R_AP
+ .send_ft_action = hostapd_wpa_auth_send_ft_action,
+ .add_sta = hostapd_wpa_auth_add_sta,
+ .add_tspec = hostapd_wpa_auth_add_tspec,
+ .set_vlan = hostapd_wpa_auth_set_vlan,
+ .get_vlan = hostapd_wpa_auth_get_vlan,
+ .set_identity = hostapd_wpa_auth_set_identity,
+ .get_identity = hostapd_wpa_auth_get_identity,
+ .set_radius_cui = hostapd_wpa_auth_set_radius_cui,
+ .get_radius_cui = hostapd_wpa_auth_get_radius_cui,
+ .set_session_timeout = hostapd_wpa_auth_set_session_timeout,
+ .get_session_timeout = hostapd_wpa_auth_get_session_timeout,
+#endif /* CONFIG_IEEE80211R_AP */
+ };
const u8 *wpa_ie;
size_t wpa_ie_len;
@@ -603,28 +1211,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
_conf.tx_status = 1;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
_conf.ap_mlme = 1;
- os_memset(&cb, 0, sizeof(cb));
- cb.ctx = hapd;
- cb.logger = hostapd_wpa_auth_logger;
- cb.disconnect = hostapd_wpa_auth_disconnect;
- cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
- cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report;
- cb.set_eapol = hostapd_wpa_auth_set_eapol;
- cb.get_eapol = hostapd_wpa_auth_get_eapol;
- cb.get_psk = hostapd_wpa_auth_get_psk;
- cb.get_msk = hostapd_wpa_auth_get_msk;
- cb.set_key = hostapd_wpa_auth_set_key;
- cb.get_seqnum = hostapd_wpa_auth_get_seqnum;
- cb.send_eapol = hostapd_wpa_auth_send_eapol;
- cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
- cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
- cb.send_ether = hostapd_wpa_auth_send_ether;
-#ifdef CONFIG_IEEE80211R
- cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
- cb.add_sta = hostapd_wpa_auth_add_sta;
- cb.add_tspec = hostapd_wpa_auth_add_tspec;
-#endif /* CONFIG_IEEE80211R */
- hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
+ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) {
wpa_printf(MSG_ERROR, "WPA initialization failed.");
return -1;
@@ -649,12 +1236,14 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
return -1;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (!hostapd_drv_none(hapd) &&
wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
- hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
- hapd->conf->bridge :
- hapd->conf->iface, NULL, ETH_P_RRB,
+ const char *ft_iface;
+
+ ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
+ hapd->conf->iface;
+ hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
hostapd_rrb_receive, hapd, 1);
if (hapd->l2 == NULL &&
(hapd->driver == NULL ||
@@ -663,8 +1252,14 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
"interface");
return -1;
}
+
+ if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to open ETH_P_OUI interface");
+ return -1;
+ }
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
return 0;
@@ -702,8 +1297,13 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd)
}
ieee802_1x_deinit(hapd);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
+ eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX);
+ hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */
+ eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX);
+ hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */
l2_packet_deinit(hapd->l2);
hapd->l2 = NULL;
-#endif /* CONFIG_IEEE80211R */
+ hostapd_wpa_unregister_ft_oui(hapd);
+#endif /* CONFIG_IEEE80211R_AP */
}
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 7fd8f05fa8ff..b1cea1b49b14 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -9,18 +9,13 @@
#ifndef WPA_AUTH_I_H
#define WPA_AUTH_I_H
+#include "utils/list.h"
+
/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
#define RSNA_MAX_EAPOL_RETRIES 4
struct wpa_group;
-struct wpa_stsl_negotiation {
- struct wpa_stsl_negotiation *next;
- u8 initiator[ETH_ALEN];
- u8 peer[ETH_ALEN];
-};
-
-
struct wpa_state_machine {
struct wpa_authenticator *wpa_auth;
struct wpa_group *group;
@@ -48,8 +43,9 @@ struct wpa_state_machine {
Boolean AuthenticationRequest;
Boolean ReAuthenticationRequest;
Boolean Disconnect;
- int TimeoutCtr;
- int GTimeoutCtr;
+ u16 disconnect_reason; /* specific reason code to use with Disconnect */
+ u32 TimeoutCtr;
+ u32 GTimeoutCtr;
Boolean TimeoutEvt;
Boolean EAPOLKeyReceived;
Boolean EAPOLKeyPairwise;
@@ -62,6 +58,7 @@ struct wpa_state_machine {
u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
u8 PMK[PMK_LEN_MAX];
unsigned int pmk_len;
+ u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
struct wpa_ptk PTK;
Boolean PTK_valid;
Boolean pairwise_set;
@@ -89,11 +86,12 @@ struct wpa_state_machine {
unsigned int rx_eapol_key_secure:1;
unsigned int update_snonce:1;
unsigned int alt_snonce_valid:1;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
unsigned int ft_completed:1;
unsigned int pmk_r1_name_valid:1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
unsigned int is_wnmsleep:1;
+ unsigned int pmkid_set:1;
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
int req_replay_counter_used;
@@ -113,8 +111,9 @@ struct wpa_state_machine {
u32 dot11RSNAStatsTKIPLocalMICFailures;
u32 dot11RSNAStatsTKIPRemoteMICFailures;
-#ifdef CONFIG_IEEE80211R
- u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+#ifdef CONFIG_IEEE80211R_AP
+ u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
+ * first 384 bits of MSK */
size_t xxkey_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
* Request */
@@ -129,16 +128,30 @@ struct wpa_state_machine {
const u8 *ies, size_t ies_len);
void *ft_pending_cb_ctx;
struct wpabuf *ft_pending_req_ies;
- u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+ u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
u8 ft_pending_auth_transaction;
u8 ft_pending_current_ap[ETH_ALEN];
-#endif /* CONFIG_IEEE80211R */
+ int ft_pending_pull_left_retries;
+#endif /* CONFIG_IEEE80211R_AP */
int pending_1_of_4_timeout;
#ifdef CONFIG_P2P
u8 ip_addr[4];
#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_FILS
+ u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+ u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+ size_t fils_key_auth_len;
+ unsigned int fils_completed:1;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ void (*eapol_status_cb)(void *ctx1, void *ctx2);
+ void *eapol_status_cb_ctx1;
+ void *eapol_status_cb_ctx2;
+#endif /* CONFIG_TESTING_OPTIONS */
};
@@ -194,10 +207,9 @@ struct wpa_authenticator {
unsigned int dot11RSNATKIPCounterMeasuresInvoked;
unsigned int dot11RSNA4WayHandshakeFailures;
- struct wpa_stsl_negotiation *stsl_negotiations;
-
struct wpa_auth_config conf;
- struct wpa_auth_callbacks cb;
+ const struct wpa_auth_callbacks *cb;
+ void *cb_ctx;
u8 *wpa_ie;
size_t wpa_ie_len;
@@ -213,6 +225,38 @@ struct wpa_authenticator {
};
+#ifdef CONFIG_IEEE80211R_AP
+
+#define FT_REMOTE_SEQ_BACKLOG 16
+struct ft_remote_seq_rx {
+ u32 dom;
+ struct os_reltime time_offset; /* local time - offset = remote time */
+
+ /* accepted sequence numbers: (offset ... offset + 0x40000000]
+ * (except those in last)
+ * dropped sequence numbers: (offset - 0x40000000 ... offset]
+ * all others trigger SEQ_REQ message (except first message)
+ */
+ u32 last[FT_REMOTE_SEQ_BACKLOG];
+ unsigned int num_last;
+ u32 offsetidx;
+
+ struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
+};
+
+struct ft_remote_seq_tx {
+ u32 dom; /* non zero if initialized */
+ u32 seq;
+};
+
+struct ft_remote_seq {
+ struct ft_remote_seq_rx rx;
+ struct ft_remote_seq_tx tx;
+};
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
const u8 *pmkid);
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
@@ -231,24 +275,10 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_authenticator *a, void *ctx),
void *cb_ctx);
-#ifdef CONFIG_PEERKEY
-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
- struct wpa_stsl_negotiation *neg);
-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- const u8 *key_data, size_t key_data_len);
-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
- const u8 *key_data, size_t key_data_len);
-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
- const u8 *key_data, size_t key_data_len);
-#endif /* CONFIG_PEERKEY */
-
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
- size_t r0kh_id_len,
+int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+ const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len);
@@ -257,6 +287,8 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
void wpa_ft_install_ptk(struct wpa_state_machine *sm);
-#endif /* CONFIG_IEEE80211R */
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0,
+ const u8 *pmk_r0_name);
+#endif /* CONFIG_IEEE80211R_AP */
#endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index f79783b91929..cdcc5de39d45 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1,6 +1,6 @@
/*
* hostapd - WPA/RSN IE and KDE definitions
- * Copyright (c) 2004-2015, Jouni Malinen <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.
@@ -164,18 +164,25 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
pos += RSN_SELECTOR_LEN;
num_suites++;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+#ifdef CONFIG_SHA384
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_SHA384 */
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
@@ -210,6 +217,51 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+#ifdef CONFIG_FILS
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_HS20 */
#ifdef CONFIG_RSN_TESTING
if (rsn_testing) {
@@ -230,8 +282,6 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
capab = 0;
if (conf->rsn_preauth)
capab |= WPA_CAPABILITY_PREAUTH;
- if (conf->peerkey)
- capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
if (conf->wmm_enabled) {
/* 4 PTKSA replay counters when using WMM */
capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
@@ -407,7 +457,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
return res;
pos += res;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
res = wpa_write_mdie(&wpa_auth->conf, pos,
buf + sizeof(buf) - pos);
@@ -415,7 +465,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
return res;
pos += res;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
res = wpa_write_wpa_ie(&wpa_auth->conf,
pos, buf + sizeof(buf) - pos);
@@ -474,7 +524,8 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len,
- const u8 *mdie, size_t mdie_len)
+ const u8 *mdie, size_t mdie_len,
+ const u8 *owe_dh, size_t owe_dh_len)
{
struct wpa_ie_data data;
int ciphers, key_mgmt, res, version;
@@ -509,12 +560,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
selector = RSN_AUTH_KEY_MGMT_FT_PSK;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
@@ -531,6 +598,18 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+#ifdef CONFIG_OWE
+ else if (data.key_mgmt & WPA_KEY_MGMT_OWE)
+ selector = RSN_AUTH_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
+ selector = RSN_AUTH_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
+ selector = RSN_AUTH_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
@@ -591,12 +670,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
@@ -611,6 +706,18 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
#endif /* CONFIG_SAE */
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+#ifdef CONFIG_OWE
+ else if (key_mgmt & WPA_KEY_MGMT_OWE)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ else if (key_mgmt & WPA_KEY_MGMT_DPP)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (key_mgmt & WPA_KEY_MGMT_OSEN)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
else
sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
@@ -634,12 +741,6 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
}
- if (ciphers & WPA_CIPHER_TKIP) {
- wpa_printf(MSG_DEBUG, "Management frame protection "
- "cannot use TKIP");
- return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
- }
-
if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
{
wpa_printf(MSG_DEBUG, "Unsupported management group "
@@ -648,14 +749,31 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
}
+#ifdef CONFIG_SAE
+ if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ wpa_auth->conf.sae_require_mfp &&
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection required with SAE, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+#endif /* CONFIG_SAE */
+
if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
!(data.capabilities & WPA_CAPABILITY_MFPC))
sm->mgmt_frame_prot = 0;
else
sm->mgmt_frame_prot = 1;
+
+ if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection cannot use TKIP");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
@@ -668,8 +786,25 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
"MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
return WPA_INVALID_MDIE;
}
+ } else if (mdie != NULL) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Trying to use non-FT AKM suite, but MDIE included");
+ return WPA_INVALID_AKMP;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_OWE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: No Diffie-Hellman Parameter element");
+ return WPA_INVALID_AKMP;
+ }
+ if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM");
+ return WPA_INVALID_AKMP;
+ }
+#endif /* CONFIG_OWE */
sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
if (sm->pairwise < 0)
@@ -723,6 +858,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
}
+#ifdef CONFIG_SAE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
+ !sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKSA cache entry found for SAE");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_DPP
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKSA cache entry found for DPP");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_DPP */
+
if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
os_free(sm->wpa_ie);
sm->wpa_ie = os_malloc(wpa_ie_len);
@@ -815,36 +967,6 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
return 0;
}
-#ifdef CONFIG_PEERKEY
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
- ie->smk = pos + 2 + RSN_SELECTOR_LEN;
- ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
- ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
- ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
- ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
- ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
- ie->error = pos + 2 + RSN_SELECTOR_LEN;
- ie->error_len = pos[1] - RSN_SELECTOR_LEN;
- return 0;
- }
-#endif /* CONFIG_PEERKEY */
-
#ifdef CONFIG_IEEE80211W
if (pos[1] > RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
@@ -908,14 +1030,14 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
if (*pos == WLAN_EID_RSN) {
ie->rsn_ie = pos;
ie->rsn_ie_len = pos[1] + 2;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
ie->mdie = pos;
ie->mdie_len = pos[1] + 2;
} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
ie->ftie = pos;
ie->ftie_len = pos[1] + 2;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, end, ie);
if (ret < 0)
@@ -938,3 +1060,36 @@ int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
{
return sm ? sm->mgmt_frame_prot : 0;
}
+
+
+#ifdef CONFIG_OWE
+u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
+ u8 *pos, size_t max_len,
+ const u8 *req_ies, size_t req_ies_len)
+{
+ int res;
+ struct wpa_auth_config *conf;
+
+ if (!sm)
+ return pos;
+ conf = &sm->wpa_auth->conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->own_ie_override_len) {
+ if (max_len < conf->own_ie_override_len)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+ conf->own_ie_override, conf->own_ie_override_len);
+ os_memcpy(pos, conf->own_ie_override,
+ conf->own_ie_override_len);
+ return pos + conf->own_ie_override_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ res = wpa_write_rsn_ie(conf, pos, max_len,
+ sm->pmksa ? sm->pmksa->pmkid : NULL);
+ if (res < 0)
+ return pos;
+ return pos + res;
+}
+#endif /* CONFIG_OWE */
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index d2067ba3112c..73e433349049 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -19,26 +19,16 @@ struct wpa_eapol_ie_parse {
size_t gtk_len;
const u8 *mac_addr;
size_t mac_addr_len;
-#ifdef CONFIG_PEERKEY
- const u8 *smk;
- size_t smk_len;
- const u8 *nonce;
- size_t nonce_len;
- const u8 *lifetime;
- size_t lifetime_len;
- const u8 *error;
- size_t error_len;
-#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_IEEE80211W
const u8 *igtk;
size_t igtk_len;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
const u8 *mdie;
size_t mdie_len;
const u8 *ftie;
size_t ftie_len;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
const u8 *ip_addr_req;
const u8 *ip_addr_alloc;
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 95b40da0f6bb..5ec019971f37 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1064,7 +1064,9 @@ int hostapd_init_wps(struct hostapd_data *hapd,
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
wps->auth_types |= WPS_AUTH_WPA2;
- if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) {
+ if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)) {
wps->encr_types |= WPS_ENCR_AES;
wps->encr_types_rsn |= WPS_ENCR_AES;
}
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index e0769c08e764..0b596bbcc631 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -53,12 +53,38 @@ static const struct ieee802_11_parse_test_data parse_tests[] = {
18, ParseOK, 9 },
{ (u8 *) "\x8b\x00", 2, ParseOK, 1 },
{ (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 },
+ { (u8 *) "\xed\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xef\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xef\x01\x11", 3, ParseOK, 1 },
+ { (u8 *) "\xf0\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xf1\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 },
+ { (u8 *) "\xf2\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
+ { (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 },
+ { (u8 *) "\xff\x01\x01", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x02", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x04", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x05", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55",
+ 15, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x06", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x07", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11,
+ ParseOK, 1 },
+ { (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 },
{ NULL, 0, ParseOK, 0 }
};
static int ieee802_11_parse_tests(void)
{
int i, ret = 0;
+ struct wpabuf *buf;
wpa_printf(MSG_INFO, "ieee802_11_parse tests");
@@ -84,6 +110,35 @@ static int ieee802_11_parse_tests(void)
ret = -1;
}
+ buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01",
+ 16, 0x11223344);
+ do {
+ const u8 *pos;
+
+ if (!buf) {
+ wpa_printf(MSG_ERROR,
+ "ieee802_11_vendor_ie_concat test 2 failed");
+ ret = -1;
+ break;
+ }
+
+ if (wpabuf_len(buf) != 2) {
+ wpa_printf(MSG_ERROR,
+ "ieee802_11_vendor_ie_concat test 3 failed");
+ ret = -1;
+ break;
+ }
+
+ pos = wpabuf_head(buf);
+ if (pos[0] != 0x01 || pos[1] != 0x02) {
+ wpa_printf(MSG_ERROR,
+ "ieee802_11_vendor_ie_concat test 3 failed");
+ ret = -1;
+ break;
+ }
+ } while (0);
+ wpabuf_free(buf);
+
return ret;
}
diff --git a/src/common/ctrl_iface_common.c b/src/common/ctrl_iface_common.c
index ebbe6ffdb385..e26407dab902 100644
--- a/src/common/ctrl_iface_common.c
+++ b/src/common/ctrl_iface_common.c
@@ -113,17 +113,53 @@ void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
}
+static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input)
+{
+ const char *value;
+ int val;
+
+ if (!input)
+ return 0;
+
+ value = os_strchr(input, '=');
+ if (!value)
+ return -1;
+ value++;
+ val = atoi(value);
+ if (val < 0 || val > 1)
+ return -1;
+
+ if (str_starts(input, "probe_rx_events=")) {
+ if (val)
+ dst->events |= WPA_EVENT_RX_PROBE_REQUEST;
+ else
+ dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST;
+ }
+
+ return 0;
+}
+
+
int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
- socklen_t fromlen)
+ socklen_t fromlen, const char *input)
{
struct wpa_ctrl_dst *dst;
+ /* Update event registration if already attached */
+ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
+ if (!sockaddr_compare(from, fromlen,
+ &dst->addr, dst->addrlen))
+ return ctrl_set_events(dst, input);
+ }
+
+ /* New attachment */
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
return -1;
os_memcpy(&dst->addr, from, fromlen);
dst->addrlen = fromlen;
dst->debug_level = MSG_INFO;
+ ctrl_set_events(dst, input);
dl_list_add(ctrl_dst, &dst->list);
sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen);
diff --git a/src/common/ctrl_iface_common.h b/src/common/ctrl_iface_common.h
index 0b6e3e740291..85e258e938b6 100644
--- a/src/common/ctrl_iface_common.h
+++ b/src/common/ctrl_iface_common.h
@@ -11,6 +11,9 @@
#include "utils/list.h"
+/* Events enable bits (wpa_ctrl_dst::events) */
+#define WPA_EVENT_RX_PROBE_REQUEST BIT(0)
+
/**
* struct wpa_ctrl_dst - Data structure of control interface monitors
*
@@ -23,13 +26,14 @@ struct wpa_ctrl_dst {
socklen_t addrlen;
int debug_level;
int errors;
+ u32 events; /* WPA_EVENT_* bitmap */
};
void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
socklen_t socklen);
int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
- socklen_t fromlen);
+ socklen_t fromlen, const char *input);
int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
socklen_t fromlen);
int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
diff --git a/src/common/defs.h b/src/common/defs.h
index 4f567945942e..c968cd6cb82a 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - Common definitions
- * Copyright (c) 2004-2015, Jouni Malinen <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.
@@ -51,16 +51,28 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
#define WPA_KEY_MGMT_OSEN BIT(15)
#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
+#define WPA_KEY_MGMT_FILS_SHA256 BIT(18)
+#define WPA_KEY_MGMT_FILS_SHA384 BIT(19)
+#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20)
+#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21)
+#define WPA_KEY_MGMT_OWE BIT(22)
+#define WPA_KEY_MGMT_DPP BIT(23)
+#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
{
return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
WPA_KEY_MGMT_CCKM |
WPA_KEY_MGMT_OSEN |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SUITE_B |
- WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+ WPA_KEY_MGMT_FILS_SHA256 |
+ WPA_KEY_MGMT_FILS_SHA384 |
+ WPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA384));
}
static inline int wpa_key_mgmt_wpa_psk(int akm)
@@ -76,7 +88,15 @@ static inline int wpa_key_mgmt_ft(int akm)
{
return !!(akm & (WPA_KEY_MGMT_FT_PSK |
WPA_KEY_MGMT_FT_IEEE8021X |
- WPA_KEY_MGMT_FT_SAE));
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
+ WPA_KEY_MGMT_FT_SAE |
+ WPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA384));
+}
+
+static inline int wpa_key_mgmt_ft_psk(int akm)
+{
+ return !!(akm & WPA_KEY_MGMT_FT_PSK);
}
static inline int wpa_key_mgmt_sae(int akm)
@@ -85,17 +105,32 @@ static inline int wpa_key_mgmt_sae(int akm)
WPA_KEY_MGMT_FT_SAE));
}
+static inline int wpa_key_mgmt_fils(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 |
+ WPA_KEY_MGMT_FILS_SHA384 |
+ WPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA384));
+}
+
static inline int wpa_key_mgmt_sha256(int akm)
{
return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_FT_SAE |
WPA_KEY_MGMT_OSEN |
- WPA_KEY_MGMT_IEEE8021X_SUITE_B));
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+ WPA_KEY_MGMT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA256));
}
static inline int wpa_key_mgmt_sha384(int akm)
{
- return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
+ return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
+ WPA_KEY_MGMT_FILS_SHA384 |
+ WPA_KEY_MGMT_FT_FILS_SHA384));
}
static inline int wpa_key_mgmt_suite_b(int akm)
@@ -108,7 +143,10 @@ static inline int wpa_key_mgmt_wpa(int akm)
{
return wpa_key_mgmt_wpa_ieee8021x(akm) ||
wpa_key_mgmt_wpa_psk(akm) ||
- wpa_key_mgmt_sae(akm);
+ wpa_key_mgmt_fils(akm) ||
+ wpa_key_mgmt_sae(akm) ||
+ akm == WPA_KEY_MGMT_OWE ||
+ akm == WPA_KEY_MGMT_DPP;
}
static inline int wpa_key_mgmt_wpa_any(int akm)
@@ -132,7 +170,13 @@ static inline int wpa_key_mgmt_cckm(int akm)
#define WPA_AUTH_ALG_LEAP BIT(2)
#define WPA_AUTH_ALG_FT BIT(3)
#define WPA_AUTH_ALG_SAE BIT(4)
+#define WPA_AUTH_ALG_FILS BIT(5)
+#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6)
+static inline int wpa_auth_alg_fils(int alg)
+{
+ return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS));
+}
enum wpa_alg {
WPA_ALG_NONE,
@@ -341,4 +385,18 @@ enum wpa_radio_work_band {
BAND_60_GHZ = BIT(2),
};
+enum beacon_rate_type {
+ BEACON_RATE_LEGACY,
+ BEACON_RATE_HT,
+ BEACON_RATE_VHT
+};
+
+enum eap_proxy_sim_state {
+ SIM_STATE_ERROR,
+};
+
+#define OCE_STA BIT(0)
+#define OCE_STA_CFON BIT(1)
+#define OCE_AP BIT(2)
+
#endif /* DEFS_H */
diff --git a/src/common/dhcp.h b/src/common/dhcp.h
new file mode 100644
index 000000000000..e38512c24d9f
--- /dev/null
+++ b/src/common/dhcp.h
@@ -0,0 +1,263 @@
+/*
+ * DHCP definitions
+ * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include <netinet/ip.h>
+#if __FAVOR_BSD
+#include <netinet/udp.h>
+#else
+#define __FAVOR_BSD 1
+#include <netinet/udp.h>
+#undef __FAVOR_BSD
+#endif
+
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+struct dhcp_data {
+ u8 op;
+ u8 htype;
+ u8 hlen;
+ u8 hops;
+ be32 xid;
+ be16 secs;
+ be16 flags;
+ be32 client_ip;
+ be32 your_ip;
+ be32 server_ip;
+ be32 relay_ip;
+ u8 hw_addr[16];
+ u8 serv_name[64];
+ u8 boot_file[128];
+} STRUCT_PACKED;
+
+struct bootp_pkt {
+ struct iphdr iph;
+ struct udphdr udph;
+ u8 op;
+ u8 htype;
+ u8 hlen;
+ u8 hops;
+ be32 xid;
+ be16 secs;
+ be16 flags;
+ be32 client_ip;
+ be32 your_ip;
+ be32 server_ip;
+ be32 relay_ip;
+ u8 hw_addr[16];
+ u8 serv_name[64];
+ u8 boot_file[128];
+ u8 exten[312];
+} STRUCT_PACKED;
+
+#define DHCP_MAGIC 0x63825363
+
+/*
+ * IANA DHCP/BOOTP registry
+ * http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
+*/
+enum dhcp_options {
+ DHCP_OPT_PAD = 0,
+ DHCP_OPT_SUBNET_MASK = 1,
+ DHCP_OPT_TIME_OFFSET = 2,
+ DHCP_OPT_ROUTER = 3,
+ DHCP_OPT_TIME_SERVER = 4,
+ DHCP_OPT_NAME_SERVER = 5,
+ DHCP_OPT_DOMAIN_NAME_SERVER = 6,
+ DHCP_OPT_LOG_SERVER = 7,
+ DHCP_OPT_QUOTES_SERVER = 8,
+ DHCP_OPT_LPR_SERVER = 9,
+ DHCP_OPT_IMPRESS_SERVER = 10,
+ DHCP_OPT_RLP_SERVER = 11,
+ DHCP_OPT_HOSTNAME = 12,
+ DHCP_OPT_BOOT_FILE_SIZE = 13,
+ DHCP_OPT_MERIT_DUMP_FILE = 14,
+ DHCP_OPT_DOMAIN_NAME = 15,
+ DHCP_OPT_SWAP_SERVER = 16,
+ DHCP_OPT_ROOT_PATH = 17,
+ DHCP_OPT_EXTENSION_PATH = 18,
+ DHCP_OPT_FORWARD = 19,
+ DHCP_OPT_SRC_RTE = 20,
+ DHCP_OPT_POLICY_FILTER = 21,
+ DHCP_OPT_MAX_DG_ASSEMBLY = 22,
+ DHCP_OPT_DEFAULT_IP_TTL = 23,
+ DHCP_OPT_MTU_TIMEOUT = 24,
+ DHCP_OPT_MTU_PLATEAU = 25,
+ DHCP_OPT_MTU_INTERFACE = 26,
+ DHCP_OPT_ALL_SUBNETS_LOCAL = 27,
+ DHCP_OPT_BROADCAST_ADDRESS = 28,
+ DHCP_OPT_MASK_DISCOVERY = 29,
+ DHCP_OPT_MASK_SUPPLIER = 30,
+ DHCP_OPT_ROUTER_DISCOVERY = 31,
+ DHCP_OPT_ROUTER_SOLICITATION_ADDRESS = 32,
+ DHCP_OPT_STATIC_ROUTE = 33,
+ DHCP_OPT_TRAILERS = 34,
+ DHCP_OPT_ARP_TIMEOUT = 35,
+ DHCP_OPT_ETHERNET = 36,
+ DHCP_OPT_TCP_DEFAULT_TTL = 37,
+ DHCP_OPT_TCP_KEEPALIVE_INTERVAL = 38,
+ DHCP_OPT_TCP_KEEPALIVE_GARBAGE = 39,
+ DHCP_OPT_NIS_DOMAIN = 40,
+ DHCP_OPT_NIS_SERVERS = 41,
+ DHCP_OPT_NTP_SERVERS = 42,
+ DHCP_OPT_VENDOR_SPECIFIC = 43,
+ DHCP_OPT_NETBIOS_NAME_SERVER = 44,
+ DHCP_OPT_NETBIOS_DISTRIBUTION_SERVER = 45,
+ DHCP_OPT_NETBIOS_NODE_TYPE = 46,
+ DHCP_OPT_NETBIOS_SCOPE = 47,
+ DHCP_OPT_FONT_SERVER = 48,
+ DHCP_OPT_DISPLAY_MANAGER = 49,
+ DHCP_OPT_REQUESTED_IP_ADDRESS = 50,
+ DHCP_OPT_IP_ADDRESS_LEASE_TIME = 51,
+ DHCP_OPT_OVERLOAD = 52,
+ DHCP_OPT_MSG_TYPE = 53,
+ DHCP_OPT_SERVER_ID = 54,
+ DHCP_OPT_PARAMETER_REQ_LIST = 55,
+ DHCP_OPT_MESSAGE = 56,
+ DHCP_OPT_MAX_MESSAGE_SIZE = 57,
+ DHCP_OPT_RENEWAL_TIME = 58,
+ DHCP_OPT_REBINDING_TIME = 59,
+ DHCP_OPT_VENDOR_CLASS_ID = 60,
+ DHCP_OPT_CLIENT_ID = 61,
+ DHCP_OPT_NETWARE_IP_DOMAIN = 62,
+ DHCP_OPT_NETWARE_IP_OPTION = 63,
+ DHCP_OPT_NIS_V3_DOMAIN = 64,
+ DHCP_OPT_NIS_V3_SERVERS = 65,
+ DHCP_OPT_TFTP_SERVER_NAME = 66,
+ DHCP_OPT_BOOT_FILE_NAME = 67,
+ DHCP_OPT_HOME_AGENT_ADDRESSES = 68,
+ DHCP_OPT_SMTP_SERVER = 69,
+ DHCP_OPT_POP3_SERVER = 70,
+ DHCP_OPT_NNTP_SERVER = 71,
+ DHCP_OPT_WWW_SERVER = 72,
+ DHCP_OPT_FINGER_SERVER = 73,
+ DHCP_OPT_IRC_SERVER = 74,
+ DHCP_OPT_STREETTALK_SERVER = 75,
+ DHCP_OPT_STDA_SERVER = 76,
+ DHCP_OPT_USER_CLASS = 77,
+ DHCP_OPT_DIRECTORY_AGENT = 78,
+ DHCP_OPT_SERVICE_SCOPE = 79,
+ DHCP_OPT_RAPID_COMMIT = 80,
+ DHCP_OPT_CLIENT_FQDN = 81,
+ DHCP_OPT_RELAY_AGENT_INFO = 82,
+ DHCP_OPT_ISNS = 83,
+ DHCP_OPT_NDS_SERVERS = 85,
+ DHCP_OPT_NDS_TREE_NAME = 86,
+ DHCP_OPT_NDS_CONTEXT = 87,
+ DHCP_OPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88,
+ DHCP_OPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89,
+ DHCP_OPT_AUTHENTICATION = 90,
+ DHCP_OPT_CLIENT_LAST_TRANSACTION_TIME = 91,
+ DHCP_OPT_ASSOCIATED_IP = 92,
+ DHCP_OPT_CLIENT_SYSYEM = 93,
+ DHCP_OPT_CLIENT_NDI = 94,
+ DHCP_OPT_LDAP = 95,
+ DHCP_OPT_UUID_GUID = 97,
+ DHCP_OPT_USER_AUTH = 98,
+ DHCP_OPT_GEOCONF_CIVIC = 99,
+ DHCP_OPT_PCODE = 100,
+ DHCP_OPT_TCODE = 101,
+ DHCP_OPT_NETINFO_ADDRESS = 112,
+ DHCP_OPT_NETINFO_TAG = 113,
+ DHCP_OPT_URL = 114,
+ DHCP_OPT_AUTO_CONFIG = 116,
+ DHCP_OPT_NAME_SERVICE_SEARCH = 117,
+ DHCP_OPT_SUBNET_SELECTION = 118,
+ DHCP_OPT_DOMAIN_SEARCH = 119,
+ DHCP_OPT_SIP_SERVERS_DCP = 120,
+ DHCP_OPT_CLASSLESS_STATIC_ROUTE = 121,
+ DHCP_OPT_CCC = 122,
+ DHCP_OPT_GEOCONF = 123,
+ DHCP_OPT_V_I_VENDOR_CLASS = 124,
+ DHCP_OPT_V_I_VENDOR_SPECIFIC_INFO = 125,
+ DHCP_OPT_PANA_AGENT = 136,
+ DHCP_OPT_V4_LOST = 137,
+ DHCP_OPT_CAPWAP_AC_V4 = 138,
+ DHCP_OPT_IPV4_ADDRESS_MOS = 139,
+ DHCP_OPT_IPV4_FQDN_MOS = 140,
+ DHCP_OPT_SIP_UA_CONF = 141,
+ DHCP_OPT_IPV4_ADDRESS_ANDSF = 142,
+ DHCP_OPT_GEOLOC = 144,
+ DHCP_OPT_FORCERENEW_NONCE_CAPABLE = 145,
+ DHCP_OPT_RDNSS_SELECTION = 146,
+ DHCP_OPT_TFTP_SERVER_ADDRESS = 150,
+ DHCP_OPT_STATUS_CODE = 151,
+ DHCP_OPT_BASE_TIME = 152,
+ DHCP_OPT_START_TIME_OF_STATE = 153,
+ DHCP_OPT_QUERY_START_TIME = 154,
+ DHCP_OPT_QUERY_END_TIME = 155,
+ DHCP_OPT_STATE = 156,
+ DHCP_OPT_DATA_SOURCE = 157,
+ DHCP_OPT_V4_PCP_SERVER = 158,
+ DHCP_OPT_V4_PORTPARAMS = 159,
+ DHCP_OPT_CAPTIVE_PORTAL = 160,
+ DHCP_OPT_CONF_FILE = 209,
+ DHCP_OPT_PATH_PREFIX = 210,
+ DHCP_OPT_REBOOT_TIME = 211,
+ DHCP_OPT_6RD = 212,
+ DHCP_OPT_V4_ACCESS_DOMAIN = 213,
+ DHCP_OPT_SUBNET_ALLOCATION = 220,
+ DHCP_OPT_VSS = 221,
+ DHCP_OPT_END = 255
+};
+
+enum dhcp_message_types {
+ DHCPDISCOVER = 1,
+ DHCPOFFER = 2,
+ DHCPREQUEST = 3,
+ DHCPDECLINE = 4,
+ DHCPACK = 5,
+ DHCPNAK = 6,
+ DHCPRELEASE = 7,
+ DHCPINFORM = 8,
+ DHCPFORCERENEW = 9,
+ DHCPLEASEQUERY = 10,
+ DHCPLEASEUNASSIGNED = 11,
+ DHCPLEASEUNKNOWN = 12,
+ DHCPLEASEACTIVE = 13,
+ DHCPBULKLEASEQUERY = 14,
+ DHCPLEASEQUERYDONE = 15,
+ DHCPACTIVELEASEQUERY = 16,
+ DHCPLEASEQUERYSTATUS = 17,
+ DHCPTLS = 18,
+};
+
+enum dhcp_relay_agent_suboptions {
+ DHCP_RELAY_OPT_AGENT_CIRCUIT_ID = 1,
+ DHCP_RELAY_OPT_AGENT_REMOTE_ID = 2,
+ DHCP_RELAY_OPT_DOCSIS_DEVICE_CLASS = 4,
+ DHCP_RELAY_OPT_LINK_SELECTION = 5,
+ DHCP_RELAY_OPT_SUBSCRIBE_ID = 6,
+ DHCP_RELAY_OPT_RADIUS_ATTRIBUTES = 7,
+ DHCP_RELAY_OPT_AUTHENTICATION = 8,
+ DHCP_RELAY_OPT_VEDOR_SPECIFIC = 9,
+ DHCP_RELAY_OPT_RELAY_AGENT_FLAGS = 10,
+ DHCP_RELAY_OPT_SERVER_ID_OVERRIDE = 11,
+ DHCP_RELAY_OPT_RELAY_AGENT_ID = 12,
+ DHCP_RELAY_OPT_ACCESS_TECHNOLOGY_TYPE = 13,
+ DHCP_RELAY_OPT_ACCESS_NETWORK_NAME = 14,
+ DHCP_RELAY_OPT_ACCESS_POINT_NAME = 15,
+ DHCP_RELAY_OPT_ACCESS_POINT_BSSID = 16,
+ DHCP_RELAY_OPT_OPERATOR_ID = 17,
+ DHCP_RELAY_OPT_OPERATOR_REALM = 18,
+ DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION = 151,
+ DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION_CONTROL = 152,
+};
+
+enum access_technology_types {
+ ACCESS_TECHNOLOGY_VIRTUAL = 1,
+ ACCESS_TECHNOLOGY_PPP = 2,
+ ACCESS_TECHNOLOGY_ETHERNET = 3,
+ ACCESS_TECHNOLOGY_WLAN = 4,
+ ACCESS_TECHNOLOGY_WIMAX = 5,
+};
+
+#endif /* DHCP_H */
diff --git a/src/common/dpp.c b/src/common/dpp.c
new file mode 100644
index 000000000000..e715e0454d72
--- /dev/null
+++ b/src/common/dpp.c
@@ -0,0 +1,7691 @@
+/*
+ * DPP functionality shared between hostapd and wpa_supplicant
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/json.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "drivers/driver.h"
+#include "dpp.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
+u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_ephemeral_key_override[600];
+size_t dpp_pkex_ephemeral_key_override_len = 0;
+u8 dpp_protocol_key_override[600];
+size_t dpp_protocol_key_override_len = 0;
+u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+size_t dpp_nonce_override_len = 0;
+
+static int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+ sig->r = r;
+ sig->s = s;
+ return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+ const BIGNUM **ps)
+{
+ if (pr)
+ *pr = sig->r;
+ if (ps)
+ *ps = sig->s;
+}
+
+#endif
+
+
+static const struct dpp_curve_params dpp_curves[] = {
+ /* The mandatory to support and the default NIST P-256 curve needs to
+ * be the first entry on this list. */
+ { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
+ { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
+ { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
+ { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
+ { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
+ { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
+ { NULL, 0, 0, 0, 0, NULL, 0, NULL }
+};
+
+
+/* Role-specific elements for PKEX */
+
+/* NIST P-256 */
+static const u8 pkex_init_x_p256[32] = {
+ 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
+ 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
+ 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
+ 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
+ };
+static const u8 pkex_init_y_p256[32] = {
+ 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
+ 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
+ 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
+ 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
+ };
+static const u8 pkex_resp_x_p256[32] = {
+ 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
+ 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
+ 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
+ 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
+};
+static const u8 pkex_resp_y_p256[32] = {
+ 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
+ 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
+ 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
+ 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
+};
+
+/* NIST P-384 */
+static const u8 pkex_init_x_p384[48] = {
+ 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
+ 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
+ 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
+ 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
+ 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
+ 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
+};
+static const u8 pkex_init_y_p384[48] = {
+ 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
+ 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
+ 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
+ 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
+ 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
+ 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
+};
+static const u8 pkex_resp_x_p384[48] = {
+ 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
+ 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
+ 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
+ 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
+ 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
+ 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
+};
+static const u8 pkex_resp_y_p384[48] = {
+ 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
+ 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
+ 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
+ 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
+ 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
+ 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
+};
+
+/* NIST P-521 */
+static const u8 pkex_init_x_p521[66] = {
+ 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
+ 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
+ 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
+ 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
+ 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
+ 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
+ 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
+ 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
+ 0x97, 0x76
+};
+static const u8 pkex_init_y_p521[66] = {
+ 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
+ 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
+ 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
+ 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
+ 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
+ 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
+ 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
+ 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
+ 0x03, 0xa8
+};
+static const u8 pkex_resp_x_p521[66] = {
+ 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
+ 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
+ 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
+ 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
+ 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
+ 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
+ 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
+ 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
+ 0x84, 0xb4
+};
+static const u8 pkex_resp_y_p521[66] = {
+ 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
+ 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
+ 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
+ 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
+ 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
+ 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
+ 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
+ 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
+ 0xce, 0xe1
+};
+
+/* Brainpool P-256r1 */
+static const u8 pkex_init_x_bp_p256r1[32] = {
+ 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
+ 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
+ 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
+ 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
+};
+static const u8 pkex_init_y_bp_p256r1[32] = {
+ 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
+ 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
+ 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
+ 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
+};
+static const u8 pkex_resp_x_bp_p256r1[32] = {
+ 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
+ 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
+ 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
+ 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
+};
+static const u8 pkex_resp_y_bp_p256r1[32] = {
+ 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
+ 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
+ 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
+ 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
+};
+
+/* Brainpool P-384r1 */
+static const u8 pkex_init_x_bp_p384r1[48] = {
+ 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
+ 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
+ 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
+ 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
+ 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
+ 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
+};
+static const u8 pkex_init_y_bp_p384r1[48] = {
+ 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
+ 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
+ 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
+ 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
+ 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
+ 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
+};
+static const u8 pkex_resp_x_bp_p384r1[48] = {
+ 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
+ 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
+ 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
+ 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
+ 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
+ 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
+};
+static const u8 pkex_resp_y_bp_p384r1[48] = {
+ 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
+ 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
+ 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
+ 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
+ 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
+ 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
+};
+
+/* Brainpool P-512r1 */
+static const u8 pkex_init_x_bp_p512r1[64] = {
+ 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
+ 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
+ 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
+ 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
+ 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
+ 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
+ 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
+ 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
+};
+static const u8 pkex_init_y_bp_p512r1[64] = {
+ 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
+ 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
+ 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
+ 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
+ 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
+ 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
+ 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
+ 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
+};
+static const u8 pkex_resp_x_bp_p512r1[64] = {
+ 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
+ 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
+ 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
+ 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
+ 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
+ 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
+ 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
+ 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
+};
+static const u8 pkex_resp_y_bp_p512r1[64] = {
+ 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
+ 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
+ 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
+ 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
+ 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
+ 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
+ 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
+ 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
+};
+
+
+static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point)
+{
+ BIGNUM *x, *y;
+ BN_CTX *ctx;
+ char *x_str = NULL, *y_str = NULL;
+
+ if (!wpa_debug_show_keys)
+ return;
+
+ ctx = BN_CTX_new();
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !x || !y ||
+ EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
+ goto fail;
+
+ x_str = BN_bn2hex(x);
+ y_str = BN_bn2hex(y);
+ if (!x_str || !y_str)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+ OPENSSL_free(x_str);
+ OPENSSL_free(y_str);
+ BN_free(x);
+ BN_free(y);
+ BN_CTX_free(ctx);
+}
+
+
+static int dpp_hash_vector(const struct dpp_curve_params *curve,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ if (curve->hash_len == 32)
+ return sha256_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 48)
+ return sha384_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 64)
+ return sha512_vector(num_elem, addr, len, mac);
+ return -1;
+}
+
+
+static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+ const char *label, u8 *out, size_t outlen)
+{
+ if (hash_len == 32)
+ return hmac_sha256_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 48)
+ return hmac_sha384_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 64)
+ return hmac_sha512_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ return -1;
+}
+
+
+static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 48)
+ return hmac_sha384_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 64)
+ return hmac_sha512_vector(key, key_len, num_elem, addr, len,
+ mac);
+ return -1;
+}
+
+
+static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len, u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256(key, key_len, data, data_len, mac);
+ if (hash_len == 48)
+ return hmac_sha384(key, key_len, data, data_len, mac);
+ if (hash_len == 64)
+ return hmac_sha512(key, key_len, data, data_len, mac);
+ return -1;
+}
+
+
+static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+ int num_bytes, offset;
+
+ num_bytes = BN_num_bytes(bn);
+ if ((size_t) num_bytes > len)
+ return -1;
+ offset = len - num_bytes;
+ os_memset(pos, 0, offset);
+ BN_bn2bin(bn, pos + offset);
+ return 0;
+}
+
+
+static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+ int len, res;
+ EC_KEY *eckey;
+ struct wpabuf *buf;
+ unsigned char *pos;
+
+ eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ if (!eckey)
+ return NULL;
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+ len = i2o_ECPublicKey(eckey, NULL);
+ if (len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to determine public key encoding length");
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ buf = wpabuf_alloc(len);
+ if (!buf) {
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ pos = wpabuf_put(buf, len);
+ res = i2o_ECPublicKey(eckey, &pos);
+ EC_KEY_free(eckey);
+ if (res != len) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to encode public key (res=%d/%d)",
+ res, len);
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ if (!prefix) {
+ /* Remove 0x04 prefix to match DPP definition */
+ pos = wpabuf_mhead(buf);
+ os_memmove(pos, pos + 1, len - 1);
+ buf->used--;
+ }
+
+ return buf;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+ const u8 *buf_x, const u8 *buf_y,
+ size_t len)
+{
+ EC_KEY *eckey = NULL;
+ BN_CTX *ctx;
+ EC_POINT *point = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ ctx = BN_CTX_new();
+ if (!ctx) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ return NULL;
+ }
+
+ point = EC_POINT_new(group);
+ x = BN_bin2bn(buf_x, len, NULL);
+ y = BN_bin2bn(buf_y, len, NULL);
+ if (!point || !x || !y) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ goto fail;
+ }
+
+ if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx) ||
+ EC_POINT_is_at_infinity(group, point)) {
+ wpa_printf(MSG_ERROR, "DPP: Invalid point");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
+
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, group) != 1 ||
+ EC_KEY_set_public_key(eckey, point) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+ goto fail;
+ }
+
+out:
+ BN_free(x);
+ BN_free(y);
+ EC_KEY_free(eckey);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ return pkey;
+fail:
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
+ const u8 *buf, size_t len)
+{
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ EVP_PKEY *pkey = NULL;
+
+ if (len & 1)
+ return NULL;
+
+ eckey = EVP_PKEY_get1_EC_KEY(group_key);
+ if (!eckey) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Could not get EC_KEY from group_key");
+ return NULL;
+ }
+
+ group = EC_KEY_get0_group(eckey);
+ if (group)
+ pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+ len / 2);
+ else
+ wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+ EC_KEY_free(eckey);
+ return pkey;
+}
+
+
+static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
+{
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
+ size_t len)
+{
+ struct wpabuf *msg;
+
+ msg = wpabuf_alloc(8 + len);
+ if (!msg)
+ return NULL;
+ wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+ wpabuf_put_be24(msg, OUI_WFA);
+ wpabuf_put_u8(msg, DPP_OUI_TYPE);
+ wpabuf_put_u8(msg, 1); /* Crypto Suite */
+ wpabuf_put_u8(msg, type);
+ return msg;
+}
+
+
+const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
+{
+ u16 id, alen;
+ const u8 *pos = buf, *end = buf + len;
+
+ while (end - pos >= 4) {
+ id = WPA_GET_LE16(pos);
+ pos += 2;
+ alen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (alen > end - pos)
+ return NULL;
+ if (id == req_id) {
+ *ret_len = alen;
+ return pos;
+ }
+ pos += alen;
+ }
+
+ return NULL;
+}
+
+
+int dpp_check_attrs(const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+ int wrapped_data = 0;
+
+ pos = buf;
+ end = buf + len;
+ while (end - pos >= 4) {
+ u16 id, alen;
+
+ id = WPA_GET_LE16(pos);
+ pos += 2;
+ alen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u",
+ id, alen);
+ if (alen > end - pos) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Truncated message - not enough room for the attribute - dropped");
+ return -1;
+ }
+ if (wrapped_data) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: An unexpected attribute included after the Wrapped Data attribute");
+ return -1;
+ }
+ if (id == DPP_ATTR_WRAPPED_DATA)
+ wrapped_data = 1;
+ pos += alen;
+ }
+
+ if (end != pos) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected octets (%d) after the last attribute",
+ (int) (end - pos));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
+{
+ if (!info)
+ return;
+ os_free(info->uri);
+ os_free(info->info);
+ EVP_PKEY_free(info->pubkey);
+ os_free(info);
+}
+
+
+const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type)
+{
+ switch (type) {
+ case DPP_BOOTSTRAP_QR_CODE:
+ return "QRCODE";
+ case DPP_BOOTSTRAP_PKEX:
+ return "PKEX";
+ }
+ return "??";
+}
+
+
+static int dpp_uri_valid_info(const char *info)
+{
+ while (*info) {
+ unsigned char val = *info++;
+
+ if (val < 0x20 || val > 0x7e || val == 0x3b)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri)
+{
+ bi->uri = os_strdup(uri);
+ return bi->uri ? 0 : -1;
+}
+
+
+int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
+ const char *chan_list)
+{
+ const char *pos = chan_list;
+ int opclass, channel, freq;
+
+ while (pos && *pos && *pos != ';') {
+ opclass = atoi(pos);
+ if (opclass <= 0)
+ goto fail;
+ pos = os_strchr(pos, '/');
+ if (!pos)
+ goto fail;
+ pos++;
+ channel = atoi(pos);
+ if (channel <= 0)
+ goto fail;
+ while (*pos >= '0' && *pos <= '9')
+ pos++;
+ freq = ieee80211_chan_to_freq(NULL, opclass, channel);
+ wpa_printf(MSG_DEBUG,
+ "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
+ opclass, channel, freq);
+ if (freq < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
+ opclass, channel);
+ } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too many channels in URI channel-list - ignore list");
+ bi->num_freq = 0;
+ break;
+ } else {
+ bi->freq[bi->num_freq++] = freq;
+ }
+
+ if (*pos == ';' || *pos == '\0')
+ break;
+ if (*pos != ',')
+ goto fail;
+ pos++;
+ }
+
+ return 0;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list");
+ return -1;
+}
+
+
+int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac)
+{
+ if (!mac)
+ return 0;
+
+ if (hwaddr_aton2(mac, bi->mac_addr) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr));
+
+ return 0;
+}
+
+
+int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
+{
+ const char *end;
+
+ if (!info)
+ return 0;
+
+ end = os_strchr(info, ';');
+ if (!end)
+ end = info + os_strlen(info);
+ bi->info = os_malloc(end - info + 1);
+ if (!bi->info)
+ return -1;
+ os_memcpy(bi->info, info, end - info);
+ bi->info[end - info] = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info);
+ if (!dpp_uri_valid_info(bi->info)) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+ ASN1_OBJECT *oid;
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+ if (oid && OBJ_cmp(poid, oid) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+ int i, tmp;
+
+ if (!nid)
+ return NULL;
+ for (i = 0; dpp_curves[i].name; i++) {
+ tmp = OBJ_txt2nid(dpp_curves[i].name);
+ if (tmp == nid)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
+{
+ const char *end;
+ u8 *data;
+ size_t data_len;
+ EVP_PKEY *pkey;
+ const unsigned char *p;
+ int res;
+ X509_PUBKEY *pub = NULL;
+ ASN1_OBJECT *ppkalg;
+ const unsigned char *pk;
+ int ppklen;
+ X509_ALGOR *pa;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ ASN1_OBJECT *pa_oid;
+#else
+ const ASN1_OBJECT *pa_oid;
+#endif
+ const void *pval;
+ int ptype;
+ const ASN1_OBJECT *poid;
+ char buf[100];
+
+ end = os_strchr(info, ';');
+ if (!end)
+ return -1;
+
+ data = base64_decode((const unsigned char *) info, end - info,
+ &data_len);
+ if (!data) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Invalid base64 encoding on URI public-key");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
+ data, data_len);
+
+ if (sha256_vector(1, (const u8 **) &data, &data_len,
+ bi->pubkey_hash) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ os_free(data);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+ bi->pubkey_hash, SHA256_MAC_LEN);
+
+ /* DER encoded ASN.1 SubjectPublicKeyInfo
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ *
+ * subjectPublicKey = compressed format public key per ANSI X9.63
+ * algorithm = ecPublicKey (1.2.840.10045.2.1)
+ * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+ * prime256v1 (1.2.840.10045.3.1.7)
+ */
+
+ p = data;
+ pkey = d2i_PUBKEY(NULL, &p, data_len);
+ os_free(data);
+
+ if (!pkey) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+ return -1;
+ }
+
+ if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo does not describe an EC key");
+ EVP_PKEY_free(pkey);
+ return -1;
+ }
+
+ res = X509_PUBKEY_set(&pub, pkey);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+ goto fail;
+ }
+
+ res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters");
+ goto fail;
+ }
+ res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+ if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+
+ X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+ if (ptype != V_ASN1_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+ goto fail;
+ }
+ poid = pval;
+ res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+ bi->curve = dpp_get_curve_oid(poid);
+ if (!bi->curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+ buf);
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+ X509_PUBKEY_free(pub);
+ bi->pubkey = pkey;
+ return 0;
+fail:
+ X509_PUBKEY_free(pub);
+ EVP_PKEY_free(pkey);
+ return -1;
+}
+
+
+static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
+{
+ const char *pos = uri;
+ const char *end;
+ const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
+ struct dpp_bootstrap_info *bi;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
+
+ if (os_strncmp(pos, "DPP:", 4) != 0) {
+ wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
+ return NULL;
+ }
+ pos += 4;
+
+ for (;;) {
+ end = os_strchr(pos, ';');
+ if (!end)
+ break;
+
+ if (end == pos) {
+ /* Handle terminating ";;" and ignore unexpected ";"
+ * for parsing robustness. */
+ pos++;
+ continue;
+ }
+
+ if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
+ chan_list = pos + 2;
+ else if (pos[0] == 'M' && pos[1] == ':' && !mac)
+ mac = pos + 2;
+ else if (pos[0] == 'I' && pos[1] == ':' && !info)
+ info = pos + 2;
+ else if (pos[0] == 'K' && pos[1] == ':' && !pk)
+ pk = pos + 2;
+ else
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: Ignore unrecognized URI parameter",
+ pos, end - pos);
+ pos = end + 1;
+ }
+
+ if (!pk) {
+ wpa_printf(MSG_INFO, "DPP: URI missing public-key");
+ return NULL;
+ }
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return NULL;
+
+ if (dpp_clone_uri(bi, uri) < 0 ||
+ dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
+ dpp_parse_uri_mac(bi, mac) < 0 ||
+ dpp_parse_uri_info(bi, info) < 0 ||
+ dpp_parse_uri_pk(bi, pk) < 0) {
+ dpp_bootstrap_info_free(bi);
+ bi = NULL;
+ }
+
+ return bi;
+}
+
+
+struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_parse_uri(uri);
+ if (bi)
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ return bi;
+}
+
+
+static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+ EC_KEY *eckey;
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ EVP_PKEY_print_private(out, key, 0, NULL);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!eckey)
+ return;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (group && point)
+ dpp_debug_print_point(title, group, point);
+
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+ OPENSSL_free(der);
+ if (der_len <= 0) {
+ der = NULL;
+ der_len = i2d_EC_PUBKEY(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+ OPENSSL_free(der);
+ }
+
+ EC_KEY_free(eckey);
+}
+
+
+static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+ EVP_PKEY_CTX *kctx = NULL;
+ EC_KEY *ec_params;
+ EVP_PKEY *params = NULL, *key = NULL;
+ int nid;
+
+ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+ nid = OBJ_txt2nid(curve->name);
+ if (nid == NID_undef) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+ return NULL;
+ }
+
+ ec_params = EC_KEY_new_by_curve_name(nid);
+ if (!ec_params) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EC_KEY parameters");
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EVP_PKEY parameters");
+ goto fail;
+ }
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (!kctx ||
+ EVP_PKEY_keygen_init(kctx) != 1 ||
+ EVP_PKEY_keygen(kctx, &key) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+ goto fail;
+ }
+
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("Own generated key", key);
+
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ return key;
+fail:
+ EVP_PKEY_CTX_free(kctx);
+ EVP_PKEY_free(params);
+ return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_name(const char *name)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+ (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_jwk_crv(const char *name)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ pkey = EVP_PKEY_new();
+ if (!pkey)
+ return NULL;
+ eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ group = EC_KEY_get0_group(eckey);
+ if (!group) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ nid = EC_GROUP_get_curve_name(group);
+ *curve = dpp_get_curve_nid(nid);
+ if (!*curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+ nid);
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+
+ if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ return pkey;
+}
+
+
+typedef struct {
+ /* AlgorithmIdentifier ecPublicKey with optional parameters present
+ * as an OID identifying the curve */
+ X509_ALGOR *alg;
+ /* Compressed format public key per ANSI X9.63 */
+ ASN1_BIT_STRING *pub_key;
+} DPP_BOOTSTRAPPING_KEY;
+
+ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
+
+
+static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ EC_KEY *eckey;
+ struct wpabuf *ret = NULL;
+ size_t len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+ BN_CTX *ctx;
+ DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
+ int nid;
+
+ ctx = BN_CTX_new();
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!ctx || !eckey)
+ goto fail;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (!group || !point)
+ goto fail;
+ dpp_debug_print_point("DPP: bootstrap public key", group, point);
+ nid = EC_GROUP_get_curve_name(group);
+
+ bootstrap = DPP_BOOTSTRAPPING_KEY_new();
+ if (!bootstrap ||
+ X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
+ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+ goto fail;
+
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ NULL, 0, ctx);
+ if (len == 0)
+ goto fail;
+
+ der = OPENSSL_malloc(len);
+ if (!der)
+ goto fail;
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ der, len, ctx);
+
+ OPENSSL_free(bootstrap->pub_key->data);
+ bootstrap->pub_key->data = der;
+ der = NULL;
+ bootstrap->pub_key->length = len;
+ /* No unused bits */
+ bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+ der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to build DER encoded public key");
+ goto fail;
+ }
+
+ ret = wpabuf_alloc_copy(der, der_len);
+fail:
+ DPP_BOOTSTRAPPING_KEY_free(bootstrap);
+ OPENSSL_free(der);
+ EC_KEY_free(eckey);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+ struct wpabuf *der;
+ int res;
+ const u8 *addr[1];
+ size_t len[1];
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ return -1;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+
+ addr[0] = wpabuf_head(der);
+ len[0] = wpabuf_len(der);
+ res = sha256_vector(1, addr, len, bi->pubkey_hash);
+ if (res < 0)
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ else
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
+ SHA256_MAC_LEN);
+ wpabuf_free(der);
+ return res;
+}
+
+
+char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ unsigned char *base64 = NULL;
+ char *pos, *end;
+ size_t len;
+ struct wpabuf *der = NULL;
+ const u8 *addr[1];
+ int res;
+
+ if (!curve) {
+ bi->curve = &dpp_curves[0];
+ } else {
+ bi->curve = dpp_get_curve_name(curve);
+ if (!bi->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+ curve);
+ return NULL;
+ }
+ }
+ if (privkey)
+ bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+ else
+ bi->pubkey = dpp_gen_keypair(bi->curve);
+ if (!bi->pubkey)
+ goto fail;
+ bi->own = 1;
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+
+ addr[0] = wpabuf_head(der);
+ len = wpabuf_len(der);
+ res = sha256_vector(1, addr, &len, bi->pubkey_hash);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
+ SHA256_MAC_LEN);
+
+ base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
+ wpabuf_free(der);
+ der = NULL;
+ if (!base64)
+ goto fail;
+ pos = (char *) base64;
+ end = pos + len;
+ for (;;) {
+ pos = os_strchr(pos, '\n');
+ if (!pos)
+ break;
+ os_memmove(pos, pos + 1, end - pos);
+ }
+ return (char *) base64;
+fail:
+ os_free(base64);
+ wpabuf_free(der);
+ return NULL;
+}
+
+
+static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
+ unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "first intermediate key";
+ int res;
+
+ /* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+ /* HKDF-Extract(<>, M.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+ k1, hash_len);
+ return 0;
+}
+
+
+static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
+ unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "second intermediate key";
+ int res;
+
+ /* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+ k2, hash_len);
+ return 0;
+}
+
+
+static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
+ unsigned int hash_len)
+{
+ size_t nonce_len;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+ const char *info_ke = "DPP Key";
+ u8 prk[DPP_MAX_HASH_LEN];
+ int res;
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem = 0;
+
+ if (!auth->Mx_len || !auth->Nx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mx/Nx not available - cannot derive ke");
+ return -1;
+ }
+
+ /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
+
+ /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+ nonce_len = auth->curve->nonce_len;
+ os_memcpy(nonces, auth->i_nonce, nonce_len);
+ os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+ addr[num_elem] = auth->Mx;
+ len[num_elem] = auth->Mx_len;
+ num_elem++;
+ addr[num_elem] = auth->Nx;
+ len[num_elem] = auth->Nx_len;
+ num_elem++;
+ if (auth->peer_bi && auth->own_bi) {
+ if (!auth->Lx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Lx not available - cannot derive ke");
+ return -1;
+ }
+ addr[num_elem] = auth->Lx;
+ len[num_elem] = auth->secret_len;
+ num_elem++;
+ }
+ res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
+ num_elem, addr, len, prk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
+ ke, hash_len);
+ return 0;
+}
+
+
+static void dpp_build_attr_status(struct wpabuf *msg,
+ enum dpp_status_error status)
+{
+ wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
+ wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, status);
+}
+
+
+static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
+ const struct wpabuf *pi,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ unsigned int neg_freq)
+{
+ struct wpabuf *msg;
+ u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+ u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+ u8 *pos;
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end;
+
+ /* Build DPP Authentication Request frame attributes */
+ attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
+ 4 + sizeof(wrapped_data);
+ if (neg_freq > 0)
+ attr_len += 4 + 2;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Initiator Protocol Key */
+ if (pi) {
+ wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pi));
+ wpabuf_put_buf(msg, pi);
+ }
+
+ /* Channel */
+ if (neg_freq > 0) {
+ u8 op_class, channel;
+
+ if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
+ &channel) ==
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported negotiation frequency request: %d",
+ neg_freq);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_u8(msg, op_class);
+ wpabuf_put_u8(msg, channel);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({I-nonce, I-capabilities}k1) */
+ pos = clear;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ goto skip_i_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len - 1);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len - 1);
+ pos += nonce_len - 1;
+ goto skip_i_nonce;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len);
+ pos += nonce_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_nonce:
+ if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
+ goto skip_i_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->i_capab = auth->allowed_roles;
+ *pos++ = auth->i_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
+ pos[-1] = 0;
+ }
+skip_i_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Request frame attributes", msg);
+
+ return msg;
+}
+
+
+static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
+ enum dpp_status_error status,
+ const struct wpabuf *pr,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ const u8 *r_nonce, const u8 *i_nonce,
+ const u8 *wrapped_r_auth,
+ size_t wrapped_r_auth_len,
+ const u8 *siv_key)
+{
+ struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+ 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
+ u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+ u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end, *pos;
+
+ auth->waiting_auth_conf = 1;
+ auth->auth_resp_tries = 0;
+
+ /* Build DPP Authentication Response frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* DPP Status */
+ if (status != 255)
+ dpp_build_attr_status(msg, status);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Responder Protocol Key */
+ if (pr) {
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+ }
+
+ attr_end = wpabuf_put(msg, 0);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+ pos = clear;
+
+ if (r_nonce) {
+ /* R-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, r_nonce, nonce_len);
+ pos += nonce_len;
+ }
+
+ if (i_nonce) {
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
+ pos[nonce_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ pos += nonce_len;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
+ goto skip_r_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* R-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+ DPP_CAPAB_ENROLLEE;
+ *pos++ = auth->r_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
+ pos[-1] = 0;
+ } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - incompatible R-capabilities");
+ if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
+ pos[-1] = 0;
+ else
+ pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
+ DPP_CAPAB_CONFIGURATOR;
+ }
+skip_r_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wrapped_r_auth) {
+ /* {R-auth}ke */
+ WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+ pos += 2;
+ WPA_PUT_LE16(pos, wrapped_r_auth_len);
+ pos += 2;
+ os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+ pos += wrapped_r_auth_len;
+ }
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Response frame attributes", msg);
+ return msg;
+}
+
+
+static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
+ u16 num_modes, unsigned int freq)
+{
+ u16 m;
+ int c, flag;
+
+ if (!own_modes || !num_modes)
+ return 1;
+
+ for (m = 0; m < num_modes; m++) {
+ for (c = 0; c < own_modes[m].num_channels; c++) {
+ if ((unsigned int) own_modes[m].channels[c].freq !=
+ freq)
+ continue;
+ flag = own_modes[m].channels[c].flag;
+ if (!(flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR)))
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
+ return 0;
+}
+
+
+static int freq_included(const unsigned int freqs[], unsigned int num,
+ unsigned int freq)
+{
+ while (num > 0) {
+ if (freqs[--num] == freq)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void freq_to_start(unsigned int freqs[], unsigned int num,
+ unsigned int freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ if (freqs[i] == freq)
+ break;
+ }
+ if (i == 0 || i >= num)
+ return;
+ os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
+ freqs[0] = freq;
+}
+
+
+static int dpp_channel_intersect(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
+ unsigned int i, freq;
+
+ for (i = 0; i < peer_bi->num_freq; i++) {
+ freq = peer_bi->freq[i];
+ if (freq_included(auth->freq, auth->num_freq, freq))
+ continue;
+ if (dpp_channel_ok_init(own_modes, num_modes, freq))
+ auth->freq[auth->num_freq++] = freq;
+ }
+ if (!auth->num_freq) {
+ wpa_printf(MSG_INFO,
+ "DPP: No available channels for initiating DPP Authentication");
+ return -1;
+ }
+ auth->curr_freq = auth->freq[0];
+ return 0;
+}
+
+
+static int dpp_channel_local_list(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ u16 m;
+ int c, flag;
+ unsigned int freq;
+
+ auth->num_freq = 0;
+
+ if (!own_modes || !num_modes) {
+ auth->freq[0] = 2412;
+ auth->freq[1] = 2437;
+ auth->freq[2] = 2462;
+ auth->num_freq = 3;
+ return 0;
+ }
+
+ for (m = 0; m < num_modes; m++) {
+ for (c = 0; c < own_modes[m].num_channels; c++) {
+ freq = own_modes[m].channels[c].freq;
+ flag = own_modes[m].channels[c].flag;
+ if (flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+ if (freq_included(auth->freq, auth->num_freq, freq))
+ continue;
+ auth->freq[auth->num_freq++] = freq;
+ if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+ m = num_modes;
+ break;
+ }
+ }
+ }
+
+ return auth->num_freq == 0 ? -1 : 0;
+}
+
+
+static int dpp_prepare_channel_list(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ int res;
+ char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
+ unsigned int i;
+
+ if (auth->peer_bi->num_freq > 0)
+ res = dpp_channel_intersect(auth, own_modes, num_modes);
+ else
+ res = dpp_channel_local_list(auth, own_modes, num_modes);
+ if (res < 0)
+ return res;
+
+ /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
+ * likely channels first. */
+ freq_to_start(auth->freq, auth->num_freq, 2462);
+ freq_to_start(auth->freq, auth->num_freq, 2412);
+ freq_to_start(auth->freq, auth->num_freq, 2437);
+
+ auth->freq_idx = 0;
+ auth->curr_freq = auth->freq[0];
+
+ pos = freqs;
+ end = pos + sizeof(freqs);
+ for (i = 0; i < auth->num_freq; i++) {
+ res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
+ freqs);
+
+ return 0;
+}
+
+
+static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
+{
+ struct dpp_bootstrap_info *bi;
+ char *pk = NULL;
+ size_t len;
+
+ if (auth->own_bi)
+ return 0; /* already generated */
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return -1;
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0);
+ if (!pk)
+ goto fail;
+
+ len = 4; /* "DPP:" */
+ len += 4 + os_strlen(pk);
+ bi->uri = os_malloc(len + 1);
+ if (!bi->uri)
+ goto fail;
+ os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Auto-generated own bootstrapping key info: URI %s",
+ bi->uri);
+
+ auth->tmp_own_bi = auth->own_bi = bi;
+
+ os_free(pk);
+
+ return 0;
+fail:
+ os_free(pk);
+ dpp_bootstrap_info_free(bi);
+ return -1;
+}
+
+
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ u8 dpp_allowed_roles,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_authentication *auth;
+ size_t nonce_len;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ struct wpabuf *pi = NULL;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ auth = os_zalloc(sizeof(*auth));
+ if (!auth)
+ return NULL;
+ auth->msg_ctx = msg_ctx;
+ auth->initiator = 1;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = dpp_allowed_roles;
+ auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = peer_bi->curve;
+
+ if (dpp_autogen_bootstrap_key(auth) < 0 ||
+ dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pi)
+ goto fail;
+
+ /* ECDH: M = pI * BR */
+ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ auth->secret_len = secret_len;
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
+ wpabuf_free(pi);
+ pi = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
+ wpabuf_free(pi);
+ pi = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
+ i_pubkey_hash, neg_freq);
+ if (!auth->req_msg)
+ goto fail;
+
+out:
+ wpabuf_free(pi);
+ EVP_PKEY_CTX_free(ctx);
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+ const char *json)
+{
+ size_t nonce_len;
+ size_t json_len, clear_len;
+ struct wpabuf *clear = NULL, *msg = NULL;
+ u8 *wrapped;
+ size_t attr_len;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
+
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->e_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
+ json_len = os_strlen(json);
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
+
+ /* { E-nonce, configAttrib }ke */
+ clear_len = 4 + nonce_len + 4 + json_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = wpabuf_alloc(attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce");
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, nonce_len - 1);
+ wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1);
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* E-nonce */
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, nonce_len);
+ wpabuf_put_data(clear, auth->e_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_e_nonce:
+ if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib");
+ goto skip_conf_attr_obj;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* configAttrib */
+ wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
+ wpabuf_put_le16(clear, json_len);
+ wpabuf_put_data(clear, json, json_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_conf_attr_obj:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ /* No AES-SIV AD */
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 0, NULL, NULL, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Configuration Request frame attributes", msg);
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(clear);
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication success - clear temporary keys");
+ os_memset(auth->Mx, 0, sizeof(auth->Mx));
+ auth->Mx_len = 0;
+ os_memset(auth->Nx, 0, sizeof(auth->Nx));
+ auth->Nx_len = 0;
+ os_memset(auth->Lx, 0, sizeof(auth->Lx));
+ auth->Lx_len = 0;
+ os_memset(auth->k1, 0, sizeof(auth->k1));
+ os_memset(auth->k2, 0, sizeof(auth->k2));
+
+ auth->auth_success = 1;
+}
+
+
+static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+ struct wpabuf *pix, *prx, *bix, *brx;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 zero = 0;
+ int res = -1;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ addr[num_elem] = &zero;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+ struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 one = 1;
+ int res = -1;
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->peer_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->own_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = &one;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL;
+ EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
+ const EC_POINT *BI_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx, *sum, *q;
+ const BIGNUM *bR_bn, *pR_bn;
+ int ret = -1;
+
+ /* L = ((bR + pR) modulo q) * BI */
+
+ bnctx = BN_CTX_new();
+ sum = BN_new();
+ q = BN_new();
+ lx = BN_new();
+ if (!bnctx || !sum || !q || !lx)
+ goto fail;
+ BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+ if (!BI)
+ goto fail;
+ BI_point = EC_KEY_get0_public_key(BI);
+ group = EC_KEY_get0_group(BI);
+ if (!group)
+ goto fail;
+
+ bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+ pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+ if (!bR || !pR)
+ goto fail;
+ bR_bn = EC_KEY_get0_private_key(bR);
+ pR_bn = EC_KEY_get0_private_key(pR);
+ if (!bR_bn || !pR_bn)
+ goto fail;
+ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+ BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+ goto fail;
+ l = EC_POINT_new(group);
+ if (!l ||
+ EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ EC_KEY_free(BI);
+ EC_KEY_free(bR);
+ EC_KEY_free(pR);
+ BN_clear_free(lx);
+ BN_clear_free(sum);
+ BN_free(q);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL, *sum = NULL;
+ EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
+ const EC_POINT *BR_point, *PR_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx;
+ const BIGNUM *bI_bn;
+ int ret = -1;
+
+ /* L = bI * (BR + PR) */
+
+ bnctx = BN_CTX_new();
+ lx = BN_new();
+ if (!bnctx || !lx)
+ goto fail;
+ BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+ PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
+ if (!BR || !PR)
+ goto fail;
+ BR_point = EC_KEY_get0_public_key(BR);
+ PR_point = EC_KEY_get0_public_key(PR);
+
+ bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+ if (!bI)
+ goto fail;
+ group = EC_KEY_get0_group(bI);
+ bI_bn = EC_KEY_get0_private_key(bI);
+ if (!group || !bI_bn)
+ goto fail;
+ sum = EC_POINT_new(group);
+ l = EC_POINT_new(group);
+ if (!sum || !l ||
+ EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+ EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ EC_POINT_clear_free(sum);
+ EC_KEY_free(bI);
+ EC_KEY_free(BR);
+ EC_KEY_free(PR);
+ BN_clear_free(lx);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
+{
+ size_t nonce_len;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ struct wpabuf *msg, *pr = NULL;
+ u8 r_auth[4 + DPP_MAX_HASH_LEN];
+ u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
+ size_t wrapped_r_auth_len;
+ int ret = -1;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
+ enum dpp_status_error status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+ if (!auth->own_bi)
+ return -1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pr)
+ goto fail;
+
+ /* ECDH: N = pR * PI */
+ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ if (auth->own_bi && auth->peer_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_responder(auth) < 0)
+ goto fail;
+ }
+
+ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+ goto fail;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+ WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+ if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
+ goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
+ r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ r_auth, 4 + auth->curve->hash_len,
+ 0, NULL, NULL, wrapped_r_auth) < 0)
+ goto fail;
+ wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+ wrapped_r_auth, wrapped_r_auth_len);
+ w_r_auth = wrapped_r_auth;
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+ r_nonce = auth->r_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
+ wpabuf_free(pr);
+ pr = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
+ wpabuf_free(pr);
+ pr = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
+ goto fail;
+ } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
+ w_r_auth = NULL;
+ wrapped_r_auth_len = 0;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
+ r_nonce = NULL;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ r_nonce, i_nonce,
+ w_r_auth, wrapped_r_auth_len,
+ auth->k2);
+ if (!msg)
+ goto fail;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ ret = 0;
+fail:
+ wpabuf_free(pr);
+ return ret;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->own_bi)
+ return -1;
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ NULL, i_nonce, NULL, 0, auth->k1);
+ if (!msg)
+ return -1;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ return 0;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ unsigned int freq, const u8 *hdr, const u8 *attr_start,
+ size_t attr_len)
+{
+ EVP_PKEY *pi = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
+ *channel;
+ u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
+ i_bootstrap_len, channel_len;
+ struct dpp_authentication *auth = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Request");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ auth = os_zalloc(sizeof(*auth));
+ if (!auth)
+ goto fail;
+ auth->msg_ctx = msg_ctx;
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = own_bi->curve;
+ auth->curr_freq = freq;
+
+ channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
+ &channel_len);
+ if (channel) {
+ int neg_freq;
+
+ if (channel_len < 2) {
+ dpp_auth_fail(auth, "Too short Channel attribute");
+ goto fail;
+ }
+
+ neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
+ channel[0], channel[1], neg_freq);
+ if (neg_freq < 0) {
+ dpp_auth_fail(auth,
+ "Unsupported Channel attribute value");
+ goto fail;
+ }
+
+ if (auth->curr_freq != (unsigned int) neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Changing negotiation channel from %u MHz to %u MHz",
+ freq, neg_freq);
+ auth->curr_freq = neg_freq;
+ }
+ }
+
+ i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+ &i_proto_len);
+ if (!i_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Initiator Protocol Key attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+ i_proto, i_proto_len);
+
+ /* M = bR * PI */
+ pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+ if (!pi) {
+ dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
+ goto fail;
+ }
+ dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+ ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+ goto fail;
+ }
+ auth->secret_len = secret_len;
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+ i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_I_CAPABILITIES,
+ &i_capab_len);
+ if (!i_capab || i_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid I-capabilities");
+ goto fail;
+ }
+ auth->i_capab = i_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ unwrapped = NULL;
+
+ switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+ case DPP_CAPAB_ENROLLEE:
+ if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ break;
+ case DPP_CAPAB_CONFIGURATOR:
+ if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Enrollee role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ break;
+ case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
+ if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator/Enrollee role");
+ goto not_compatible;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
+ auth->i_capab & DPP_CAPAB_ROLE_MASK);
+ goto fail;
+ }
+
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+ char hex[SHA256_MAC_LEN * 2 + 1];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+ if (dpp_auth_build_resp_status(auth,
+ DPP_STATUS_RESPONSE_PENDING) < 0)
+ goto fail;
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+ auth->response_pending = 1;
+ os_memcpy(auth->waiting_pubkey_hash,
+ i_bootstrap, i_bootstrap_len);
+ wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+ i_bootstrap_len);
+ } else {
+ hex[0] = '\0';
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+ "%s", hex);
+ return auth;
+ }
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ goto fail;
+
+ return auth;
+
+not_compatible:
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "i-capab=0x%02x", auth->i_capab);
+ if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+ auth->configurator = 1;
+ else
+ auth->configurator = 0;
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+ goto fail;
+
+ auth->remove_on_tx_status = 1;
+ return auth;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ EVP_PKEY_free(pi);
+ EVP_PKEY_CTX_free(ctx);
+ dpp_auth_deinit(auth);
+ return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+ struct dpp_bootstrap_info *peer_bi)
+{
+ if (!auth || !auth->response_pending ||
+ os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0)
+ return 0;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+ MACSTR, MAC2STR(auth->peer_mac_addr));
+ auth->peer_bi = peer_bi;
+
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return -1;
+
+ return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ u8 i_auth[4 + DPP_MAX_HASH_LEN];
+ size_t i_auth_len;
+ u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
+ size_t r_nonce_len;
+ const u8 *addr[2];
+ size_t len[2], attr_len;
+ u8 *wrapped_i_auth;
+ u8 *wrapped_r_nonce;
+ u8 *attr_start, *attr_end;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+ i_auth_len = 4 + auth->curve->hash_len;
+ r_nonce_len = 4 + auth->curve->nonce_len;
+ /* Build DPP Authentication Confirmation frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ if (auth->own_bi)
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
+ goto skip_wrapped_data;
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ i_auth_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (status == DPP_STATUS_OK) {
+ /* I-auth wrapped with ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+ wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ goto skip_i_auth;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
+ * 1) */
+ WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+ WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+ if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
+ i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+skip_i_auth:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ i_auth, i_auth_len,
+ 2, addr, len, wrapped_i_auth) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+ wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+ } else {
+ /* R-nonce wrapped with k2 */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
+ wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
+
+ WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
+ WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
+ os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
+
+ if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
+ r_nonce, r_nonce_len,
+ 2, addr, len, wrapped_r_nonce) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
+ wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Confirmation frame attributes",
+ msg);
+ if (status == DPP_STATUS_OK)
+ dpp_auth_success(auth);
+
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data, u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *i_nonce, *r_capab;
+ u16 i_nonce_len, r_capab_len;
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported incompatible roles");
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported more time needed");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported failure (status %d)",
+ status);
+ dpp_auth_fail(auth, "Responder reported failure");
+ return;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "r-capab=0x%02x", auth->r_capab);
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+
+ if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
+ role);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue waiting for full DPP Authentication Response");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_RESPONSE_PENDING "%s",
+ auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
+ }
+ }
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ EVP_PKEY *pr;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL, *unwrapped2 = NULL;
+ size_t unwrapped_len = 0, unwrapped2_len = 0;
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+ *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+ wrapped2_len, r_auth_len;
+ u8 r_auth2[DPP_MAX_HASH_LEN];
+ u8 role;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Response");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->initiator || !auth->peer_bi) {
+ dpp_auth_fail(auth, "Unexpected Authentication Response");
+ return NULL;
+ }
+
+ auth->waiting_auth_resp = 0;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Unexpected Responder Bootstrapping Key Hash value");
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ return NULL;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->own_bi ||
+ os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash attribute did not match");
+ return NULL;
+ }
+ } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
+ /* PKEX bootstrapping mandates use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ auth->auth_resp_status = status[0];
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_resp_rx_status(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+ return NULL;
+ }
+
+ if (!i_bootstrap && auth->own_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder decided not to use mutual authentication");
+ auth->own_bi = NULL;
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
+ auth->own_bi != NULL);
+
+ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+ &r_proto_len);
+ if (!r_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+ r_proto, r_proto_len);
+
+ /* N = pI * PR */
+ pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+ if (!pr) {
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+ return NULL;
+ }
+ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+ goto fail;
+ }
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+ auth->peer_protocol_key = pr;
+ pr = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+ os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ if (auth->own_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_initiator(auth) < 0)
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+ if ((auth->allowed_roles ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
+ (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
+ /* Peer selected its role, so move from "either role" to the
+ * role that is compatible with peer's selection. */
+ auth->configurator = role == DPP_CAPAB_ENROLLEE;
+ wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
+ auth->configurator ? "Configurator" : "Enrollee");
+ } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Unexpected role in R-capabilities 0x%02x",
+ role);
+ if (role != DPP_CAPAB_ENROLLEE &&
+ role != DPP_CAPAB_CONFIGURATOR)
+ goto fail;
+ bin_clear_free(unwrapped, unwrapped_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
+ }
+
+ wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+ if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Secondary Wrapped Data");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped2, wrapped2_len);
+
+ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+ goto fail;
+
+ unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+ unwrapped2 = os_malloc(unwrapped2_len);
+ if (!unwrapped2)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped2, wrapped2_len,
+ 0, NULL, NULL, unwrapped2) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped2, unwrapped2_len);
+
+ if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+ dpp_auth_fail(auth,
+ "Invalid attribute in secondary unwrapped data");
+ goto fail;
+ }
+
+ r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+ &r_auth_len);
+ if (!r_auth || r_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Responder Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+ r_auth, r_auth_len);
+ /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ if (dpp_gen_r_auth(auth, r_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+ r_auth2, r_auth_len);
+ if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - Authentication Response in place of Confirm");
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return NULL;
+ return wpabuf_dup(auth->resp_msg);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return dpp_auth_build_conf(auth, DPP_STATUS_OK);
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ EVP_PKEY_free(pr);
+ EVP_PKEY_CTX_free(ctx);
+ return NULL;
+}
+
+
+static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
+ const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data,
+ u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *r_nonce;
+ u16 r_nonce_len;
+
+ /* Authentication Confirm failure cases are expected to include
+ * {R-nonce}k2 in the Wrapped Data attribute. */
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped) {
+ dpp_auth_fail(auth, "Authentication failed");
+ goto fail;
+ }
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+ wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
+ r_nonce, r_nonce_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
+ auth->r_nonce, r_nonce_len);
+ dpp_auth_fail(auth, "R-nonce mismatch");
+ goto fail;
+ }
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE)
+ dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
+ else if (status == DPP_STATUS_AUTH_FAILURE)
+ dpp_auth_fail(auth, "Peer reported authentication failure)");
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ i_auth_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ u8 i_auth2[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return -1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (auth->initiator || !auth->own_bi) {
+ dpp_auth_fail(auth, "Unexpected Authentication Confirm");
+ return -1;
+ }
+
+ auth->waiting_auth_conf = 0;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ dpp_auth_fail(auth,
+ "Responder Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->peer_bi ||
+ os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+ } else if (auth->peer_bi) {
+ /* Mutual authentication and peer did not include its
+ * Bootstrapping Key Hash attribute. */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
+ status[0] == DPP_STATUS_AUTH_FAILURE)
+ return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Authentication failed");
+ return -1;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ return -1;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &i_auth_len);
+ if (!i_auth || i_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Initiator Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+ i_auth, i_auth_len);
+ /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ if (dpp_gen_i_auth(auth, i_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+ i_auth2, i_auth_len);
+ if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
+ goto fail;
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ dpp_auth_success(auth);
+ return 0;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
+
+
+void dpp_configuration_free(struct dpp_configuration *conf)
+{
+ if (!conf)
+ return;
+ str_clear_free(conf->passphrase);
+ os_free(conf->group_id);
+ bin_clear_free(conf, sizeof(*conf));
+}
+
+
+void dpp_auth_deinit(struct dpp_authentication *auth)
+{
+ if (!auth)
+ return;
+ dpp_configuration_free(auth->conf_ap);
+ dpp_configuration_free(auth->conf_sta);
+ EVP_PKEY_free(auth->own_protocol_key);
+ EVP_PKEY_free(auth->peer_protocol_key);
+ wpabuf_free(auth->req_msg);
+ wpabuf_free(auth->resp_msg);
+ wpabuf_free(auth->conf_req);
+ os_free(auth->connector);
+ wpabuf_free(auth->net_access_key);
+ wpabuf_free(auth->c_sign_key);
+ dpp_bootstrap_info_free(auth->tmp_own_bi);
+#ifdef CONFIG_TESTING_OPTIONS
+ os_free(auth->config_obj_override);
+ os_free(auth->discovery_override);
+ os_free(auth->groups_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+ bin_clear_free(auth, sizeof(*auth));
+}
+
+
+static struct wpabuf *
+dpp_build_conf_start(struct dpp_authentication *auth,
+ struct dpp_configuration *conf, size_t tailroom)
+{
+ struct wpabuf *buf;
+ char ssid[6 * sizeof(conf->ssid) + 1];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->discovery_override)
+ tailroom += os_strlen(auth->discovery_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ buf = wpabuf_alloc(200 + tailroom);
+ if (!buf)
+ return NULL;
+ wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->discovery_override) {
+ wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
+ auth->discovery_override);
+ wpabuf_put_str(buf, auth->discovery_override);
+ wpabuf_put_u8(buf, ',');
+ return buf;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpabuf_put_str(buf, "{\"ssid\":\"");
+ json_escape_string(ssid, sizeof(ssid),
+ (const char *) conf->ssid, conf->ssid_len);
+ wpabuf_put_str(buf, ssid);
+ wpabuf_put_str(buf, "\"},");
+
+ return buf;
+}
+
+
+static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+ const char *kid, const struct dpp_curve_params *curve)
+{
+ struct wpabuf *pub;
+ const u8 *pos;
+ char *x = NULL, *y = NULL;
+ int ret = -1;
+
+ pub = dpp_get_pubkey_point(key, 0);
+ if (!pub)
+ goto fail;
+ pos = wpabuf_head(pub);
+ x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
+ pos += curve->prime_len;
+ y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
+ if (!x || !y)
+ goto fail;
+
+ wpabuf_put_str(buf, "\"");
+ wpabuf_put_str(buf, name);
+ wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
+ wpabuf_put_str(buf, curve->jwk_crv);
+ wpabuf_put_str(buf, "\",\"x\":\"");
+ wpabuf_put_str(buf, x);
+ wpabuf_put_str(buf, "\",\"y\":\"");
+ wpabuf_put_str(buf, y);
+ if (kid) {
+ wpabuf_put_str(buf, "\",\"kid\":\"");
+ wpabuf_put_str(buf, kid);
+ }
+ wpabuf_put_str(buf, "\"}");
+ ret = 0;
+fail:
+ wpabuf_free(pub);
+ os_free(x);
+ os_free(y);
+ return ret;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
+ struct dpp_configuration *conf)
+{
+ struct wpabuf *buf = NULL;
+ char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+ size_t tailroom;
+ const struct dpp_curve_params *curve;
+ char jws_prot_hdr[100];
+ size_t signed1_len, signed2_len, signed3_len;
+ struct wpabuf *dppcon = NULL;
+ unsigned char *signature = NULL;
+ const unsigned char *p;
+ size_t signature_len;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ char *dot = ".";
+ const EVP_MD *sign_md;
+ const BIGNUM *r, *s;
+ size_t extra_len = 1000;
+
+ if (!auth->conf) {
+ wpa_printf(MSG_INFO,
+ "DPP: No configurator specified - cannot generate DPP config object");
+ goto fail;
+ }
+ curve = auth->conf->curve;
+ if (curve->hash_len == SHA256_MAC_LEN) {
+ sign_md = EVP_sha256();
+ } else if (curve->hash_len == SHA384_MAC_LEN) {
+ sign_md = EVP_sha384();
+ } else if (curve->hash_len == SHA512_MAC_LEN) {
+ sign_md = EVP_sha512();
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+ goto fail;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->groups_override)
+ extra_len += os_strlen(auth->groups_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (conf->group_id)
+ extra_len += os_strlen(conf->group_id);
+
+ /* Connector (JSON dppCon object) */
+ dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
+ if (!dppcon)
+ goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->groups_override) {
+ wpabuf_put_u8(dppcon, '{');
+ if (auth->groups_override) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: TESTING - groups override: '%s'",
+ auth->groups_override);
+ wpabuf_put_str(dppcon, "\"groups\":");
+ wpabuf_put_str(dppcon, auth->groups_override);
+ wpabuf_put_u8(dppcon, ',');
+ }
+ goto skip_groups;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",",
+ conf->group_id ? conf->group_id : "*");
+ wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta");
+#ifdef CONFIG_TESTING_OPTIONS
+skip_groups:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
+ auth->curve) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
+ goto fail;
+ }
+ if (conf->netaccesskey_expiry) {
+ struct os_tm tm;
+
+ if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to generate expiry string");
+ goto fail;
+ }
+ wpabuf_printf(dppcon,
+ ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
+ tm.year, tm.month, tm.day,
+ tm.hour, tm.min, tm.sec);
+ }
+ wpabuf_put_u8(dppcon, '}');
+ wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
+ (const char *) wpabuf_head(dppcon));
+
+ os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
+ "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
+ auth->conf->kid, curve->jws_alg);
+ signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
+ os_strlen(jws_prot_hdr),
+ &signed1_len, 0);
+ signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
+ wpabuf_len(dppcon),
+ &signed2_len, 0);
+ if (!signed1 || !signed2)
+ goto fail;
+
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
+ auth->conf->csign) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ signature = os_malloc(signature_len);
+ if (!signature)
+ goto fail;
+ if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
+ signature, signature_len);
+ /* Convert to raw coordinates r,s */
+ p = signature;
+ sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
+ if (!sig)
+ goto fail;
+ ECDSA_SIG_get0(sig, &r, &s);
+ if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(s, signature + curve->prime_len,
+ curve->prime_len) < 0)
+ goto fail;
+ signature_len = 2 * curve->prime_len;
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
+ signature, signature_len);
+ signed3 = (char *) base64_url_encode(signature, signature_len,
+ &signed3_len, 0);
+ if (!signed3)
+ goto fail;
+
+ tailroom = 1000;
+ tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
+ tailroom += signed1_len + signed2_len + signed3_len;
+ buf = dpp_build_conf_start(auth, conf, tailroom);
+ if (!buf)
+ goto fail;
+
+ wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
+ wpabuf_put_str(buf, signed1);
+ wpabuf_put_u8(buf, '.');
+ wpabuf_put_str(buf, signed2);
+ wpabuf_put_u8(buf, '.');
+ wpabuf_put_str(buf, signed3);
+ wpabuf_put_str(buf, "\",");
+ if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
+ curve) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
+ goto fail;
+ }
+
+ wpabuf_put_str(buf, "}}");
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
+ wpabuf_head(buf), wpabuf_len(buf));
+
+out:
+ EVP_MD_CTX_destroy(md_ctx);
+ ECDSA_SIG_free(sig);
+ os_free(signed1);
+ os_free(signed2);
+ os_free(signed3);
+ os_free(signature);
+ wpabuf_free(dppcon);
+ return buf;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object");
+ wpabuf_free(buf);
+ buf = NULL;
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
+ struct dpp_configuration *conf)
+{
+ struct wpabuf *buf;
+
+ buf = dpp_build_conf_start(auth, conf, 1000);
+ if (!buf)
+ return NULL;
+
+ wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
+ if (conf->passphrase) {
+ char pass[63 * 6 + 1];
+
+ if (os_strlen(conf->passphrase) > 63) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ json_escape_string(pass, sizeof(pass), conf->passphrase,
+ os_strlen(conf->passphrase));
+ wpabuf_put_str(buf, "\"pass\":\"");
+ wpabuf_put_str(buf, pass);
+ wpabuf_put_str(buf, "\"");
+ } else {
+ char psk[2 * sizeof(conf->psk) + 1];
+
+ wpa_snprintf_hex(psk, sizeof(psk),
+ conf->psk, sizeof(conf->psk));
+ wpabuf_put_str(buf, "\"psk_hex\":\"");
+ wpabuf_put_str(buf, psk);
+ wpabuf_put_str(buf, "\"");
+ }
+ wpabuf_put_str(buf, "}}");
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
+{
+ struct dpp_configuration *conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->config_obj_override) {
+ wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
+ return wpabuf_alloc_copy(auth->config_obj_override,
+ os_strlen(auth->config_obj_override));
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ conf = ap ? auth->conf_ap : auth->conf_sta;
+ if (!conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No configuration available for Enrollee(%s) - reject configuration request",
+ ap ? "ap" : "sta");
+ return NULL;
+ }
+
+ if (conf->akm == DPP_AKM_DPP)
+ return dpp_build_conf_obj_dpp(auth, ap, conf);
+ return dpp_build_conf_obj_legacy(auth, ap, conf);
+}
+
+
+static struct wpabuf *
+dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
+ u16 e_nonce_len, int ap)
+{
+ struct wpabuf *conf;
+ size_t clear_len, attr_len;
+ struct wpabuf *clear = NULL, *msg = NULL;
+ u8 *wrapped;
+ const u8 *addr[1];
+ size_t len[1];
+ enum dpp_status_error status;
+
+ conf = dpp_build_conf_obj(auth, ap);
+ if (conf) {
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+ wpabuf_head(conf), wpabuf_len(conf));
+ }
+ status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
+
+ /* { E-nonce, configurationObject}ke */
+ clear_len = 4 + e_nonce_len;
+ if (conf)
+ clear_len += 4 + wpabuf_len(conf);
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = wpabuf_alloc(attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, e_nonce_len);
+ wpabuf_put_data(clear, e_nonce, e_nonce_len - 1);
+ wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01);
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* E-nonce */
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, e_nonce_len);
+ wpabuf_put_data(clear, e_nonce, e_nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_e_nonce:
+ if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - Config Object");
+ goto skip_config_obj;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (conf) {
+ wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
+ wpabuf_put_le16(clear, wpabuf_len(conf));
+ wpabuf_put_buf(clear, conf);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_config_obj:
+ if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - Status");
+ goto skip_status;
+ }
+ if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 255;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head(msg);
+ len[0] = wpabuf_len(msg);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 1, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Configuration Response attributes", msg);
+out:
+ wpabuf_free(conf);
+ wpabuf_free(clear);
+
+ return msg;
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf *
+dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
+ size_t attr_len)
+{
+ const u8 *wrapped_data, *e_nonce, *config_attr;
+ u16 wrapped_data_len, e_nonce_len, config_attr_len;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ struct wpabuf *resp = NULL;
+ struct json_token *root = NULL, *token;
+ int ap;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Config Request");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_check_attrs(attr_start, attr_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in config request");
+ return NULL;
+ }
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ return NULL;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 0, NULL, NULL, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_ENROLLEE_NONCE,
+ &e_nonce_len);
+ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Enrollee Nonce attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+
+ config_attr = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONFIG_ATTR_OBJ,
+ &config_attr_len);
+ if (!config_attr) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Config Attributes attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
+ config_attr, config_attr_len);
+
+ root = json_parse((const char *) config_attr, config_attr_len);
+ if (!root) {
+ dpp_auth_fail(auth, "Could not parse Config Attributes");
+ goto fail;
+ }
+
+ token = json_get_member(root, "name");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No Config Attributes - name");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
+
+ token = json_get_member(root, "wi-fi_tech");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
+ if (os_strcmp(token->string, "infra") != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
+ token->string);
+ dpp_auth_fail(auth, "Unsupported wi-fi_tech");
+ goto fail;
+ }
+
+ token = json_get_member(root, "netRole");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No Config Attributes - netRole");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
+ if (os_strcmp(token->string, "sta") == 0) {
+ ap = 0;
+ } else if (os_strcmp(token->string, "ap") == 0) {
+ ap = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
+ token->string);
+ dpp_auth_fail(auth, "Unsupported netRole");
+ goto fail;
+ }
+
+ resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
+
+fail:
+ json_free(root);
+ os_free(unwrapped);
+ return resp;
+}
+
+
+static struct wpabuf *
+dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
+ const u8 *prot_hdr, u16 prot_hdr_len,
+ const EVP_MD **ret_md)
+{
+ struct json_token *root, *token;
+ struct wpabuf *kid = NULL;
+
+ root = json_parse((const char *) prot_hdr, prot_hdr_len);
+ if (!root) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JSON parsing failed for JWS Protected Header");
+ goto fail;
+ }
+
+ if (root->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JWS Protected Header root is not an object");
+ goto fail;
+ }
+
+ token = json_get_member(root, "typ");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
+ token->string);
+ if (os_strcmp(token->string, "dppCon") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header typ=%s",
+ token->string);
+ goto fail;
+ }
+
+ token = json_get_member(root, "alg");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
+ token->string);
+ if (os_strcmp(token->string, curve->jws_alg) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
+ token->string, curve->jws_alg);
+ goto fail;
+ }
+ if (os_strcmp(token->string, "ES256") == 0 ||
+ os_strcmp(token->string, "BS256") == 0)
+ *ret_md = EVP_sha256();
+ else if (os_strcmp(token->string, "ES384") == 0 ||
+ os_strcmp(token->string, "BS384") == 0)
+ *ret_md = EVP_sha384();
+ else if (os_strcmp(token->string, "ES512") == 0 ||
+ os_strcmp(token->string, "BS512") == 0)
+ *ret_md = EVP_sha512();
+ else
+ *ret_md = NULL;
+ if (!*ret_md) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header alg=%s",
+ token->string);
+ goto fail;
+ }
+
+ kid = json_get_member_base64url(root, "kid");
+ if (!kid) {
+ wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
+ kid);
+
+fail:
+ json_free(root);
+ return kid;
+}
+
+
+static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
+ struct json_token *cred)
+{
+ struct json_token *pass, *psk_hex;
+
+ wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential");
+
+ pass = json_get_member(cred, "pass");
+ psk_hex = json_get_member(cred, "psk_hex");
+
+ if (pass && pass->type == JSON_STRING) {
+ size_t len = os_strlen(pass->string);
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
+ pass->string, len);
+ if (len < 8 || len > 63)
+ return -1;
+ os_strlcpy(auth->passphrase, pass->string,
+ sizeof(auth->passphrase));
+ } else if (psk_hex && psk_hex->type == JSON_STRING) {
+ if (auth->akm == DPP_AKM_SAE) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected psk_hex with akm=sae");
+ return -1;
+ }
+ if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
+ hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
+ auth->psk, PMK_LEN);
+ auth->psk_set = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
+ return -1;
+ }
+
+ if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
+ !auth->passphrase[0]) {
+ wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve)
+{
+ struct json_token *token;
+ const struct dpp_curve_params *curve;
+ struct wpabuf *x = NULL, *y = NULL;
+ EC_GROUP *group;
+ EVP_PKEY *pkey = NULL;
+
+ token = json_get_member(jwk, "kty");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No kty in JWK");
+ goto fail;
+ }
+ if (os_strcmp(token->string, "EC") != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'",
+ token->string);
+ goto fail;
+ }
+
+ token = json_get_member(jwk, "crv");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No crv in JWK");
+ goto fail;
+ }
+ curve = dpp_get_curve_jwk_crv(token->string);
+ if (!curve) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'",
+ token->string);
+ goto fail;
+ }
+
+ x = json_get_member_base64url(jwk, "x");
+ if (!x) {
+ wpa_printf(MSG_DEBUG, "DPP: No x in JWK");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x);
+ if (wpabuf_len(x) != curve->prime_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWK x length %u (expected %u for curve %s)",
+ (unsigned int) wpabuf_len(x),
+ (unsigned int) curve->prime_len, curve->name);
+ goto fail;
+ }
+
+ y = json_get_member_base64url(jwk, "y");
+ if (!y) {
+ wpa_printf(MSG_DEBUG, "DPP: No y in JWK");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y);
+ if (wpabuf_len(y) != curve->prime_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWK y length %u (expected %u for curve %s)",
+ (unsigned int) wpabuf_len(y),
+ (unsigned int) curve->prime_len, curve->name);
+ goto fail;
+ }
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK");
+ goto fail;
+ }
+
+ pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
+ wpabuf_len(x));
+ *key_curve = curve;
+
+fail:
+ wpabuf_free(x);
+ wpabuf_free(y);
+
+ return pkey;
+}
+
+
+int dpp_key_expired(const char *timestamp, os_time_t *expiry)
+{
+ struct os_time now;
+ unsigned int year, month, day, hour, min, sec;
+ os_time_t utime;
+ const char *pos;
+
+ /* ISO 8601 date and time:
+ * <date>T<time>
+ * YYYY-MM-DDTHH:MM:SSZ
+ * YYYY-MM-DDTHH:MM:SS+03:00
+ */
+ if (os_strlen(timestamp) < 19) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too short timestamp - assume expired key");
+ return 1;
+ }
+ if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u",
+ &year, &month, &day, &hour, &min, &sec) != 6) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to parse expiration day - assume expired key");
+ return 1;
+ }
+
+ if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Invalid date/time information - assume expired key");
+ return 1;
+ }
+
+ pos = timestamp + 19;
+ if (*pos == 'Z' || *pos == '\0') {
+ /* In UTC - no need to adjust */
+ } else if (*pos == '-' || *pos == '+') {
+ int items;
+
+ /* Adjust local time to UTC */
+ items = sscanf(pos + 1, "%02u:%02u", &hour, &min);
+ if (items < 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Invalid time zone designator (%s) - assume expired key",
+ pos);
+ return 1;
+ }
+ if (*pos == '-')
+ utime += 3600 * hour;
+ if (*pos == '+')
+ utime -= 3600 * hour;
+ if (items > 1) {
+ if (*pos == '-')
+ utime += 60 * min;
+ if (*pos == '+')
+ utime -= 60 * min;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Invalid time zone designator (%s) - assume expired key",
+ pos);
+ return 1;
+ }
+ if (expiry)
+ *expiry = utime;
+
+ if (os_get_time(&now) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Cannot get current time - assume expired key");
+ return 1;
+ }
+
+ if (now.sec > utime) {
+ wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)",
+ utime, now.sec);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int dpp_parse_connector(struct dpp_authentication *auth,
+ const unsigned char *payload,
+ u16 payload_len)
+{
+ struct json_token *root, *groups, *netkey, *token;
+ int ret = -1;
+ EVP_PKEY *key = NULL;
+ const struct dpp_curve_params *curve;
+ unsigned int rules = 0;
+
+ root = json_parse((const char *) payload, payload_len);
+ if (!root) {
+ wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
+ goto fail;
+ }
+
+ groups = json_get_member(root, "groups");
+ if (!groups || groups->type != JSON_ARRAY) {
+ wpa_printf(MSG_DEBUG, "DPP: No groups array found");
+ goto skip_groups;
+ }
+ for (token = groups->child; token; token = token->sibling) {
+ struct json_token *id, *role;
+
+ id = json_get_member(token, "groupId");
+ if (!id || id->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing groupId string");
+ goto fail;
+ }
+
+ role = json_get_member(token, "netRole");
+ if (!role || role->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing netRole string");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG,
+ "DPP: connector group: groupId='%s' netRole='%s'",
+ id->string, role->string);
+ rules++;
+ }
+skip_groups:
+
+ if (!rules) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Connector includes no groups");
+ goto fail;
+ }
+
+ token = json_get_member(root, "expiry");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No expiry string found - connector does not expire");
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
+ if (dpp_key_expired(token->string,
+ &auth->net_access_key_expiry)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Connector (netAccessKey) has expired");
+ goto fail;
+ }
+ }
+
+ netkey = json_get_member(root, "netAccessKey");
+ if (!netkey || netkey->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+ goto fail;
+ }
+
+ key = dpp_parse_jwk(netkey, &curve);
+ if (!key)
+ goto fail;
+ dpp_debug_print_key("DPP: Received netAccessKey", key);
+
+ if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: netAccessKey in connector does not match own protocol key");
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->ignore_netaccesskey_mismatch) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: TESTING - skip netAccessKey mismatch");
+ } else {
+ goto fail;
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ goto fail;
+#endif /* CONFIG_TESTING_OPTIONS */
+ }
+
+ ret = 0;
+fail:
+ EVP_PKEY_free(key);
+ json_free(root);
+ return ret;
+}
+
+
+static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
+{
+ struct wpabuf *uncomp;
+ int res;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[1];
+ size_t len[1];
+
+ if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
+ return -1;
+ uncomp = dpp_get_pubkey_point(pub, 1);
+ if (!uncomp)
+ return -1;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
+ addr[0], len[0]);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ if (res < 0)
+ return -1;
+ if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received hash value does not match calculated public key hash value");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
+ hash, SHA256_MAC_LEN);
+ return -1;
+ }
+ return 0;
+}
+
+
+static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign)
+{
+ unsigned char *der = NULL;
+ int der_len;
+
+ der_len = i2d_PUBKEY(csign, &der);
+ if (der_len <= 0)
+ return;
+ wpabuf_free(auth->c_sign_key);
+ auth->c_sign_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+}
+
+
+static void dpp_copy_netaccesskey(struct dpp_authentication *auth)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ EC_KEY *eckey;
+
+ eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+ if (!eckey)
+ return;
+
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len <= 0) {
+ EC_KEY_free(eckey);
+ return;
+ }
+ wpabuf_free(auth->net_access_key);
+ auth->net_access_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+ EC_KEY_free(eckey);
+}
+
+
+struct dpp_signed_connector_info {
+ unsigned char *payload;
+ size_t payload_len;
+};
+
+static enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+ EVP_PKEY *csign_pub, const char *connector)
+{
+ enum dpp_status_error ret = 255;
+ const char *pos, *end, *signed_start, *signed_end;
+ struct wpabuf *kid = NULL;
+ unsigned char *prot_hdr = NULL, *signature = NULL;
+ size_t prot_hdr_len = 0, signature_len = 0;
+ const EVP_MD *sign_md = NULL;
+ unsigned char *der = NULL;
+ int der_len;
+ int res;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ BIGNUM *r = NULL, *s = NULL;
+ const struct dpp_curve_params *curve;
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
+ if (!eckey)
+ goto fail;
+ group = EC_KEY_get0_group(eckey);
+ if (!group)
+ goto fail;
+ nid = EC_GROUP_get_curve_name(group);
+ curve = dpp_get_curve_nid(nid);
+ if (!curve)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
+ os_memset(info, 0, sizeof(*info));
+
+ signed_start = pos = connector;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ prot_hdr = base64_url_decode((const unsigned char *) pos,
+ end - pos, &prot_hdr_len);
+ if (!prot_hdr) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector JWS Protected Header");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: signedConnector - JWS Protected Header",
+ prot_hdr, prot_hdr_len);
+ kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
+ if (!kid) {
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ if (wpabuf_len(kid) != SHA256_MAC_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
+ (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ pos = end + 1;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing dot(2) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ signed_end = end - 1;
+ info->payload = base64_url_decode((const unsigned char *) pos,
+ end - pos, &info->payload_len);
+ if (!info->payload) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector JWS Payload");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: signedConnector - JWS Payload",
+ info->payload, info->payload_len);
+ pos = end + 1;
+ signature = base64_url_decode((const unsigned char *) pos,
+ os_strlen(pos), &signature_len);
+ if (!signature) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector signature");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
+ signature, signature_len);
+
+ if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
+ ret = DPP_STATUS_NO_MATCH;
+ goto fail;
+ }
+
+ if (signature_len & 0x01) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected signedConnector signature length (%d)",
+ (int) signature_len);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ /* JWS Signature encodes the signature (r,s) as two octet strings. Need
+ * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
+ r = BN_bin2bn(signature, signature_len / 2, NULL);
+ s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
+ sig = ECDSA_SIG_new();
+ if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
+ goto fail;
+ r = NULL;
+ s = NULL;
+
+ der_len = i2d_ECDSA_SIG(sig, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
+ signed_end - signed_start + 1) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
+ res, ERR_error_string(ERR_get_error(), NULL));
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ ret = DPP_STATUS_OK;
+fail:
+ EC_KEY_free(eckey);
+ EVP_MD_CTX_destroy(md_ctx);
+ os_free(prot_hdr);
+ wpabuf_free(kid);
+ os_free(signature);
+ ECDSA_SIG_free(sig);
+ BN_free(r);
+ BN_free(s);
+ OPENSSL_free(der);
+ return ret;
+}
+
+
+static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
+ struct json_token *cred)
+{
+ struct dpp_signed_connector_info info;
+ struct json_token *token, *csign;
+ int ret = -1;
+ EVP_PKEY *csign_pub = NULL;
+ const struct dpp_curve_params *key_curve = NULL;
+ const char *signed_connector;
+
+ os_memset(&info, 0, sizeof(info));
+
+ wpa_printf(MSG_DEBUG, "DPP: Connector credential");
+
+ csign = json_get_member(cred, "csign");
+ if (!csign || csign->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON");
+ goto fail;
+ }
+
+ csign_pub = dpp_parse_jwk(csign, &key_curve);
+ if (!csign_pub) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
+
+ token = json_get_member(cred, "signedConnector");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector",
+ token->string, os_strlen(token->string));
+ signed_connector = token->string;
+
+ if (os_strchr(signed_connector, '"') ||
+ os_strchr(signed_connector, '\n')) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected character in signedConnector");
+ goto fail;
+ }
+
+ if (dpp_process_signed_connector(&info, csign_pub,
+ signed_connector) != DPP_STATUS_OK)
+ goto fail;
+
+ if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
+ goto fail;
+ }
+
+ os_free(auth->connector);
+ auth->connector = os_strdup(signed_connector);
+
+ dpp_copy_csign(auth, csign_pub);
+ dpp_copy_netaccesskey(auth);
+
+ ret = 0;
+fail:
+ EVP_PKEY_free(csign_pub);
+ os_free(info.payload);
+ return ret;
+}
+
+
+const char * dpp_akm_str(enum dpp_akm akm)
+{
+ switch (akm) {
+ case DPP_AKM_DPP:
+ return "dpp";
+ case DPP_AKM_PSK:
+ return "psk";
+ case DPP_AKM_SAE:
+ return "sae";
+ case DPP_AKM_PSK_SAE:
+ return "psk+sae";
+ default:
+ return "??";
+ }
+}
+
+
+static enum dpp_akm dpp_akm_from_str(const char *akm)
+{
+ if (os_strcmp(akm, "psk") == 0)
+ return DPP_AKM_PSK;
+ if (os_strcmp(akm, "sae") == 0)
+ return DPP_AKM_SAE;
+ if (os_strcmp(akm, "psk+sae") == 0)
+ return DPP_AKM_PSK_SAE;
+ if (os_strcmp(akm, "dpp") == 0)
+ return DPP_AKM_DPP;
+ return DPP_AKM_UNKNOWN;
+}
+
+
+static int dpp_parse_conf_obj(struct dpp_authentication *auth,
+ const u8 *conf_obj, u16 conf_obj_len)
+{
+ int ret = -1;
+ struct json_token *root, *token, *discovery, *cred;
+
+ root = json_parse((const char *) conf_obj, conf_obj_len);
+ if (!root)
+ return -1;
+ if (root->type != JSON_OBJECT) {
+ dpp_auth_fail(auth, "JSON root is not an object");
+ goto fail;
+ }
+
+ token = json_get_member(root, "wi-fi_tech");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No wi-fi_tech string value found");
+ goto fail;
+ }
+ if (os_strcmp(token->string, "infra") != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
+ token->string);
+ dpp_auth_fail(auth, "Unsupported wi-fi_tech value");
+ goto fail;
+ }
+
+ discovery = json_get_member(root, "discovery");
+ if (!discovery || discovery->type != JSON_OBJECT) {
+ dpp_auth_fail(auth, "No discovery object in JSON");
+ goto fail;
+ }
+
+ token = json_get_member(discovery, "ssid");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No discovery::ssid string value found");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
+ token->string, os_strlen(token->string));
+ if (os_strlen(token->string) > SSID_MAX_LEN) {
+ dpp_auth_fail(auth, "Too long discovery::ssid string value");
+ goto fail;
+ }
+ auth->ssid_len = os_strlen(token->string);
+ os_memcpy(auth->ssid, token->string, auth->ssid_len);
+
+ cred = json_get_member(root, "cred");
+ if (!cred || cred->type != JSON_OBJECT) {
+ dpp_auth_fail(auth, "No cred object in JSON");
+ goto fail;
+ }
+
+ token = json_get_member(cred, "akm");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No cred::akm string value found");
+ goto fail;
+ }
+ auth->akm = dpp_akm_from_str(token->string);
+
+ if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE ||
+ auth->akm == DPP_AKM_PSK_SAE) {
+ if (dpp_parse_cred_legacy(auth, cred) < 0)
+ goto fail;
+ } else if (auth->akm == DPP_AKM_DPP) {
+ if (dpp_parse_cred_dpp(auth, cred) < 0)
+ goto fail;
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
+ token->string);
+ dpp_auth_fail(auth, "Unsupported akm");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
+ ret = 0;
+fail:
+ json_free(root);
+ return ret;
+}
+
+
+int dpp_conf_resp_rx(struct dpp_authentication *auth,
+ const struct wpabuf *resp)
+{
+ const u8 *wrapped_data, *e_nonce, *status, *conf_obj;
+ u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len;
+ const u8 *addr[1];
+ size_t len[1];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ int ret = -1;
+
+ if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in config response");
+ return -1;
+ }
+
+ wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
+ DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ return -1;
+
+ addr[0] = wpabuf_head(resp);
+ len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 1, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_ENROLLEE_NONCE,
+ &e_nonce_len);
+ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Enrollee Nonce attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+ if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Enrollee Nonce mismatch");
+ goto fail;
+ }
+
+ status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
+ DPP_ATTR_STATUS, &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Configurator rejected configuration");
+ goto fail;
+ }
+
+ conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
+ if (!conf_obj) {
+ dpp_auth_fail(auth,
+ "Missing required Configuration Object attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+ conf_obj, conf_obj_len);
+ if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
+ goto fail;
+
+ ret = 0;
+
+fail:
+ os_free(unwrapped);
+ return ret;
+}
+
+
+void dpp_configurator_free(struct dpp_configurator *conf)
+{
+ if (!conf)
+ return;
+ EVP_PKEY_free(conf->csign);
+ os_free(conf->kid);
+ os_free(conf);
+}
+
+
+int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
+ size_t buflen)
+{
+ EC_KEY *eckey;
+ int key_len, ret = -1;
+ unsigned char *key = NULL;
+
+ if (!conf->csign)
+ return -1;
+
+ eckey = EVP_PKEY_get1_EC_KEY(conf->csign);
+ if (!eckey)
+ return -1;
+
+ key_len = i2d_ECPrivateKey(eckey, &key);
+ if (key_len > 0)
+ ret = wpa_snprintf_hex(buf, buflen, key, key_len);
+
+ EC_KEY_free(eckey);
+ OPENSSL_free(key);
+ return ret;
+}
+
+
+struct dpp_configurator *
+dpp_keygen_configurator(const char *curve, const u8 *privkey,
+ size_t privkey_len)
+{
+ struct dpp_configurator *conf;
+ struct wpabuf *csign_pub = NULL;
+ u8 kid_hash[SHA256_MAC_LEN];
+ const u8 *addr[1];
+ size_t len[1];
+
+ conf = os_zalloc(sizeof(*conf));
+ if (!conf)
+ return NULL;
+
+ if (!curve) {
+ conf->curve = &dpp_curves[0];
+ } else {
+ conf->curve = dpp_get_curve_name(curve);
+ if (!conf->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+ curve);
+ os_free(conf);
+ return NULL;
+ }
+ }
+ if (privkey)
+ conf->csign = dpp_set_keypair(&conf->curve, privkey,
+ privkey_len);
+ else
+ conf->csign = dpp_gen_keypair(conf->curve);
+ if (!conf->csign)
+ goto fail;
+ conf->own = 1;
+
+ csign_pub = dpp_get_pubkey_point(conf->csign, 1);
+ if (!csign_pub) {
+ wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key");
+ goto fail;
+ }
+
+ /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
+ addr[0] = wpabuf_head(csign_pub);
+ len[0] = wpabuf_len(csign_pub);
+ if (sha256_vector(1, addr, len, kid_hash) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to derive kid for C-sign-key");
+ goto fail;
+ }
+
+ conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash),
+ NULL, 0);
+ if (!conf->kid)
+ goto fail;
+out:
+ wpabuf_free(csign_pub);
+ return conf;
+fail:
+ dpp_configurator_free(conf);
+ conf = NULL;
+ goto out;
+}
+
+
+int dpp_configurator_own_config(struct dpp_authentication *auth,
+ const char *curve, int ap)
+{
+ struct wpabuf *conf_obj;
+ int ret = -1;
+
+ if (!auth->conf) {
+ wpa_printf(MSG_DEBUG, "DPP: No configurator specified");
+ return -1;
+ }
+
+ if (!curve) {
+ auth->curve = &dpp_curves[0];
+ } else {
+ auth->curve = dpp_get_curve_name(curve);
+ if (!auth->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+ curve);
+ return -1;
+ }
+ }
+ wpa_printf(MSG_DEBUG,
+ "DPP: Building own configuration/connector with curve %s",
+ auth->curve->name);
+
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ if (!auth->own_protocol_key)
+ return -1;
+ dpp_copy_netaccesskey(auth);
+ auth->peer_protocol_key = auth->own_protocol_key;
+ dpp_copy_csign(auth, auth->conf->csign);
+
+ conf_obj = dpp_build_conf_obj(auth, ap);
+ if (!conf_obj)
+ goto fail;
+ ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
+ wpabuf_len(conf_obj));
+fail:
+ wpabuf_free(conf_obj);
+ auth->peer_protocol_key = NULL;
+ return ret;
+}
+
+
+static int dpp_compatible_netrole(const char *role1, const char *role2)
+{
+ return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) ||
+ (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0);
+}
+
+
+static int dpp_connector_compatible_group(struct json_token *root,
+ const char *group_id,
+ const char *net_role)
+{
+ struct json_token *groups, *token;
+
+ groups = json_get_member(root, "groups");
+ if (!groups || groups->type != JSON_ARRAY)
+ return 0;
+
+ for (token = groups->child; token; token = token->sibling) {
+ struct json_token *id, *role;
+
+ id = json_get_member(token, "groupId");
+ if (!id || id->type != JSON_STRING)
+ continue;
+
+ role = json_get_member(token, "netRole");
+ if (!role || role->type != JSON_STRING)
+ continue;
+
+ if (os_strcmp(id->string, "*") != 0 &&
+ os_strcmp(group_id, "*") != 0 &&
+ os_strcmp(id->string, group_id) != 0)
+ continue;
+
+ if (dpp_compatible_netrole(role->string, net_role))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int dpp_connector_match_groups(struct json_token *own_root,
+ struct json_token *peer_root)
+{
+ struct json_token *groups, *token;
+
+ groups = json_get_member(peer_root, "groups");
+ if (!groups || groups->type != JSON_ARRAY) {
+ wpa_printf(MSG_DEBUG, "DPP: No peer groups array found");
+ return 0;
+ }
+
+ for (token = groups->child; token; token = token->sibling) {
+ struct json_token *id, *role;
+
+ id = json_get_member(token, "groupId");
+ if (!id || id->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing peer groupId string");
+ continue;
+ }
+
+ role = json_get_member(token, "netRole");
+ if (!role || role->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing peer groups::netRole string");
+ continue;
+ }
+ wpa_printf(MSG_DEBUG,
+ "DPP: peer connector group: groupId='%s' netRole='%s'",
+ id->string, role->string);
+ if (dpp_connector_compatible_group(own_root, id->string,
+ role->string)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Compatible group/netRole in own connector");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
+ unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "DPP PMK";
+ int res;
+
+ /* PMK = HKDF(<>, "DPP PMK", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
+ pmk, hash_len);
+ return 0;
+}
+
+
+static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+ EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
+{
+ struct wpabuf *nkx, *pkx;
+ int ret = -1, res;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 hash[SHA256_MAC_LEN];
+
+ /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+ nkx = dpp_get_pubkey_point(own_key, 0);
+ pkx = dpp_get_pubkey_point(peer_key, 0);
+ if (!nkx || !pkx)
+ goto fail;
+ addr[0] = wpabuf_head(nkx);
+ len[0] = wpabuf_len(nkx) / 2;
+ addr[1] = wpabuf_head(pkx);
+ len[1] = wpabuf_len(pkx) / 2;
+ if (len[0] != len[1])
+ goto fail;
+ if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
+ addr[0] = wpabuf_head(pkx);
+ addr[1] = wpabuf_head(nkx);
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
+ res = sha256_vector(2, addr, len, hash);
+ if (res < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
+ os_memcpy(pmkid, hash, PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
+ ret = 0;
+fail:
+ wpabuf_free(nkx);
+ wpabuf_free(pkx);
+ return ret;
+}
+
+
+enum dpp_status_error
+dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len,
+ os_time_t *expiry)
+{
+ struct json_token *root = NULL, *netkey, *token;
+ struct json_token *own_root = NULL;
+ enum dpp_status_error ret = 255, res;
+ EVP_PKEY *own_key = NULL, *peer_key = NULL;
+ struct wpabuf *own_key_pub = NULL;
+ const struct dpp_curve_params *curve, *own_curve;
+ struct dpp_signed_connector_info info;
+ const unsigned char *p;
+ EVP_PKEY *csign = NULL;
+ char *signed_connector = NULL;
+ const char *pos, *end;
+ unsigned char *own_conn = NULL;
+ size_t own_conn_len;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t Nx_len;
+ u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+
+ os_memset(intro, 0, sizeof(*intro));
+ os_memset(&info, 0, sizeof(info));
+ if (expiry)
+ *expiry = 0;
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to parse local C-sign-key information");
+ goto fail;
+ }
+
+ own_key = dpp_set_keypair(&own_curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ goto fail;
+ }
+
+ pos = os_strchr(own_connector, '.');
+ if (!pos) {
+ wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+ goto fail;
+ }
+ pos++;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
+ goto fail;
+ }
+ own_conn = base64_url_decode((const unsigned char *) pos,
+ end - pos, &own_conn_len);
+ if (!own_conn) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode own signedConnector JWS Payload");
+ goto fail;
+ }
+
+ own_root = json_parse((const char *) own_conn, own_conn_len);
+ if (!own_root) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
+ goto fail;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
+ peer_connector, peer_connector_len);
+ signed_connector = os_malloc(peer_connector_len + 1);
+ if (!signed_connector)
+ goto fail;
+ os_memcpy(signed_connector, peer_connector, peer_connector_len);
+ signed_connector[peer_connector_len] = '\0';
+
+ res = dpp_process_signed_connector(&info, csign, signed_connector);
+ if (res != DPP_STATUS_OK) {
+ ret = res;
+ goto fail;
+ }
+
+ root = json_parse((const char *) info.payload, info.payload_len);
+ if (!root) {
+ wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ if (!dpp_connector_match_groups(own_root, root)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer connector does not include compatible group netrole with own connector");
+ ret = DPP_STATUS_NO_MATCH;
+ goto fail;
+ }
+
+ token = json_get_member(root, "expiry");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No expiry string found - connector does not expire");
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
+ if (dpp_key_expired(token->string, expiry)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Connector (netAccessKey) has expired");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ }
+
+ netkey = json_get_member(root, "netAccessKey");
+ if (!netkey || netkey->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ peer_key = dpp_parse_jwk(netkey, &curve);
+ if (!peer_key) {
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+
+ if (own_curve != curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mismatching netAccessKey curves (%s != %s)",
+ own_curve->name, curve->name);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ /* ECDH: N = nk * PK */
+ ctx = EVP_PKEY_CTX_new(own_key, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 ||
+ Nx_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ Nx, Nx_len);
+
+ /* PMK = HKDF(<>, "DPP PMK", N.x) */
+ if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK");
+ goto fail;
+ }
+ intro->pmk_len = curve->hash_len;
+
+ /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+ if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
+ goto fail;
+ }
+
+ ret = DPP_STATUS_OK;
+fail:
+ if (ret != DPP_STATUS_OK)
+ os_memset(intro, 0, sizeof(*intro));
+ os_memset(Nx, 0, sizeof(Nx));
+ EVP_PKEY_CTX_free(ctx);
+ os_free(own_conn);
+ os_free(signed_connector);
+ os_free(info.payload);
+ EVP_PKEY_free(own_key);
+ wpabuf_free(own_key_pub);
+ EVP_PKEY_free(peer_key);
+ EVP_PKEY_free(csign);
+ json_free(root);
+ json_free(own_root);
+ return ret;
+}
+
+
+static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
+ int init)
+{
+ EC_GROUP *group;
+ size_t len = curve->prime_len;
+ const u8 *x, *y;
+
+ switch (curve->ike_group) {
+ case 19:
+ x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
+ y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
+ break;
+ case 20:
+ x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
+ y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
+ break;
+ case 21:
+ x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
+ y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
+ break;
+ case 28:
+ x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
+ y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
+ break;
+ case 29:
+ x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
+ y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
+ break;
+ case 30:
+ x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
+ y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
+ break;
+ default:
+ return NULL;
+ }
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return NULL;
+ return dpp_set_pubkey_point_group(group, x, y, len);
+}
+
+
+static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+ const u8 *mac_init, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ const EC_GROUP **ret_group)
+{
+ u8 hash[DPP_MAX_HASH_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ unsigned int num_elem = 0;
+ EC_POINT *Qi = NULL;
+ EVP_PKEY *Pi = NULL;
+ EC_KEY *Pi_ec = NULL;
+ const EC_POINT *Pi_point;
+ BIGNUM *hash_bn = NULL;
+ const EC_GROUP *group = NULL;
+ EC_GROUP *group2 = NULL;
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+
+ wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
+ addr[num_elem] = mac_init;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+ if (identifier) {
+ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+ identifier);
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ addr[num_elem] = (const u8 *) code;
+ len[num_elem] = os_strlen(code);
+ num_elem++;
+ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: H(MAC-Initiator | [identifier |] code)",
+ hash, curve->hash_len);
+ Pi = dpp_pkex_get_role_elem(curve, 1);
+ if (!Pi)
+ goto fail;
+ dpp_debug_print_key("DPP: Pi", Pi);
+ Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
+ if (!Pi_ec)
+ goto fail;
+ Pi_point = EC_KEY_get0_public_key(Pi_ec);
+
+ group = EC_KEY_get0_group(Pi_ec);
+ if (!group)
+ goto fail;
+ group2 = EC_GROUP_dup(group);
+ if (!group2)
+ goto fail;
+ Qi = EC_POINT_new(group2);
+ if (!Qi) {
+ EC_GROUP_free(group2);
+ goto fail;
+ }
+ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+ if (!hash_bn ||
+ EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
+ goto fail;
+ if (EC_POINT_is_at_infinity(group, Qi)) {
+ wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qi", group, Qi);
+out:
+ EC_KEY_free(Pi_ec);
+ EVP_PKEY_free(Pi);
+ BN_clear_free(hash_bn);
+ if (ret_group)
+ *ret_group = group2;
+ return Qi;
+fail:
+ EC_POINT_free(Qi);
+ Qi = NULL;
+ goto out;
+}
+
+
+static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+ const u8 *mac_resp, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ const EC_GROUP **ret_group)
+{
+ u8 hash[DPP_MAX_HASH_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ unsigned int num_elem = 0;
+ EC_POINT *Qr = NULL;
+ EVP_PKEY *Pr = NULL;
+ EC_KEY *Pr_ec = NULL;
+ const EC_POINT *Pr_point;
+ BIGNUM *hash_bn = NULL;
+ const EC_GROUP *group = NULL;
+ EC_GROUP *group2 = NULL;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+
+ wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
+ addr[num_elem] = mac_resp;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+ if (identifier) {
+ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+ identifier);
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ addr[num_elem] = (const u8 *) code;
+ len[num_elem] = os_strlen(code);
+ num_elem++;
+ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: H(MAC-Responder | [identifier |] code)",
+ hash, curve->hash_len);
+ Pr = dpp_pkex_get_role_elem(curve, 0);
+ if (!Pr)
+ goto fail;
+ dpp_debug_print_key("DPP: Pr", Pr);
+ Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
+ if (!Pr_ec)
+ goto fail;
+ Pr_point = EC_KEY_get0_public_key(Pr_ec);
+
+ group = EC_KEY_get0_group(Pr_ec);
+ if (!group)
+ goto fail;
+ group2 = EC_GROUP_dup(group);
+ if (!group2)
+ goto fail;
+ Qr = EC_POINT_new(group2);
+ if (!Qr) {
+ EC_GROUP_free(group2);
+ goto fail;
+ }
+ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+ if (!hash_bn ||
+ EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
+ goto fail;
+ if (EC_POINT_is_at_infinity(group, Qr)) {
+ wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qr", group, Qr);
+out:
+ EC_KEY_free(Pr_ec);
+ EVP_PKEY_free(Pr);
+ BN_clear_free(hash_bn);
+ if (ret_group)
+ *ret_group = group2;
+ return Qr;
+fail:
+ EC_POINT_free(Qr);
+ Qr = NULL;
+ goto out;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve)
+{
+ BN_CTX *ctx;
+ BIGNUM *x, *y;
+ int ret = -1;
+ EC_GROUP *group;
+ EC_POINT *point;
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return -1;
+
+ ctx = BN_CTX_new();
+ point = EC_POINT_new(group);
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !point || !x || !y)
+ goto fail;
+
+ if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ /* Generate a random y coordinate that results in a point that is not
+ * on the curve. */
+ for (;;) {
+ if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
+ ctx) != 1) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
+ /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
+ * return an error from EC_POINT_set_affine_coordinates_GFp()
+ * when the point is not on the curve. */
+ break;
+#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
+ goto fail;
+#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx))
+ break;
+ }
+
+ if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ if (ret < 0)
+ wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
+ BN_free(x);
+ BN_free(y);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+
+ return ret;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
+{
+ EC_KEY *X_ec = NULL;
+ const EC_POINT *X_point;
+ BN_CTX *bnctx = NULL;
+ const EC_GROUP *group;
+ EC_POINT *Qi = NULL, *M = NULL;
+ struct wpabuf *M_buf = NULL;
+ BIGNUM *Mx = NULL, *My = NULL;
+ struct wpabuf *msg = NULL;
+ size_t attr_len;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
+ pkex->identifier, bnctx, &group);
+ if (!Qi)
+ goto fail;
+
+ /* Generate a random ephemeral keypair x/X */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key x/X");
+ pkex->x = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->x = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->x = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->x)
+ goto fail;
+
+ /* M = X + Qi */
+ X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
+ if (!X_ec)
+ goto fail;
+ X_point = EC_KEY_get0_public_key(X_ec);
+ if (!X_point)
+ goto fail;
+ dpp_debug_print_point("DPP: X", group, X_point);
+ M = EC_POINT_new(group);
+ Mx = BN_new();
+ My = BN_new();
+ if (!M || !Mx || !My ||
+ EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: M", group, M);
+
+ /* Initiator -> Responder: group, [identifier,] M */
+ attr_len = 4 + 2;
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
+ attr_len += 4 + 2 * curve->prime_len;
+ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
+ if (!msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
+ goto skip_finite_cyclic_group;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_finite_cyclic_group:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Code Identifier attribute */
+ if (pkex->identifier) {
+ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+ wpabuf_put_str(msg, pkex->identifier);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* M in Encrypted Key attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+ wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+out:
+ wpabuf_free(M_buf);
+ EC_KEY_free(X_ec);
+ EC_POINT_free(M);
+ EC_POINT_free(Qi);
+ BN_clear_free(Mx);
+ BN_clear_free(My);
+ BN_CTX_free(bnctx);
+ return msg;
+fail:
+ wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
+{
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const char *identifier,
+ const char *code)
+{
+ struct dpp_pkex *pkex;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ return NULL;
+ pkex->msg_ctx = msg_ctx;
+ pkex->initiator = 1;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+ pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
+ if (!pkex->exchange_req)
+ goto fail;
+ return pkex;
+fail:
+ dpp_pkex_free(pkex);
+ return NULL;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
+ enum dpp_status_error status,
+ const BIGNUM *Nx, const BIGNUM *Ny)
+{
+ struct wpabuf *msg = NULL;
+ size_t attr_len;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+ /* Initiator -> Responder: DPP Status, [identifier,] N */
+ attr_len = 4 + 1;
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
+ attr_len += 4 + 2 * curve->prime_len;
+ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
+ if (!msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ }
+
+ if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 255;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Code Identifier attribute */
+ if (pkex->identifier) {
+ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+ wpabuf_put_str(msg, pkex->identifier);
+ }
+
+ if (status != DPP_STATUS_OK)
+ goto skip_encrypted_key;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* N in Encrypted Key attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+ wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+skip_encrypted_key:
+ if (status == DPP_STATUS_BAD_GROUP) {
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+ }
+
+ return msg;
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+ const u8 *Mx, size_t Mx_len,
+ const u8 *Nx, size_t Nx_len,
+ const char *code,
+ const u8 *Kx, size_t Kx_len,
+ u8 *z, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ int res;
+ u8 *info, *pos;
+ size_t info_len;
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+
+ /* HKDF-Extract(<>, IKM=K.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+ prk, hash_len);
+ info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
+ info = os_malloc(info_len);
+ if (!info)
+ return -1;
+ pos = info;
+ os_memcpy(pos, mac_init, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, mac_resp, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, Mx, Mx_len);
+ pos += Mx_len;
+ os_memcpy(pos, Nx, Nx_len);
+ pos += Nx_len;
+ os_memcpy(pos, code, os_strlen(code));
+
+ /* HKDF-Expand(PRK, info, L) */
+ if (hash_len == 32)
+ res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else if (hash_len == 48)
+ res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else if (hash_len == 64)
+ res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else
+ res = -1;
+ os_free(info);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
+ z, hash_len);
+ return 0;
+}
+
+
+static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
+ const char *identifier)
+{
+ if (!attr_id && identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PKEX code identifier received, but expected one");
+ return 0;
+ }
+
+ if (attr_id && !identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX code identifier received, but not expecting one");
+ return 0;
+ }
+
+ if (attr_id && identifier &&
+ (os_strlen(identifier) != attr_id_len ||
+ os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+ struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const u8 *peer_mac,
+ const char *identifier,
+ const char *code,
+ const u8 *buf, size_t len)
+{
+ const u8 *attr_group, *attr_id, *attr_key;
+ u16 attr_group_len, attr_id_len, attr_key_len;
+ const struct dpp_curve_params *curve = bi->curve;
+ u16 ike_group;
+ struct dpp_pkex *pkex = NULL;
+ EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
+ BN_CTX *bnctx = NULL;
+ const EC_GROUP *group;
+ BIGNUM *Mx = NULL, *My = NULL;
+ EC_KEY *Y_ec = NULL, *X_ec = NULL;;
+ const EC_POINT *Y_point;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Kx_len;
+ int res;
+ EVP_PKEY_CTX *ctx = NULL;
+
+ if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "PKEX counter t limit reached - ignore message");
+ return NULL;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_id_len = 0;
+ attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
+ return NULL;
+
+ attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (!attr_group || attr_group_len != 2) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid Finite Cyclic Group attribute");
+ return NULL;
+ }
+ ike_group = WPA_GET_LE16(attr_group);
+ if (ike_group != curve->ike_group) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Mismatching PKEX curve: peer=%u own=%u",
+ ike_group, curve->ike_group);
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->own_bi = bi;
+ pkex->failed = 1;
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(
+ pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
+ if (!pkex->exchange_resp)
+ goto fail;
+ return pkex;
+ }
+
+ /* M in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
+ attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
+ &group);
+ if (!Qi)
+ goto fail;
+
+ /* X' = M - Qi */
+ X = EC_POINT_new(group);
+ M = EC_POINT_new(group);
+ Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!X || !M || !Mx || !My ||
+ EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, M) ||
+ !EC_POINT_is_on_curve(group, M, bnctx) ||
+ EC_POINT_invert(group, Qi, bnctx) != 1 ||
+ EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, X) ||
+ !EC_POINT_is_on_curve(group, X, bnctx)) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Invalid Encrypted Key value");
+ bi->pkex_t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: M", group, M);
+ dpp_debug_print_point("DPP: X'", group, X);
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->t = bi->pkex_t;
+ pkex->msg_ctx = msg_ctx;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+
+ os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
+
+ X_ec = EC_KEY_new();
+ if (!X_ec ||
+ EC_KEY_set_group(X_ec, group) != 1 ||
+ EC_KEY_set_public_key(X_ec, X) != 1)
+ goto fail;
+ pkex->x = EVP_PKEY_new();
+ if (!pkex->x ||
+ EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+ goto fail;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+ Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+ if (!Qr)
+ goto fail;
+
+ /* Generate a random ephemeral keypair y/Y */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key y/Y");
+ pkex->y = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->y = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->y = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->y)
+ goto fail;
+
+ /* N = Y + Qr */
+ Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
+ if (!Y_ec)
+ goto fail;
+ Y_point = EC_KEY_get0_public_key(Y_ec);
+ if (!Y_point)
+ goto fail;
+ dpp_debug_print_point("DPP: Y", group, Y_point);
+ N = EC_POINT_new(group);
+ Nx = BN_new();
+ Ny = BN_new();
+ if (!N || !Nx || !Ny ||
+ EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: N", group, N);
+
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
+ Nx, Ny);
+ if (!pkex->exchange_resp)
+ goto fail;
+
+ /* K = y * X' */
+ ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
+ Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+ pkex->Mx, curve->prime_len,
+ pkex->Nx, curve->prime_len, pkex->code,
+ Kx, Kx_len, pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ pkex->exchange_done = 1;
+
+out:
+ EVP_PKEY_CTX_free(ctx);
+ BN_CTX_free(bnctx);
+ EC_POINT_free(Qi);
+ EC_POINT_free(Qr);
+ BN_free(Mx);
+ BN_free(My);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_POINT_free(M);
+ EC_POINT_free(N);
+ EC_POINT_free(X);
+ EC_KEY_free(X_ec);
+ EC_KEY_free(Y_ec);
+ return pkex;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
+ dpp_pkex_free(pkex);
+ pkex = NULL;
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
+ const struct wpabuf *A_pub, const u8 *u)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ size_t clear_len, attr_len;
+ struct wpabuf *clear = NULL;
+ u8 *wrapped;
+ u8 octet;
+ const u8 *addr[2];
+ size_t len[2];
+
+ /* {A, u, [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* A in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(A_pub));
+ wpabuf_put_buf(clear, A_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
+ goto skip_i_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len - 1);
+ wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
+ goto skip_i_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* u in I-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+ const u8 *peer_mac,
+ const u8 *buf, size_t buflen)
+{
+ const u8 *attr_status, *attr_id, *attr_key, *attr_group;
+ u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
+ const EC_GROUP *group;
+ BN_CTX *bnctx = NULL;
+ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ EC_KEY *Y_ec = NULL;
+ size_t Jx_len, Kx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 u[DPP_MAX_HASH_LEN];
+ int res;
+
+ if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX Exchange Response");
+ pkex->failed = 1;
+ return NULL;
+ }
+
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+
+ attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
+ &attr_status_len);
+ if (!attr_status || attr_status_len != 1) {
+ dpp_pkex_fail(pkex, "No DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+
+ if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
+ attr_group = dpp_get_attr(buf, buflen,
+ DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (attr_group && attr_group_len == 2) {
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Peer indicated mismatching PKEX group - proposed %u",
+ WPA_GET_LE16(attr_group));
+ return NULL;
+ }
+ }
+
+ if (attr_status[0] != DPP_STATUS_OK) {
+ dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
+ return NULL;
+ }
+
+ attr_id_len = 0;
+ attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
+ pkex->identifier)) {
+ dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
+ return NULL;
+ }
+
+ /* N in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
+ dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
+ pkex->identifier, bnctx, &group);
+ if (!Qr)
+ goto fail;
+
+ /* Y' = N - Qr */
+ Y = EC_POINT_new(group);
+ N = EC_POINT_new(group);
+ Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!Y || !N || !Nx || !Ny ||
+ EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, N) ||
+ !EC_POINT_is_on_curve(group, N, bnctx) ||
+ EC_POINT_invert(group, Qr, bnctx) != 1 ||
+ EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, Y) ||
+ !EC_POINT_is_on_curve(group, Y, bnctx)) {
+ dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
+ pkex->t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: N", group, N);
+ dpp_debug_print_point("DPP: Y'", group, Y);
+
+ pkex->exchange_done = 1;
+
+ /* ECDH: J = a * Y’ */
+ Y_ec = EC_KEY_new();
+ if (!Y_ec ||
+ EC_KEY_set_group(Y_ec, group) != 1 ||
+ EC_KEY_set_public_key(Y_ec, Y) != 1)
+ goto fail;
+ pkex->y = EVP_PKEY_new();
+ if (!pkex->y ||
+ EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
+ goto fail;
+ ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
+ Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+ Jx, Jx_len);
+
+ /* u = HMAC(J.x, MAC-Initiator | A.x | Y’.x | X.x ) */
+ A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ if (!A_pub || !Y_pub || !X_pub)
+ goto fail;
+ addr[0] = pkex->own_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(A_pub);
+ len[1] = wpabuf_len(A_pub) / 2;
+ addr[2] = wpabuf_head(Y_pub);
+ len[2] = wpabuf_len(Y_pub) / 2;
+ addr[3] = wpabuf_head(X_pub);
+ len[3] = wpabuf_len(X_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
+
+ /* K = x * Y’ */
+ EVP_PKEY_CTX_free(ctx);
+ ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
+ Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
+ pkex->Mx, curve->prime_len,
+ attr_key /* N.x */, attr_key_len / 2,
+ pkex->code, Kx, Kx_len,
+ pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
+ if (!msg)
+ goto fail;
+
+out:
+ wpabuf_free(A_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ EC_POINT_free(Qr);
+ EC_POINT_free(Y);
+ EC_POINT_free(N);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_KEY_free(Y_ec);
+ EVP_PKEY_CTX_free(ctx);
+ BN_CTX_free(bnctx);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
+ const struct wpabuf *B_pub, const u8 *v)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 octet;
+ u8 *wrapped;
+ struct wpabuf *clear = NULL;
+ size_t clear_len, attr_len;
+
+ /* {B, v [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* B in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(B_pub));
+ wpabuf_put_buf(clear, B_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
+ goto skip_r_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len - 1);
+ wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
+ goto skip_r_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* v in R-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_r_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 1;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+ const u8 *hdr,
+ const u8 *buf, size_t buflen)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t Jx_len, Lx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ const u8 *wrapped_data, *b_key, *peer_u;
+ u16 wrapped_data_len, b_key_len, peer_u_len = 0;
+ const u8 *addr[4];
+ size_t len[4];
+ u8 octet;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+ struct wpabuf *B_pub = NULL;
+ u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Request");
+ pkex->failed = 1;
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (aes_siv_decrypt(pkex->z, curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->failed = 1;
+ pkex->t++;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+ &b_key_len);
+ if (!b_key || b_key_len != 2 * curve->prime_len) {
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+ goto fail;
+ }
+ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+ b_key_len);
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Peer bootstrap public key",
+ pkex->peer_bootstrap_key);
+
+ /* ECDH: J' = y * A' */
+ ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 ||
+ Jx_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+ Jx, Jx_len);
+
+ /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
+ A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ if (!A_pub || !Y_pub || !X_pub)
+ goto fail;
+ addr[0] = pkex->peer_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(A_pub);
+ len[1] = wpabuf_len(A_pub) / 2;
+ addr[2] = wpabuf_head(Y_pub);
+ len[2] = wpabuf_len(Y_pub) / 2;
+ addr[3] = wpabuf_head(X_pub);
+ len[3] = wpabuf_len(X_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+ goto fail;
+
+ peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &peer_u_len);
+ if (!peer_u || peer_u_len != curve->hash_len ||
+ os_memcmp(peer_u, u, curve->hash_len) != 0) {
+ dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
+ u, curve->hash_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+ pkex->t++;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
+
+ /* ECDH: L = b * X' */
+ EVP_PKEY_CTX_free(ctx);
+ ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
+ Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+ Lx, Lx_len);
+
+ /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
+ B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ if (!B_pub)
+ goto fail;
+ addr[0] = pkex->own_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(B_pub);
+ len[1] = wpabuf_len(B_pub) / 2;
+ addr[2] = wpabuf_head(X_pub);
+ len[2] = wpabuf_len(X_pub) / 2;
+ addr[3] = wpabuf_head(Y_pub);
+ len[3] = wpabuf_len(Y_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
+
+ msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
+ if (!msg)
+ goto fail;
+
+out:
+ EVP_PKEY_CTX_free(ctx);
+ os_free(unwrapped);
+ wpabuf_free(A_pub);
+ wpabuf_free(B_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX Commit-Reveal Request processing failed");
+ goto out;
+}
+
+
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+ const u8 *buf, size_t buflen)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ const u8 *wrapped_data, *b_key, *peer_v;
+ u16 wrapped_data_len, b_key_len, peer_v_len = 0;
+ const u8 *addr[4];
+ size_t len[4];
+ u8 octet;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ int ret = -1;
+ u8 v[DPP_MAX_HASH_LEN];
+ size_t Lx_len;
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ EVP_PKEY_CTX *ctx = NULL;
+ struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Response");
+ pkex->failed = 1;
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ octet = 1;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (aes_siv_decrypt(pkex->z, curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->t++;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+ &b_key_len);
+ if (!b_key || b_key_len != 2 * curve->prime_len) {
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+ goto fail;
+ }
+ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+ b_key_len);
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Peer bootstrap public key",
+ pkex->peer_bootstrap_key);
+
+ /* ECDH: L' = x * B' */
+ ctx = EVP_PKEY_CTX_new(pkex->x, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 ||
+ Lx_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+ Lx, Lx_len);
+
+ /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
+ B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ if (!B_pub || !X_pub || !Y_pub)
+ goto fail;
+ addr[0] = pkex->peer_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(B_pub);
+ len[1] = wpabuf_len(B_pub) / 2;
+ addr[2] = wpabuf_head(X_pub);
+ len[2] = wpabuf_len(X_pub) / 2;
+ addr[3] = wpabuf_head(Y_pub);
+ len[3] = wpabuf_len(Y_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+ goto fail;
+
+ peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
+ &peer_v_len);
+ if (!peer_v || peer_v_len != curve->hash_len ||
+ os_memcmp(peer_v, v, curve->hash_len) != 0) {
+ dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
+ v, curve->hash_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+ pkex->t++;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
+
+ ret = 0;
+out:
+ wpabuf_free(B_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ EVP_PKEY_CTX_free(ctx);
+ os_free(unwrapped);
+ return ret;
+fail:
+ goto out;
+}
+
+
+void dpp_pkex_free(struct dpp_pkex *pkex)
+{
+ if (!pkex)
+ return;
+
+ os_free(pkex->identifier);
+ os_free(pkex->code);
+ EVP_PKEY_free(pkex->x);
+ EVP_PKEY_free(pkex->y);
+ EVP_PKEY_free(pkex->peer_bootstrap_key);
+ wpabuf_free(pkex->exchange_req);
+ wpabuf_free(pkex->exchange_resp);
+ os_free(pkex);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+char * dpp_corrupt_connector_signature(const char *connector)
+{
+ char *tmp, *pos, *signed3 = NULL;
+ unsigned char *signature = NULL;
+ size_t signature_len = 0, signed3_len;
+
+ tmp = os_zalloc(os_strlen(connector) + 5);
+ if (!tmp)
+ goto fail;
+ os_memcpy(tmp, connector, os_strlen(connector));
+
+ pos = os_strchr(tmp, '.');
+ if (!pos)
+ goto fail;
+
+ pos = os_strchr(pos + 1, '.');
+ if (!pos)
+ goto fail;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
+ pos);
+ signature = base64_url_decode((const unsigned char *) pos,
+ os_strlen(pos), &signature_len);
+ if (!signature || signature_len == 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
+ signature, signature_len);
+ signature[signature_len - 1] ^= 0x01;
+ wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
+ signature, signature_len);
+ signed3 = (char *) base64_url_encode(signature, signature_len,
+ &signed3_len, 0);
+ if (!signed3)
+ goto fail;
+ os_memcpy(pos, signed3, signed3_len);
+ pos[signed3_len] = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
+ pos);
+
+out:
+ os_free(signature);
+ os_free(signed3);
+ return tmp;
+fail:
+ os_free(tmp);
+ tmp = NULL;
+ goto out;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/common/dpp.h b/src/common/dpp.h
new file mode 100644
index 000000000000..25759088a57e
--- /dev/null
+++ b/src/common/dpp.h
@@ -0,0 +1,435 @@
+/*
+ * DPP functionality shared between hostapd and wpa_supplicant
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_H
+#define DPP_H
+
+#include <openssl/x509.h>
+
+#include "utils/list.h"
+#include "common/wpa_common.h"
+#include "crypto/sha256.h"
+
+#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
+
+enum dpp_public_action_frame_type {
+ DPP_PA_AUTHENTICATION_REQ = 0,
+ DPP_PA_AUTHENTICATION_RESP = 1,
+ DPP_PA_AUTHENTICATION_CONF = 2,
+ DPP_PA_PEER_DISCOVERY_REQ = 5,
+ DPP_PA_PEER_DISCOVERY_RESP = 6,
+ DPP_PA_PKEX_EXCHANGE_REQ = 7,
+ DPP_PA_PKEX_EXCHANGE_RESP = 8,
+ DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
+ DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
+};
+
+enum dpp_attribute_id {
+ DPP_ATTR_STATUS = 0x1000,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002,
+ DPP_ATTR_I_PROTOCOL_KEY = 0x1003,
+ DPP_ATTR_WRAPPED_DATA = 0x1004,
+ DPP_ATTR_I_NONCE = 0x1005,
+ DPP_ATTR_I_CAPABILITIES = 0x1006,
+ DPP_ATTR_R_NONCE = 0x1007,
+ DPP_ATTR_R_CAPABILITIES = 0x1008,
+ DPP_ATTR_R_PROTOCOL_KEY = 0x1009,
+ DPP_ATTR_I_AUTH_TAG = 0x100A,
+ DPP_ATTR_R_AUTH_TAG = 0x100B,
+ DPP_ATTR_CONFIG_OBJ = 0x100C,
+ DPP_ATTR_CONNECTOR = 0x100D,
+ DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E,
+ DPP_ATTR_BOOTSTRAP_KEY = 0x100F,
+ DPP_ATTR_OWN_NET_NK_HASH = 0x1011,
+ DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012,
+ DPP_ATTR_ENCRYPTED_KEY = 0x1013,
+ DPP_ATTR_ENROLLEE_NONCE = 0x1014,
+ DPP_ATTR_CODE_IDENTIFIER = 0x1015,
+ DPP_ATTR_TRANSACTION_ID = 0x1016,
+ DPP_ATTR_BOOTSTRAP_INFO = 0x1017,
+ DPP_ATTR_CHANNEL = 0x1018,
+};
+
+enum dpp_status_error {
+ DPP_STATUS_OK = 0,
+ DPP_STATUS_NOT_COMPATIBLE = 1,
+ DPP_STATUS_AUTH_FAILURE = 2,
+ DPP_STATUS_UNWRAP_FAILURE = 3,
+ DPP_STATUS_BAD_GROUP = 4,
+ DPP_STATUS_CONFIGURE_FAILURE = 5,
+ DPP_STATUS_RESPONSE_PENDING = 6,
+ DPP_STATUS_INVALID_CONNECTOR = 7,
+ DPP_STATUS_NO_MATCH = 8,
+};
+
+#define DPP_CAPAB_ENROLLEE BIT(0)
+#define DPP_CAPAB_CONFIGURATOR BIT(1)
+#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
+
+#define DPP_BOOTSTRAP_MAX_FREQ 30
+#define DPP_MAX_NONCE_LEN 32
+#define DPP_MAX_HASH_LEN 64
+#define DPP_MAX_SHARED_SECRET_LEN 66
+
+struct dpp_curve_params {
+ const char *name;
+ size_t hash_len;
+ size_t aes_siv_key_len;
+ size_t nonce_len;
+ size_t prime_len;
+ const char *jwk_crv;
+ u16 ike_group;
+ const char *jws_alg;
+};
+
+enum dpp_bootstrap_type {
+ DPP_BOOTSTRAP_QR_CODE,
+ DPP_BOOTSTRAP_PKEX,
+};
+
+struct dpp_bootstrap_info {
+ struct dl_list list;
+ unsigned int id;
+ enum dpp_bootstrap_type type;
+ char *uri;
+ u8 mac_addr[ETH_ALEN];
+ char *info;
+ unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
+ unsigned int num_freq;
+ int own;
+ EVP_PKEY *pubkey;
+ u8 pubkey_hash[SHA256_MAC_LEN];
+ const struct dpp_curve_params *curve;
+ unsigned int pkex_t; /* number of failures before dpp_pkex
+ * instantiation */
+};
+
+#define PKEX_COUNTER_T_LIMIT 5
+
+struct dpp_pkex {
+ void *msg_ctx;
+ unsigned int initiator:1;
+ unsigned int exchange_done:1;
+ unsigned int failed:1;
+ struct dpp_bootstrap_info *own_bi;
+ u8 own_mac[ETH_ALEN];
+ u8 peer_mac[ETH_ALEN];
+ char *identifier;
+ char *code;
+ EVP_PKEY *x;
+ EVP_PKEY *y;
+ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 z[DPP_MAX_HASH_LEN];
+ EVP_PKEY *peer_bootstrap_key;
+ struct wpabuf *exchange_req;
+ struct wpabuf *exchange_resp;
+ unsigned int t; /* number of failures on code use */
+ unsigned int exch_req_wait_time;
+ unsigned int exch_req_tries;
+ unsigned int freq;
+};
+
+enum dpp_akm {
+ DPP_AKM_UNKNOWN,
+ DPP_AKM_DPP,
+ DPP_AKM_PSK,
+ DPP_AKM_SAE,
+ DPP_AKM_PSK_SAE
+};
+
+struct dpp_configuration {
+ u8 ssid[32];
+ size_t ssid_len;
+ enum dpp_akm akm;
+
+ /* For DPP configuration (connector) */
+ os_time_t netaccesskey_expiry;
+
+ /* TODO: groups */
+ char *group_id;
+
+ /* For legacy configuration */
+ char *passphrase;
+ u8 psk[32];
+};
+
+struct dpp_authentication {
+ void *msg_ctx;
+ const struct dpp_curve_params *curve;
+ struct dpp_bootstrap_info *peer_bi;
+ struct dpp_bootstrap_info *own_bi;
+ struct dpp_bootstrap_info *tmp_own_bi;
+ u8 waiting_pubkey_hash[SHA256_MAC_LEN];
+ int response_pending;
+ enum dpp_status_error auth_resp_status;
+ u8 peer_mac_addr[ETH_ALEN];
+ u8 i_nonce[DPP_MAX_NONCE_LEN];
+ u8 r_nonce[DPP_MAX_NONCE_LEN];
+ u8 e_nonce[DPP_MAX_NONCE_LEN];
+ u8 i_capab;
+ u8 r_capab;
+ EVP_PKEY *own_protocol_key;
+ EVP_PKEY *peer_protocol_key;
+ struct wpabuf *req_msg;
+ struct wpabuf *resp_msg;
+ /* Intersection of possible frequencies for initiating DPP
+ * Authentication exchange */
+ unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
+ unsigned int num_freq, freq_idx;
+ unsigned int curr_freq;
+ unsigned int neg_freq;
+ unsigned int num_freq_iters;
+ size_t secret_len;
+ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Mx_len;
+ u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Nx_len;
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Lx_len;
+ u8 k1[DPP_MAX_HASH_LEN];
+ u8 k2[DPP_MAX_HASH_LEN];
+ u8 ke[DPP_MAX_HASH_LEN];
+ int initiator;
+ int waiting_auth_resp;
+ int waiting_auth_conf;
+ int auth_req_ack;
+ unsigned int auth_resp_tries;
+ u8 allowed_roles;
+ int configurator;
+ int remove_on_tx_status;
+ int auth_success;
+ struct wpabuf *conf_req;
+ const struct wpabuf *conf_resp; /* owned by GAS server */
+ struct dpp_configuration *conf_ap;
+ struct dpp_configuration *conf_sta;
+ struct dpp_configurator *conf;
+ char *connector; /* received signedConnector */
+ u8 ssid[SSID_MAX_LEN];
+ u8 ssid_len;
+ char passphrase[64];
+ u8 psk[PMK_LEN];
+ int psk_set;
+ enum dpp_akm akm;
+ struct wpabuf *net_access_key;
+ os_time_t net_access_key_expiry;
+ struct wpabuf *c_sign_key;
+#ifdef CONFIG_TESTING_OPTIONS
+ char *config_obj_override;
+ char *discovery_override;
+ char *groups_override;
+ unsigned int ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+struct dpp_configurator {
+ struct dl_list list;
+ unsigned int id;
+ int own;
+ EVP_PKEY *csign;
+ char *kid;
+ const struct dpp_curve_params *curve;
+};
+
+struct dpp_introduction {
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+};
+
+#ifdef CONFIG_TESTING_OPTIONS
+enum dpp_test_behavior {
+ DPP_TEST_DISABLED = 0,
+ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ = 1,
+ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP = 2,
+ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF = 3,
+ DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ = 4,
+ DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP = 5,
+ DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ = 6,
+ DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP = 7,
+ DPP_TEST_ZERO_I_CAPAB = 8,
+ DPP_TEST_ZERO_R_CAPAB = 9,
+ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 10,
+ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 11,
+ DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ = 12,
+ DPP_TEST_NO_I_NONCE_AUTH_REQ = 13,
+ DPP_TEST_NO_I_CAPAB_AUTH_REQ = 14,
+ DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ = 15,
+ DPP_TEST_NO_STATUS_AUTH_RESP = 16,
+ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 17,
+ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 18,
+ DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP = 19,
+ DPP_TEST_NO_R_NONCE_AUTH_RESP = 20,
+ DPP_TEST_NO_I_NONCE_AUTH_RESP = 21,
+ DPP_TEST_NO_R_CAPAB_AUTH_RESP = 22,
+ DPP_TEST_NO_R_AUTH_AUTH_RESP = 23,
+ DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP = 24,
+ DPP_TEST_NO_STATUS_AUTH_CONF = 25,
+ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 26,
+ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 27,
+ DPP_TEST_NO_I_AUTH_AUTH_CONF = 28,
+ DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF = 29,
+ DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP = 30,
+ DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP = 31,
+ DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP = 32,
+ DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF = 33,
+ DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ = 34,
+ DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 35,
+ DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP = 36,
+ DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 37,
+ DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ = 38,
+ DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ = 39,
+ DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ = 40,
+ DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP = 41,
+ DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP = 42,
+ DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP = 43,
+ DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 44,
+ DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 45,
+ DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP = 46,
+ DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ = 47,
+ DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP = 48,
+ DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ = 49,
+ DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP = 50,
+ DPP_TEST_NO_E_NONCE_CONF_REQ = 51,
+ DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ = 52,
+ DPP_TEST_NO_WRAPPED_DATA_CONF_REQ = 53,
+ DPP_TEST_NO_E_NONCE_CONF_RESP = 54,
+ DPP_TEST_NO_CONFIG_OBJ_CONF_RESP = 55,
+ DPP_TEST_NO_STATUS_CONF_RESP = 56,
+ DPP_TEST_NO_WRAPPED_DATA_CONF_RESP = 57,
+ DPP_TEST_INVALID_STATUS_CONF_RESP = 58,
+ DPP_TEST_E_NONCE_MISMATCH_CONF_RESP = 59,
+ DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_REQ = 60,
+ DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ = 61,
+ DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP = 62,
+ DPP_TEST_NO_STATUS_PEER_DISC_RESP = 63,
+ DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP = 64,
+ DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF = 65,
+ DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ = 66,
+ DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP = 67,
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 68,
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 69,
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 70,
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 71,
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 72,
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 73,
+ DPP_TEST_INVALID_STATUS_AUTH_RESP = 74,
+ DPP_TEST_INVALID_STATUS_AUTH_CONF = 75,
+ DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ = 76,
+ DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP = 77,
+ DPP_TEST_INVALID_STATUS_PEER_DISC_RESP = 78,
+ DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP = 79,
+ DPP_TEST_INVALID_CONNECTOR_PEER_DISC_REQ = 80,
+ DPP_TEST_INVALID_I_NONCE_AUTH_REQ = 81,
+ DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_REQ = 82,
+ DPP_TEST_INVALID_E_NONCE_CONF_REQ = 83,
+ DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP = 84,
+ DPP_TEST_STOP_AT_PKEX_CR_REQ = 85,
+ DPP_TEST_STOP_AT_PKEX_CR_RESP = 86,
+ DPP_TEST_STOP_AT_AUTH_REQ = 87,
+ DPP_TEST_STOP_AT_AUTH_RESP = 88,
+ DPP_TEST_STOP_AT_AUTH_CONF = 89,
+ DPP_TEST_STOP_AT_CONF_REQ = 90,
+};
+
+extern enum dpp_test_behavior dpp_test;
+extern u8 dpp_pkex_own_mac_override[ETH_ALEN];
+extern u8 dpp_pkex_peer_mac_override[ETH_ALEN];
+extern u8 dpp_pkex_ephemeral_key_override[600];
+extern size_t dpp_pkex_ephemeral_key_override_len;
+extern u8 dpp_protocol_key_override[600];
+extern size_t dpp_protocol_key_override_len;
+extern u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+extern size_t dpp_nonce_override_len;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info);
+const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type);
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
+int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
+ const char *chan_list);
+int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac);
+int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
+struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
+char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len);
+struct hostapd_hw_modes;
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ u8 dpp_allowed_roles,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes);
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ unsigned int freq, const u8 *hdr, const u8 *attr_start,
+ size_t attr_len);
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+ const char *json);
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+ struct dpp_bootstrap_info *peer_bi);
+void dpp_configuration_free(struct dpp_configuration *conf);
+void dpp_auth_deinit(struct dpp_authentication *auth);
+struct wpabuf *
+dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
+ size_t attr_len);
+int dpp_conf_resp_rx(struct dpp_authentication *auth,
+ const struct wpabuf *resp);
+struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
+ size_t len);
+const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
+int dpp_check_attrs(const u8 *buf, size_t len);
+int dpp_key_expired(const char *timestamp, os_time_t *expiry);
+const char * dpp_akm_str(enum dpp_akm akm);
+int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
+ size_t buflen);
+void dpp_configurator_free(struct dpp_configurator *conf);
+struct dpp_configurator *
+dpp_keygen_configurator(const char *curve, const u8 *privkey,
+ size_t privkey_len);
+int dpp_configurator_own_config(struct dpp_authentication *auth,
+ const char *curve, int ap);
+enum dpp_status_error
+dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len,
+ os_time_t *expiry);
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const char *identifier,
+ const char *code);
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+ struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const u8 *peer_mac,
+ const char *identifier,
+ const char *code,
+ const u8 *buf, size_t len);
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+ const u8 *peer_mac,
+ const u8 *buf, size_t len);
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+ const u8 *hdr,
+ const u8 *buf, size_t len);
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+ const u8 *buf, size_t len);
+void dpp_pkex_free(struct dpp_pkex *pkex);
+
+char * dpp_corrupt_connector_signature(const char *connector);
+
+#endif /* DPP_H */
diff --git a/src/common/gas.c b/src/common/gas.c
index cff9254b7440..ba21b225efc9 100644
--- a/src/common/gas.c
+++ b/src/common/gas.c
@@ -75,7 +75,7 @@ gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay,
}
-static struct wpabuf *
+struct wpabuf *
gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
u16 comeback_delay, size_t size)
{
diff --git a/src/common/gas.h b/src/common/gas.h
index 306adc58c6ee..4c93e3114ccd 100644
--- a/src/common/gas.h
+++ b/src/common/gas.h
@@ -14,6 +14,9 @@ struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size);
struct wpabuf * gas_build_comeback_req(u8 dialog_token);
struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
u16 comeback_delay, size_t size);
+struct wpabuf *
+gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size);
struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
u16 comeback_delay, size_t size);
diff --git a/src/common/gas_server.c b/src/common/gas_server.c
new file mode 100644
index 000000000000..ca46758cec7c
--- /dev/null
+++ b/src/common/gas_server.c
@@ -0,0 +1,487 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/eloop.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
+#include "gas_server.h"
+
+
+#define MAX_ADV_PROTO_ID_LEN 10
+#define GAS_QUERY_TIMEOUT 10
+
+struct gas_server_handler {
+ struct dl_list list;
+ u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
+ u8 adv_proto_id_len;
+ struct wpabuf * (*req_cb)(void *ctx, const u8 *sa,
+ const u8 *query, size_t query_len);
+ void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
+ void *ctx;
+ struct gas_server *gas;
+};
+
+struct gas_server_response {
+ struct dl_list list;
+ size_t offset;
+ u8 frag_id;
+ struct wpabuf *resp;
+ int freq;
+ u8 dst[ETH_ALEN];
+ u8 dialog_token;
+ struct gas_server_handler *handler;
+};
+
+struct gas_server {
+ struct dl_list handlers; /* struct gas_server_handler::list */
+ struct dl_list responses; /* struct gas_server_response::list */
+ void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp,
+ unsigned int wait_time);
+ void *ctx;
+};
+
+static void gas_server_free_response(struct gas_server_response *response);
+
+
+static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
+{
+ struct gas_server_response *response = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR
+ " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data",
+ response, MAC2STR(response->dst), response->dialog_token,
+ response->freq, response->frag_id,
+ (unsigned long) response->offset,
+ (unsigned long) wpabuf_len(response->resp));
+ response->handler->status_cb(response->handler->ctx,
+ response->resp, 0);
+ response->resp = NULL;
+ dl_list_del(&response->list);
+ gas_server_free_response(response);
+}
+
+
+static void gas_server_free_response(struct gas_server_response *response)
+{
+ if (!response)
+ return;
+ wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response);
+ eloop_cancel_timeout(gas_server_response_timeout, response, NULL);
+ wpabuf_free(response->resp);
+ os_free(response);
+}
+
+
+static void
+gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
+ const u8 *da, int freq, u8 dialog_token,
+ struct wpabuf *query_resp)
+{
+ size_t max_len = (freq > 56160) ? 928 : 1400;
+ size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
+ size_t resp_frag_len;
+ struct wpabuf *resp;
+ u16 comeback_delay;
+ struct gas_server_response *response;
+
+ if (!query_resp)
+ return;
+
+ response = os_zalloc(sizeof(*response));
+ if (!response) {
+ wpabuf_free(query_resp);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
+ response->freq = freq;
+ response->handler = handler;
+ os_memcpy(response->dst, da, ETH_ALEN);
+ response->dialog_token = dialog_token;
+ if (hdr_len + wpabuf_len(query_resp) > max_len) {
+ /* Need to use comeback to initiate fragmentation */
+ comeback_delay = 1;
+ resp_frag_len = 0;
+ } else {
+ /* Full response fits into the initial response */
+ comeback_delay = 0;
+ resp_frag_len = wpabuf_len(query_resp);
+ }
+
+ resp = gas_build_initial_resp(dialog_token, WLAN_STATUS_SUCCESS,
+ comeback_delay,
+ handler->adv_proto_id_len +
+ resp_frag_len);
+ if (!resp) {
+ wpabuf_free(query_resp);
+ gas_server_free_response(response);
+ return;
+ }
+
+ /* Advertisement Protocol element */
+ wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+ wpabuf_put_u8(resp, 0x7f);
+ /* Advertisement Protocol ID */
+ wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
+
+ /* Query Response Length */
+ wpabuf_put_le16(resp, resp_frag_len);
+ if (!comeback_delay)
+ wpabuf_put_buf(resp, query_resp);
+
+ if (comeback_delay) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: Need to fragment query response");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "GAS: Full query response fits in the GAS Initial Response frame");
+ }
+ response->offset = resp_frag_len;
+ response->resp = query_resp;
+ dl_list_add(&gas->responses, &response->list);
+ gas->tx(gas->ctx, freq, da, resp, comeback_delay ? 2000 : 0);
+ wpabuf_free(resp);
+ eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
+ gas_server_response_timeout, response, NULL);
+}
+
+
+static int
+gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, int freq, u8 dialog_token,
+ const u8 *data, size_t len)
+{
+ const u8 *pos, *end, *adv_proto, *query_req;
+ u8 adv_proto_len;
+ u16 query_req_len;
+ struct gas_server_handler *handler;
+ struct wpabuf *resp;
+
+ wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
+ data, len);
+ pos = data;
+ end = data + len;
+
+ if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: No Advertisement Protocol element found");
+ return -1;
+ }
+ pos++;
+ adv_proto_len = *pos++;
+ if (end - pos < adv_proto_len || adv_proto_len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: Truncated Advertisement Protocol element");
+ return -1;
+ }
+
+ adv_proto = pos;
+ pos += adv_proto_len;
+ wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element",
+ adv_proto, adv_proto_len);
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field");
+ return -1;
+ }
+ query_req_len = WPA_GET_LE16(pos);
+ pos += 2;
+ if (end - pos < query_req_len) {
+ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field");
+ return -1;
+ }
+ query_req = pos;
+ pos += query_req_len;
+ wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request",
+ query_req, query_req_len);
+
+ if (pos < end) {
+ wpa_hexdump(MSG_MSGDUMP,
+ "GAS: Ignored extra data after Query Request field",
+ pos, end - pos);
+ }
+
+ dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
+ list) {
+ if (adv_proto_len < 1 + handler->adv_proto_id_len ||
+ os_memcmp(adv_proto + 1, handler->adv_proto_id,
+ handler->adv_proto_id_len) != 0)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "GAS: Calling handler for the requested Advertisement Protocol ID");
+ resp = handler->req_cb(handler->ctx, sa, query_req,
+ query_req_len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
+ resp);
+ gas_server_send_resp(gas, handler, sa, freq, dialog_token,
+ resp);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "GAS: No registered handler for the requested Advertisement Protocol ID");
+ return -1;
+}
+
+
+static void
+gas_server_handle_rx_comeback_req(struct gas_server_response *response)
+{
+ struct gas_server_handler *handler = response->handler;
+ struct gas_server *gas = handler->gas;
+ size_t max_len = (response->freq > 56160) ? 928 : 1400;
+ size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
+ size_t remaining, resp_frag_len;
+ struct wpabuf *resp;
+
+ remaining = wpabuf_len(response->resp) - response->offset;
+ if (hdr_len + remaining > max_len)
+ resp_frag_len = max_len - hdr_len;
+ else
+ resp_frag_len = remaining;
+ wpa_printf(MSG_DEBUG,
+ "GAS: Sending out %u/%u remaining Query Response octets",
+ (unsigned int) resp_frag_len, (unsigned int) remaining);
+
+ resp = gas_build_comeback_resp(response->dialog_token,
+ WLAN_STATUS_SUCCESS,
+ response->frag_id++,
+ resp_frag_len < remaining, 0,
+ handler->adv_proto_id_len +
+ resp_frag_len);
+ if (!resp) {
+ dl_list_del(&response->list);
+ gas_server_free_response(response);
+ return;
+ }
+
+ /* Advertisement Protocol element */
+ wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+ wpabuf_put_u8(resp, 0x7f);
+ /* Advertisement Protocol ID */
+ wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len);
+
+ /* Query Response Length */
+ wpabuf_put_le16(resp, resp_frag_len);
+ wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset,
+ resp_frag_len);
+
+ response->offset += resp_frag_len;
+
+ gas->tx(gas->ctx, response->freq, response->dst, resp,
+ remaining > resp_frag_len ? 2000 : 0);
+ wpabuf_free(resp);
+}
+
+
+static int
+gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, int freq, u8 dialog_token)
+{
+ struct gas_server_response *response;
+
+ dl_list_for_each(response, &gas->responses, struct gas_server_response,
+ list) {
+ if (response->dialog_token != dialog_token ||
+ os_memcmp(sa, response->dst, ETH_ALEN) != 0)
+ continue;
+ gas_server_handle_rx_comeback_req(response);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR
+ " (dialog token %u)", MAC2STR(sa), dialog_token);
+ return -1;
+}
+
+
+/**
+ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
+ * @gas: GAS query data from gas_server_init()
+ * @da: Destination MAC address of the Action frame
+ * @sa: Source MAC address of the Action frame
+ * @bssid: BSSID 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 request frame or -1 if not
+ */
+int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 categ, const u8 *data, size_t len,
+ int freq)
+{
+ u8 action, dialog_token;
+ const u8 *pos, *end;
+
+ if (!gas || len < 2)
+ return -1;
+
+ if (categ == WLAN_ACTION_PROTECTED_DUAL)
+ return -1; /* Not supported for now */
+
+ pos = data;
+ end = data + len;
+ action = *pos++;
+ dialog_token = *pos++;
+
+ if (action != WLAN_PA_GAS_INITIAL_REQ &&
+ action != WLAN_PA_GAS_COMEBACK_REQ)
+ return -1; /* Not a GAS request */
+
+ wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR
+ " SA=" MACSTR " BSSID=" MACSTR
+ " freq=%d dialog_token=%u len=%u",
+ action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback",
+ MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token,
+ (unsigned int) len);
+
+ if (action == WLAN_PA_GAS_INITIAL_REQ)
+ return gas_server_rx_initial_req(gas, da, sa, bssid,
+ freq, dialog_token,
+ pos, end - pos);
+ return gas_server_rx_comeback_req(gas, da, sa, bssid,
+ freq, dialog_token);
+}
+
+
+static void gas_server_handle_tx_status(struct gas_server_response *response,
+ int ack)
+{
+ if (ack && response->offset < wpabuf_len(response->resp)) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: More fragments remaining - keep pending entry");
+ return;
+ }
+
+ if (!ack)
+ wpa_printf(MSG_DEBUG,
+ "GAS: No ACK received - drop pending entry");
+ else
+ wpa_printf(MSG_DEBUG,
+ "GAS: Last fragment of the response sent out - drop pending entry");
+
+ response->handler->status_cb(response->handler->ctx,
+ response->resp, ack);
+ response->resp = NULL;
+ dl_list_del(&response->list);
+ gas_server_free_response(response);
+}
+
+
+void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
+ size_t data_len, int ack)
+{
+ const u8 *pos;
+ u8 action, code, dialog_token;
+ struct gas_server_response *response;
+
+ if (data_len < 24 + 3)
+ return;
+ pos = data + 24;
+ action = *pos++;
+ code = *pos++;
+ dialog_token = *pos++;
+ if (action != WLAN_ACTION_PUBLIC ||
+ (code != WLAN_PA_GAS_INITIAL_RESP &&
+ code != WLAN_PA_GAS_COMEBACK_RESP))
+ return;
+ wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR
+ " ack=%d %s dialog_token=%u",
+ MAC2STR(dst), ack,
+ code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback",
+ dialog_token);
+ dl_list_for_each(response, &gas->responses, struct gas_server_response,
+ list) {
+ if (response->dialog_token != dialog_token ||
+ os_memcmp(dst, response->dst, ETH_ALEN) != 0)
+ continue;
+ gas_server_handle_tx_status(response, ack);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status");
+}
+
+
+struct gas_server * gas_server_init(void *ctx,
+ void (*tx)(void *ctx, int freq,
+ const u8 *da,
+ struct wpabuf *buf,
+ unsigned int wait_time))
+{
+ struct gas_server *gas;
+
+ gas = os_zalloc(sizeof(*gas));
+ if (!gas)
+ return NULL;
+ gas->ctx = ctx;
+ gas->tx = tx;
+ dl_list_init(&gas->handlers);
+ dl_list_init(&gas->responses);
+ return gas;
+}
+
+
+void gas_server_deinit(struct gas_server *gas)
+{
+ struct gas_server_handler *handler, *tmp;
+ struct gas_server_response *response, *tmp_r;
+
+ if (!gas)
+ return;
+
+ dl_list_for_each_safe(handler, tmp, &gas->handlers,
+ struct gas_server_handler, list) {
+ dl_list_del(&handler->list);
+ os_free(handler);
+ }
+
+ dl_list_for_each_safe(response, tmp_r, &gas->responses,
+ struct gas_server_response, list) {
+ dl_list_del(&response->list);
+ gas_server_free_response(response);
+ }
+
+ os_free(gas);
+}
+
+
+int gas_server_register(struct gas_server *gas,
+ const u8 *adv_proto_id, u8 adv_proto_id_len,
+ struct wpabuf *
+ (*req_cb)(void *ctx, const u8 *sa,
+ const u8 *query, size_t query_len),
+ void (*status_cb)(void *ctx, struct wpabuf *resp,
+ int ok),
+ void *ctx)
+{
+ struct gas_server_handler *handler;
+
+ if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN)
+ return -1;
+ handler = os_zalloc(sizeof(*handler));
+ if (!handler)
+ return -1;
+
+ os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len);
+ handler->adv_proto_id_len = adv_proto_id_len;
+ handler->req_cb = req_cb;
+ handler->status_cb = status_cb;
+ handler->ctx = ctx;
+ handler->gas = gas;
+ dl_list_add(&gas->handlers, &handler->list);
+
+ return 0;
+}
diff --git a/src/common/gas_server.h b/src/common/gas_server.h
new file mode 100644
index 000000000000..299f529f7c56
--- /dev/null
+++ b/src/common/gas_server.h
@@ -0,0 +1,44 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERVER_H
+#define GAS_SERVER_H
+
+#ifdef CONFIG_GAS_SERVER
+
+struct gas_server;
+
+struct gas_server * gas_server_init(void *ctx,
+ void (*tx)(void *ctx, int freq,
+ const u8 *da,
+ struct wpabuf *buf,
+ unsigned int wait_time));
+void gas_server_deinit(struct gas_server *gas);
+int gas_server_register(struct gas_server *gas,
+ const u8 *adv_proto_id, u8 adv_proto_id_len,
+ struct wpabuf *
+ (*req_cb)(void *ctx, const u8 *sa,
+ const u8 *query, size_t query_len),
+ void (*status_cb)(void *ctx, struct wpabuf *resp,
+ int ok),
+ void *ctx);
+int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 categ, const u8 *data, size_t len,
+ int freq);
+void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
+ size_t data_len, int ack);
+
+#else /* CONFIG_GAS_SERVER */
+
+static inline void gas_server_deinit(struct gas_server *gas)
+{
+}
+
+#endif /* CONFIG_GAS_SERVER */
+
+#endif /* GAS_SERVER_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 9c37ea63ca87..db4037927185 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -89,7 +89,7 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
{
int ok, j, first;
int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
- 149, 157, 184, 192 };
+ 149, 157, 165, 184, 192 };
size_t k;
if (pri_chan == sec_chan || !sec_chan)
@@ -388,8 +388,10 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
/* fall through */
case VHT_CHANWIDTH_80MHZ:
data->bandwidth = 80;
- if ((vht_oper_chwidth == 1 && center_segment1) ||
- (vht_oper_chwidth == 3 && !center_segment1) ||
+ if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ &&
+ center_segment1) ||
+ (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ &&
+ !center_segment1) ||
!sec_channel_offset)
return -1;
if (!center_segment0) {
@@ -453,3 +455,101 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
return 0;
}
+
+
+void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
+ int disabled)
+{
+ /* Masking these out disables HT40 */
+ le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+ HT_CAP_INFO_SHORT_GI40MHZ);
+
+ if (disabled)
+ htcaps->ht_capabilities_info &= ~msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+}
+
+
+#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;
+}
+
+
+int ieee80211ac_cap_check(u32 hw, u32 conf)
+{
+#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_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK);
+ VHT_CAP_CHECK(VHT_CAP_RXLDPC);
+ VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
+ VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
+ VHT_CAP_CHECK(VHT_CAP_TXSTBC);
+ VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
+ VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
+ VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+ VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
+ VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
+ VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
+ VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+ VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
+ VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
+ VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
+ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
+ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+ VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+ VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
+
+#undef VHT_CAP_CHECK
+#undef VHT_CAP_CHECK_MAX
+
+ return 1;
+}
+
+#endif /* CONFIG_IEEE80211AC */
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 7360b4e3efed..9cddbd50e56a 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -35,5 +35,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
int vht_enabled, int sec_channel_offset,
int vht_oper_chwidth, int center_segment0,
int center_segment1, u32 vht_caps);
+void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
+ int disabled);
+int ieee80211ac_cap_check(u32 hw, u32 conf);
#endif /* HW_FEATURES_COMMON_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index b6bc449bf7dc..e1ef27795b99 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "defs.h"
#include "wpa_common.h"
+#include "drivers/driver.h"
#include "qca-vendor.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
@@ -120,6 +121,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->mbo = pos;
elems->mbo_len = elen;
break;
+ case HS20_ROAMING_CONS_SEL_OUI_TYPE:
+ /* Hotspot 2.0 Roaming Consortium Selection */
+ elems->roaming_cons_sel = pos;
+ elems->roaming_cons_sel_len = elen;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
@@ -179,6 +185,100 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
}
+static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
+ struct ieee802_11_elems *elems,
+ int show_errors)
+{
+ u8 ext_id;
+
+ if (elen < 1) {
+ if (show_errors) {
+ wpa_printf(MSG_MSGDUMP,
+ "short information element (Ext)");
+ }
+ return -1;
+ }
+
+ ext_id = *pos++;
+ elen--;
+
+ switch (ext_id) {
+ case WLAN_EID_EXT_ASSOC_DELAY_INFO:
+ if (elen != 1)
+ break;
+ elems->assoc_delay_info = pos;
+ break;
+ case WLAN_EID_EXT_FILS_REQ_PARAMS:
+ if (elen < 3)
+ break;
+ elems->fils_req_params = pos;
+ elems->fils_req_params_len = elen;
+ break;
+ case WLAN_EID_EXT_FILS_KEY_CONFIRM:
+ elems->fils_key_confirm = pos;
+ elems->fils_key_confirm_len = elen;
+ break;
+ case WLAN_EID_EXT_FILS_SESSION:
+ if (elen != FILS_SESSION_LEN)
+ break;
+ elems->fils_session = pos;
+ break;
+ case WLAN_EID_EXT_FILS_HLP_CONTAINER:
+ if (elen < 2 * ETH_ALEN)
+ break;
+ elems->fils_hlp = pos;
+ elems->fils_hlp_len = elen;
+ break;
+ case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
+ if (elen < 1)
+ break;
+ elems->fils_ip_addr_assign = pos;
+ elems->fils_ip_addr_assign_len = elen;
+ break;
+ case WLAN_EID_EXT_KEY_DELIVERY:
+ if (elen < WPA_KEY_RSC_LEN)
+ break;
+ elems->key_delivery = pos;
+ elems->key_delivery_len = elen;
+ break;
+ case WLAN_EID_EXT_FILS_WRAPPED_DATA:
+ elems->fils_wrapped_data = pos;
+ elems->fils_wrapped_data_len = elen;
+ break;
+ case WLAN_EID_EXT_FILS_PUBLIC_KEY:
+ if (elen < 1)
+ break;
+ elems->fils_pk = pos;
+ elems->fils_pk_len = elen;
+ break;
+ case WLAN_EID_EXT_FILS_NONCE:
+ if (elen != FILS_NONCE_LEN)
+ break;
+ elems->fils_nonce = pos;
+ break;
+ case WLAN_EID_EXT_OWE_DH_PARAM:
+ if (elen < 2)
+ break;
+ elems->owe_dh = pos;
+ elems->owe_dh_len = elen;
+ break;
+ case WLAN_EID_EXT_PASSWORD_IDENTIFIER:
+ elems->password_id = pos;
+ elems->password_id_len = elen;
+ break;
+ default:
+ if (show_errors) {
+ wpa_printf(MSG_MSGDUMP,
+ "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
+ ext_id, (unsigned int) elen);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+
/**
* ieee802_11_parse_elems - Parse information elements in management frames
* @start: Pointer to the start of IEs
@@ -262,6 +362,10 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
elems->rsn_ie_len = elen;
break;
case WLAN_EID_PWR_CAPABILITY:
+ if (elen < 2)
+ break;
+ elems->power_capab = pos;
+ elems->power_capab_len = elen;
break;
case WLAN_EID_SUPPORTED_CHANNELS:
elems->supp_channels = pos;
@@ -379,6 +483,35 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
elems->rrm_enabled = pos;
elems->rrm_enabled_len = elen;
break;
+ case WLAN_EID_CAG_NUMBER:
+ elems->cag_number = pos;
+ elems->cag_number_len = elen;
+ break;
+ case WLAN_EID_AP_CSN:
+ if (elen < 1)
+ break;
+ elems->ap_csn = pos;
+ break;
+ case WLAN_EID_FILS_INDICATION:
+ if (elen < 2)
+ break;
+ elems->fils_indic = pos;
+ elems->fils_indic_len = elen;
+ break;
+ case WLAN_EID_DILS:
+ if (elen < 2)
+ break;
+ elems->dils = pos;
+ elems->dils_len = elen;
+ break;
+ case WLAN_EID_FRAGMENT:
+ /* TODO */
+ break;
+ case WLAN_EID_EXTENSION:
+ if (ieee802_11_parse_extension(pos, elen, elems,
+ show_errors))
+ unknown++;
+ break;
default:
unknown++;
if (!show_errors)
@@ -681,6 +814,25 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
return HOSTAPD_MODE_IEEE80211A;
}
+ /* 5 GHz, channels 52..64 */
+ if (freq >= 5260 && freq <= 5320) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ if (vht_opclass)
+ *op_class = vht_opclass;
+ else if (sec_channel == 1)
+ *op_class = 119;
+ else if (sec_channel == -1)
+ *op_class = 120;
+ else
+ *op_class = 118;
+
+ *channel = (freq - 5000) / 5;
+
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
/* 5 GHz, channels 149..169 */
if (freq >= 5745 && freq <= 5845) {
if ((freq - 5000) % 5)
@@ -981,7 +1133,7 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
return -1;
return 5000 + 5 * chan;
case 129: /* center freqs 50, 114; 160 MHz */
- if (chan < 50 || chan > 114)
+ if (chan < 36 || chan > 128)
return -1;
return 5000 + 5 * chan;
case 180: /* 60 GHz band, channels 1..4 */
@@ -1031,10 +1183,24 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
}
-int ieee80211_is_dfs(int freq)
+int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
+ u16 num_modes)
{
- /* TODO: this could be more accurate to better cover all domains */
- return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
+ int i, j;
+
+ if (!modes || !num_modes)
+ return (freq >= 5260 && freq <= 5320) ||
+ (freq >= 5500 && freq <= 5700);
+
+ for (i = 0; i < num_modes; i++) {
+ for (j = 0; j < modes[i].num_channels; j++) {
+ if (modes[i].channels[j].freq == freq &&
+ (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR))
+ return 1;
+ }
+ }
+
+ return 0;
}
@@ -1295,6 +1461,40 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
}
+/**
+ * get_ie_ext - Fetch a specified extended information element from IEs buffer
+ * @ies: Information elements buffer
+ * @len: Information elements buffer length
+ * @ext: Information element extension identifier (WLAN_EID_EXT_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the IEs
+ * buffer or %NULL in case the element is not found.
+ */
+const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
+{
+ const u8 *end;
+
+ if (!ies)
+ return NULL;
+
+ end = ies + len;
+
+ while (end - ies > 1) {
+ if (2 + ies[1] > end - ies)
+ break;
+
+ if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 &&
+ ies[2] == ext)
+ return ies;
+
+ ies += 2 + ies[1];
+ }
+
+ return NULL;
+}
+
+
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
{
/*
@@ -1317,3 +1517,250 @@ size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
return 6 + attr_len;
}
+
+
+static const struct country_op_class us_op_class[] = {
+ { 1, 115 },
+ { 2, 118 },
+ { 3, 124 },
+ { 4, 121 },
+ { 5, 125 },
+ { 12, 81 },
+ { 22, 116 },
+ { 23, 119 },
+ { 24, 122 },
+ { 25, 126 },
+ { 26, 126 },
+ { 27, 117 },
+ { 28, 120 },
+ { 29, 123 },
+ { 30, 127 },
+ { 31, 127 },
+ { 32, 83 },
+ { 33, 84 },
+ { 34, 180 },
+};
+
+static const struct country_op_class eu_op_class[] = {
+ { 1, 115 },
+ { 2, 118 },
+ { 3, 121 },
+ { 4, 81 },
+ { 5, 116 },
+ { 6, 119 },
+ { 7, 122 },
+ { 8, 117 },
+ { 9, 120 },
+ { 10, 123 },
+ { 11, 83 },
+ { 12, 84 },
+ { 17, 125 },
+ { 18, 180 },
+};
+
+static const struct country_op_class jp_op_class[] = {
+ { 1, 115 },
+ { 30, 81 },
+ { 31, 82 },
+ { 32, 118 },
+ { 33, 118 },
+ { 34, 121 },
+ { 35, 121 },
+ { 36, 116 },
+ { 37, 119 },
+ { 38, 119 },
+ { 39, 122 },
+ { 40, 122 },
+ { 41, 117 },
+ { 42, 120 },
+ { 43, 120 },
+ { 44, 123 },
+ { 45, 123 },
+ { 56, 83 },
+ { 57, 84 },
+ { 58, 121 },
+ { 59, 180 },
+};
+
+static const struct country_op_class cn_op_class[] = {
+ { 1, 115 },
+ { 2, 118 },
+ { 3, 125 },
+ { 4, 116 },
+ { 5, 119 },
+ { 6, 126 },
+ { 7, 81 },
+ { 8, 83 },
+ { 9, 84 },
+};
+
+static u8
+global_op_class_from_country_array(u8 op_class, size_t array_size,
+ const struct country_op_class *country_array)
+{
+ size_t i;
+
+ for (i = 0; i < array_size; i++) {
+ if (country_array[i].country_op_class == op_class)
+ return country_array[i].global_op_class;
+ }
+
+ return 0;
+}
+
+
+u8 country_to_global_op_class(const char *country, u8 op_class)
+{
+ const struct country_op_class *country_array;
+ size_t size;
+ u8 g_op_class;
+
+ if (country_match(us_op_class_cc, country)) {
+ country_array = us_op_class;
+ size = ARRAY_SIZE(us_op_class);
+ } else if (country_match(eu_op_class_cc, country)) {
+ country_array = eu_op_class;
+ size = ARRAY_SIZE(eu_op_class);
+ } else if (country_match(jp_op_class_cc, country)) {
+ country_array = jp_op_class;
+ size = ARRAY_SIZE(jp_op_class);
+ } else if (country_match(cn_op_class_cc, country)) {
+ country_array = cn_op_class;
+ size = ARRAY_SIZE(cn_op_class);
+ } else {
+ /*
+ * Countries that do not match any of the above countries use
+ * global operating classes
+ */
+ return op_class;
+ }
+
+ g_op_class = global_op_class_from_country_array(op_class, size,
+ country_array);
+
+ /*
+ * If the given operating class did not match any of the country's
+ * operating classes, assume that global operating class is used.
+ */
+ return g_op_class ? g_op_class : op_class;
+}
+
+
+const struct oper_class_map * get_oper_class(const char *country, u8 op_class)
+{
+ const struct oper_class_map *op;
+
+ if (country)
+ op_class = country_to_global_op_class(country, op_class);
+
+ op = &global_op_class[0];
+ while (op->op_class && op->op_class != op_class)
+ op++;
+
+ if (!op->op_class)
+ return NULL;
+
+ return op;
+}
+
+
+int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
+ size_t nei_rep_len)
+{
+ u8 *nei_pos = nei_rep;
+ const char *end;
+
+ /*
+ * BSS Transition Candidate List Entries - Neighbor Report elements
+ * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
+ * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
+ */
+ 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 + nei_rep_len) {
+ 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 + nei_rep_len) {
+ 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;
+ }
+
+ return nei_pos - nei_rep;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 42f39096f86c..ff7e51de3dc9 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -11,6 +11,8 @@
#include "defs.h"
+struct hostapd_hw_modes;
+
#define MAX_NOF_MB_IES_SUPPORTED 5
struct mb_ies_info {
@@ -64,6 +66,24 @@ struct ieee802_11_elems {
const u8 *pref_freq_list;
const u8 *supp_op_classes;
const u8 *rrm_enabled;
+ const u8 *cag_number;
+ const u8 *ap_csn;
+ const u8 *fils_indic;
+ const u8 *dils;
+ const u8 *assoc_delay_info;
+ const u8 *fils_req_params;
+ const u8 *fils_key_confirm;
+ const u8 *fils_session;
+ const u8 *fils_hlp;
+ const u8 *fils_ip_addr_assign;
+ const u8 *key_delivery;
+ const u8 *fils_wrapped_data;
+ const u8 *fils_pk;
+ const u8 *fils_nonce;
+ const u8 *owe_dh;
+ const u8 *power_capab;
+ const u8 *roaming_cons_sel;
+ const u8 *password_id;
u8 ssid_len;
u8 supp_rates_len;
@@ -96,6 +116,20 @@ struct ieee802_11_elems {
u8 pref_freq_list_len;
u8 supp_op_classes_len;
u8 rrm_enabled_len;
+ u8 cag_number_len;
+ u8 fils_indic_len;
+ u8 dils_len;
+ u8 fils_req_params_len;
+ u8 fils_key_confirm_len;
+ u8 fils_hlp_len;
+ u8 fils_ip_addr_assign_len;
+ u8 key_delivery_len;
+ u8 fils_wrapped_data_len;
+ u8 fils_pk_len;
+ u8 owe_dh_len;
+ u8 power_capab_len;
+ u8 roaming_cons_sel_len;
+ u8 password_id_len;
struct mb_ies_info mb_ies;
};
@@ -126,7 +160,8 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
int sec_channel, int vht,
u8 *op_class, u8 *channel);
-int ieee80211_is_dfs(int freq);
+int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
+ u16 num_modes);
enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
int supp_rates_11b_only(struct ieee802_11_elems *elems);
@@ -150,7 +185,20 @@ extern const struct oper_class_map global_op_class[];
extern size_t global_op_class_size;
const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
+const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
+struct country_op_class {
+ u8 country_op_class;
+ u8 global_op_class;
+};
+
+u8 country_to_global_op_class(const char *country, u8 op_class);
+
+const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
+
+int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
+ size_t nei_rep_len);
+
#endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index d453aec790ad..762e731ab022 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -81,6 +81,9 @@
#define WLAN_AUTH_SHARED_KEY 1
#define WLAN_AUTH_FT 2
#define WLAN_AUTH_SAE 3
+#define WLAN_AUTH_FILS_SK 4
+#define WLAN_AUTH_FILS_SK_PFS 5
+#define WLAN_AUTH_FILS_PK 6
#define WLAN_AUTH_LEAP 128
#define WLAN_AUTH_CHALLENGE_LEN 128
@@ -102,7 +105,7 @@
#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14)
#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15)
-/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
+/* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */
#define WLAN_STATUS_SUCCESS 0
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
@@ -119,27 +122,23 @@
#define WLAN_STATUS_AUTH_TIMEOUT 16
#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
#define WLAN_STATUS_ASSOC_DENIED_RATES 18
-/* IEEE 802.11b */
#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
-#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
-#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
-/* IEEE 802.11h */
#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
-/* IEEE 802.11g */
#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
-#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26
#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
#define WLAN_STATUS_R0KH_UNREACHABLE 28
#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
-/* IEEE 802.11w */
#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
+#define WLAN_STATUS_DENIED_INSUFFICIENT_BANDWIDTH 33
+#define WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS 34
+#define WLAN_STATUS_DENIED_QOS_NOT_SUPPORTED 35
#define WLAN_STATUS_REQUEST_DECLINED 37
#define WLAN_STATUS_INVALID_PARAMETERS 38
-/* IEEE 802.11i */
+#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39
#define WLAN_STATUS_INVALID_IE 40
#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
@@ -152,11 +151,13 @@
#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
-/* IEEE 802.11r */
#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
#define WLAN_STATUS_INVALID_PMKID 53
#define WLAN_STATUS_INVALID_MDIE 54
#define WLAN_STATUS_INVALID_FTIE 55
+#define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56
+#define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57
+#define WLAN_STATUS_TRY_ANOTHER_BSS 58
#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
@@ -167,16 +168,44 @@
#define WLAN_STATUS_REQ_REFUSED_SSPN 67
#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
#define WLAN_STATUS_INVALID_RSNIE 72
+#define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73
+#define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74
+#define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75
#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
+#define WLAN_STATUS_CANNOT_FIND_ALT_TBTT 78
#define WLAN_STATUS_TRANSMISSION_FAILURE 79
+#define WLAN_STATUS_REQ_TCLAS_NOT_SUPPORTED 80
+#define WLAN_STATUS_TCLAS_RESOURCES_EXCHAUSTED 81
#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
+#define WLAN_STATUS_REJECT_WITH_SCHEDULE 83
+#define WLAN_STATUS_REJECT_NO_WAKEUP_SPECIFIED 84
+#define WLAN_STATUS_SUCCESS_POWER_SAVE_MODE 85
#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
+#define WLAN_STATUS_PERFORMING_FST_NOW 87
+#define WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW 88
+#define WLAN_STATUS_REJECT_U_PID_SETTING 89
+#define WLAN_STATUS_REFUSED_EXTERNAL_REASON 92
+#define WLAN_STATUS_REFUSED_AP_OUT_OF_MEMORY 93
+#define WLAN_STATUS_REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED 94
#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_REJECT_DSE_BAND 96
+#define WLAN_STATUS_TCLAS_PROCESSING_TERMINATED 97
+#define WLAN_STATUS_TS_SCHEDULE_CONFLICT 98
#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
+#define WLAN_STATUS_MCCAOP_RESERVATION_CONFLICT 100
+#define WLAN_STATUS_MAF_LIMIT_EXCEEDED 101
+#define WLAN_STATUS_MCCA_TRACK_LIMIT_EXCEEDED 102
+#define WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT 103
#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
-
-/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
+#define WLAN_STATUS_ENABLEMENT_DENIED 105
+#define WLAN_STATUS_RESTRICTION_FROM_AUTHORIZED_GDB 106
+#define WLAN_STATUS_AUTHORIZATION_DEENABLED 107
+#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
+#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
+#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
+
+/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
#define WLAN_REASON_UNSPECIFIED 1
#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
#define WLAN_REASON_DEAUTH_LEAVING 3
@@ -186,10 +215,9 @@
#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
-/* IEEE 802.11h */
#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
-/* IEEE 802.11i */
+#define WLAN_REASON_BSS_TRANSITION_DISASSOC 12
#define WLAN_REASON_INVALID_IE 13
#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
@@ -204,9 +232,26 @@
#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
-/* IEEE 802.11e */
+#define WLAN_REASON_SSP_REQUESTED_DISASSOC 27
+#define WLAN_REASON_NO_SSP_ROAMING_AGREEMENT 28
+#define WLAN_REASON_BAD_CIPHER_OR_AKM 29
+#define WLAN_REASON_NOT_AUTHORIZED_THIS_LOCATION 30
+#define WLAN_REASON_SERVICE_CHANGE_PRECLUDES_TS 31
+#define WLAN_REASON_UNSPECIFIED_QOS_REASON 32
+#define WLAN_REASON_NOT_ENOUGH_BANDWIDTH 33
#define WLAN_REASON_DISASSOC_LOW_ACK 34
-/* IEEE 802.11s */
+#define WLAN_REASON_EXCEEDED_TXOP 35
+#define WLAN_REASON_STA_LEAVING 36
+#define WLAN_REASON_END_TS_BA_DLS 37
+#define WLAN_REASON_UNKNOWN_TS_BA 38
+#define WLAN_REASON_TIMEOUT 39
+#define WLAN_REASON_PEERKEY_MISMATCH 45
+#define WLAN_REASON_AUTHORIZED_ACCESS_LIMIT_REACHED 46
+#define WLAN_REASON_EXTERNAL_SERVICE_REQUIREMENTS 47
+#define WLAN_REASON_INVALID_FT_ACTION_FRAME_COUNT 48
+#define WLAN_REASON_INVALID_PMKID 49
+#define WLAN_REASON_INVALID_MDE 50
+#define WLAN_REASON_INVALID_FTE 51
#define WLAN_REASON_MESH_PEERING_CANCELLED 52
#define WLAN_REASON_MESH_MAX_PEERS 53
#define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54
@@ -216,20 +261,29 @@
#define WLAN_REASON_MESH_INVALID_GTK 58
#define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59
#define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60
+#define WLAN_REASON_MESH_PATH_ERROR_NO_PROXY_INFO 61
+#define WLAN_REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO 62
+#define WLAN_REASON_MESH_PATH_ERROR_DEST_UNREACHABLE 63
+#define WLAN_REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS 64
+#define WLAN_REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ 65
+#define WLAN_REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED 66
-/* Information Element IDs */
+/* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */
#define WLAN_EID_SSID 0
#define WLAN_EID_SUPP_RATES 1
-#define WLAN_EID_FH_PARAMS 2
#define WLAN_EID_DS_PARAMS 3
#define WLAN_EID_CF_PARAMS 4
#define WLAN_EID_TIM 5
#define WLAN_EID_IBSS_PARAMS 6
#define WLAN_EID_COUNTRY 7
+#define WLAN_EID_REQUEST 10
#define WLAN_EID_BSS_LOAD 11
+#define WLAN_EID_EDCA_PARAM_SET 12
+#define WLAN_EID_TSPEC 13
+#define WLAN_EID_TCLAS 14
+#define WLAN_EID_SCHEDULE 15
#define WLAN_EID_CHALLENGE 16
-/* EIDs defined by IEEE 802.11h - START */
#define WLAN_EID_PWR_CONSTRAINT 32
#define WLAN_EID_PWR_CAPABILITY 33
#define WLAN_EID_TPC_REQUEST 34
@@ -238,50 +292,139 @@
#define WLAN_EID_CHANNEL_SWITCH 37
#define WLAN_EID_MEASURE_REQUEST 38
#define WLAN_EID_MEASURE_REPORT 39
-#define WLAN_EID_QUITE 40
+#define WLAN_EID_QUIET 40
#define WLAN_EID_IBSS_DFS 41
-/* EIDs defined by IEEE 802.11h - END */
#define WLAN_EID_ERP_INFO 42
+#define WLAN_EID_TS_DELAY 43
+#define WLAN_EID_TCLAS_PROCESSING 44
#define WLAN_EID_HT_CAP 45
#define WLAN_EID_QOS 46
#define WLAN_EID_RSN 48
#define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_AP_CHANNEL_REPORT 51
#define WLAN_EID_NEIGHBOR_REPORT 52
+#define WLAN_EID_RCPI 53
#define WLAN_EID_MOBILITY_DOMAIN 54
#define WLAN_EID_FAST_BSS_TRANSITION 55
#define WLAN_EID_TIMEOUT_INTERVAL 56
#define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_DSE_REGISTERED_LOCATION 58
#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
#define WLAN_EID_EXT_CHANSWITCH_ANN 60
#define WLAN_EID_HT_OPERATION 61
#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
-#define WLAN_EID_WAPI 68
+#define WLAN_EID_BSS_AVERAGE_ACCESS_DELAY 63
+#define WLAN_EID_ANTENNA 64
+#define WLAN_EID_RSNI 65
+#define WLAN_EID_MEASUREMENT_PILOT_TRANSMISSION 66
+#define WLAN_EID_BSS_AVAILABLE_ADM_CAPA 67
+#define WLAN_EID_BSS_AC_ACCESS_DELAY 68 /* note: also used by WAPI */
#define WLAN_EID_TIME_ADVERTISEMENT 69
#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
+#define WLAN_EID_MULTIPLE_BSSID 71
#define WLAN_EID_20_40_BSS_COEXISTENCE 72
#define WLAN_EID_20_40_BSS_INTOLERANT 73
#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
+#define WLAN_EID_RIC_DESCRIPTOR 75
#define WLAN_EID_MMIE 76
+#define WLAN_EID_EVENT_REQUEST 78
+#define WLAN_EID_EVENT_REPORT 79
+#define WLAN_EID_DIAGNOSTIC_REQUEST 80
+#define WLAN_EID_DIAGNOSTIC_REPORT 81
+#define WLAN_EID_LOCATION_PARAMETERS 82
+#define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83
#define WLAN_EID_SSID_LIST 84
+#define WLAN_EID_MLTIPLE_BSSID_INDEX 85
+#define WLAN_EID_FMS_DESCRIPTOR 86
+#define WLAN_EID_FMS_REQUEST 87
+#define WLAN_EID_FMS_RESPONSE 88
+#define WLAN_EID_QOS_TRAFFIC_CAPABILITY 89
#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
#define WLAN_EID_TFS_REQ 91
#define WLAN_EID_TFS_RESP 92
#define WLAN_EID_WNMSLEEP 93
+#define WLAN_EID_TIM_BROADCAST_REQUEST 94
+#define WLAN_EID_TIM_BROADCAST_RESPONSE 95
+#define WLAN_EID_COLLOCATED_INTERFERENCE_REPORT 96
+#define WLAN_EID_CHANNEL_USAGE 97
#define WLAN_EID_TIME_ZONE 98
+#define WLAN_EID_DMS_REQUEST 99
+#define WLAN_EID_DMS_RESPONSE 100
#define WLAN_EID_LINK_ID 101
+#define WLAN_EID_WAKEUP_SCHEDULE 102
+#define WLAN_EID_CHANNEL_SWITCH_TIMING 104
+#define WLAN_EID_PTI_CONTROL 105
+#define WLAN_EID_TPU_BUFFER_STATUS 106
#define WLAN_EID_INTERWORKING 107
#define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_EXPEDITED_BANDWIDTH_REQ 109
#define WLAN_EID_QOS_MAP_SET 110
#define WLAN_EID_ROAMING_CONSORTIUM 111
+#define WLAN_EID_EMERGENCY_ALERT_ID 112
#define WLAN_EID_MESH_CONFIG 113
#define WLAN_EID_MESH_ID 114
+#define WLAN_EID_MESH_LINK_METRIC_REPORT 115
+#define WLAN_EID_CONGESTION_NOTIFICATION 116
#define WLAN_EID_PEER_MGMT 117
+#define WLAN_EID_MESH_CHANNEL_SWITCH_PARAMETERS 118
+#define WLAN_EID_MESH_AWAKE_WINDOW 119
+#define WLAN_EID_BEACON_TIMING 120
+#define WLAN_EID_MCCAOP_SETUP_REQUEST 121
+#define WLAN_EID_MCCAOP_SETUP_REPLY 122
+#define WLAN_EID_MCCAOP_ADVERTISEMENT 123
+#define WLAN_EID_MCCAOP_TEARDOWN 124
+#define WLAN_EID_GANN 125
+#define WLAN_EID_RANN 126
#define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_PREQ 130
+#define WLAN_EID_PREP 131
+#define WLAN_EID_PERR 132
+#define WLAN_EID_PXU 137
+#define WLAN_EID_PXUC 138
#define WLAN_EID_AMPE 139
#define WLAN_EID_MIC 140
+#define WLAN_EID_DESTINATION_URI 141
+#define WLAN_EID_U_APSD_COEX 142
+#define WLAN_EID_DMG_WAKEUP_SCHEDULE 143
+#define WLAN_EID_EXTENDED_SCHEDULE 144
+#define WLAN_EID_STA_AVAILABILITY 145
+#define WLAN_EID_DMG_TSPEC 146
+#define WLAN_EID_NEXT_DMG_ATI 147
+#define WLAN_EID_DMG_CAPABILITIES 148
+#define WLAN_EID_DMG_OPERATION 151
+#define WLAN_EID_DMG_BSS_PARAMETER_CHANGE 152
+#define WLAN_EID_DMG_BEAM_REFINEMENT 153
+#define WLAN_EID_CHANNEL_MEASUREMENT_FEEDBACK 154
#define WLAN_EID_CCKM 156
+#define WLAN_EID_AWAKE_WINDOW 157
#define WLAN_EID_MULTI_BAND 158
+#define WLAN_EID_ADDBA_EXTENSION 159
+#define WLAN_EID_NEXTPCP_LIST 160
+#define WLAN_EID_PCP_HANDOVER 161
+#define WLAN_EID_DMG_LINK_MARGIN 162
+#define WLAN_EID_SWITCHING_STREAM 163
#define WLAN_EID_SESSION_TRANSITION 164
+#define WLAN_EID_DYNAMIC_TONE_PAIRING_REPORT 165
+#define WLAN_EID_CLUSTER_REPORT 166
+#define WLAN_EID_REPLAY_CAPABILITIES 167
+#define WLAN_EID_RELAY_TRANSFER_PARAM_SET 168
+#define WLAN_EID_BEAMLINK_MAINTENANCE 169
+#define WLAN_EID_MULTIPLE_MAC_SUBLAYERS 170
+#define WLAN_EID_U_PID 171
+#define WLAN_EID_DMG_LINK_ADAPTATION_ACK 172
+#define WLAN_EID_MCCAOP_ADVERTISEMENT_OVERVIEW 174
+#define WLAN_EID_QUIET_PERIOD_REQUEST 175
+#define WLAN_EID_QUIET_PERIOD_RESPONSE 177
+#define WLAN_EID_QMF_POLICY 181
+#define WLAN_EID_ECAPC_POLICY 182
+#define WLAN_EID_CLUSTER_TIME_OFFSET 183
+#define WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY 184
+#define WLAN_EID_SCS_DESCRIPTOR 185
+#define WLAN_EID_QLOAD_REPORT 186
+#define WLAN_EID_HCCA_TXOP_UPDATE_COUNT 187
+#define WLAN_EID_HIGHER_LAYER_STREAM_ID 188
+#define WLAN_EID_GCR_GROUP_ADDRESS 189
+#define WLAN_EID_ANTENNA_SECTOR_ID_PATTERN 190
#define WLAN_EID_VHT_CAP 191
#define WLAN_EID_VHT_OPERATION 192
#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
@@ -291,10 +434,42 @@
#define WLAN_EID_VHT_AID 197
#define WLAN_EID_VHT_QUIET_CHANNEL 198
#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
+#define WLAN_EID_UPSIM 200
+#define WLAN_EID_REDUCED_NEIGHBOR_REPORT 201
+#define WLAN_EID_TVHT_OPERATION 202
+#define WLAN_EID_DEVICE_LOCATION 204
+#define WLAN_EID_WHITE_SPACE_MAP 205
+#define WLAN_EID_FTM_PARAMETERS 206
#define WLAN_EID_VENDOR_SPECIFIC 221
-
-
-/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
+#define WLAN_EID_CAG_NUMBER 237
+#define WLAN_EID_AP_CSN 239
+#define WLAN_EID_FILS_INDICATION 240
+#define WLAN_EID_DILS 241
+#define WLAN_EID_FRAGMENT 242
+#define WLAN_EID_EXTENSION 255
+
+/* Element ID Extension (EID 255) values */
+#define WLAN_EID_EXT_ASSOC_DELAY_INFO 1
+#define WLAN_EID_EXT_FILS_REQ_PARAMS 2
+#define WLAN_EID_EXT_FILS_KEY_CONFIRM 3
+#define WLAN_EID_EXT_FILS_SESSION 4
+#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5
+#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6
+#define WLAN_EID_EXT_KEY_DELIVERY 7
+#define WLAN_EID_EXT_FILS_WRAPPED_DATA 8
+#define WLAN_EID_EXT_FTM_SYNC_INFO 9
+#define WLAN_EID_EXT_EXTENDED_REQUEST 10
+#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11
+#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12
+#define WLAN_EID_EXT_FILS_NONCE 13
+#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
+#define WLAN_EID_EXT_OWE_DH_PARAM 32
+#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33
+#define WLAN_EID_EXT_HE_CAPABILITIES 35
+#define WLAN_EID_EXT_HE_OPERATION 36
+
+
+/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
#define WLAN_ACTION_SPECTRUM_MGMT 0
#define WLAN_ACTION_QOS 1
#define WLAN_ACTION_DLS 2
@@ -308,21 +483,59 @@
#define WLAN_ACTION_WNM 10
#define WLAN_ACTION_UNPROTECTED_WNM 11
#define WLAN_ACTION_TDLS 12
+#define WLAN_ACTION_MESH 13
+#define WLAN_ACTION_MULTIHOP 14
#define WLAN_ACTION_SELF_PROTECTED 15
+#define WLAN_ACTION_DMG 16
#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
#define WLAN_ACTION_FST 18
+#define WLAN_ACTION_ROBUST_AV_STREAMING 19
+#define WLAN_ACTION_UNPROTECTED_DMG 20
+#define WLAN_ACTION_VHT 21
+#define WLAN_ACTION_FILS 26
+#define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126
#define WLAN_ACTION_VENDOR_SPECIFIC 127
+/* Note: 128-255 used to report errors by setting category | 0x80 */
-/* Public action codes */
+/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */
#define WLAN_PA_20_40_BSS_COEX 0
+#define WLAN_PA_DSE_ENABLEMENT 1
+#define WLAN_PA_DSE_DEENABLEMENT 2
+#define WLAN_PA_DSE_REG_LOCATION_ANNOUNCE 3
+#define WLAN_PA_EXT_CHANNEL_SWITCH_ANNOUNCE 4
+#define WLAN_PA_DSE_MEASUREMENT_REQ 5
+#define WLAN_PA_DSE_MEASUREMENT_RESP 6
+#define WLAN_PA_MEASUREMENT_PILOT 7
+#define WLAN_PA_DSE_POWER_CONSTRAINT 8
#define WLAN_PA_VENDOR_SPECIFIC 9
#define WLAN_PA_GAS_INITIAL_REQ 10
#define WLAN_PA_GAS_INITIAL_RESP 11
#define WLAN_PA_GAS_COMEBACK_REQ 12
#define WLAN_PA_GAS_COMEBACK_RESP 13
#define WLAN_TDLS_DISCOVERY_RESPONSE 14
-
-/* Protected Dual of Public Action frames */
+#define WLAN_PA_LOCATION_TRACK_NOTIFICATION 15
+#define WLAN_PA_QAB_REQUEST_FRAME 16
+#define WLAN_PA_QAB_RESPONSE_FRAME 17
+#define WLAN_PA_QMF_POLICY 18
+#define WLAN_PA_QMF_POLICY_CHANGE 19
+#define WLAN_PA_QLOAD_REQUEST 20
+#define WLAN_PA_QLOAD_REPORT 21
+#define WLAN_PA_HCCA_TXOP_ADVERTISEMENT 22
+#define WLAN_PA_HCCA_TXOP_RESPONSE 23
+#define WLAN_PA_PUBLIC_KEY 24
+#define WLAN_PA_CHANNEL_AVAILABILITY_QUERY 25
+#define WLAN_PA_CHANNEL_SCHEDULE_MANAGEMENT 26
+#define WLAN_PA_CONTACT_VERIFICATION_SIGNAL 27
+#define WLAN_PA_GDD_ENABLEMENT_REQ 28
+#define WLAN_PA_GDD_ENABLEMENT_RESP 29
+#define WLAN_PA_NETWORK_CHANNEL_CONTROL 30
+#define WLAN_PA_WHITE_SPACE_MAP_ANNOUNCEMENT 31
+#define WLAN_PA_FTM_REQUEST 32
+#define WLAN_PA_FTM 33
+#define WLAN_PA_FILS_DISCOVERY 34
+
+/* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11,
+ * Table 9-332) */
#define WLAN_PROT_DSE_ENABLEMENT 1
#define WLAN_PROT_DSE_DEENABLEMENT 2
#define WLAN_PROT_EXT_CSA 4
@@ -334,6 +547,21 @@
#define WLAN_PROT_GAS_INITIAL_RESP 11
#define WLAN_PROT_GAS_COMEBACK_REQ 12
#define WLAN_PROT_GAS_COMEBACK_RESP 13
+#define WLAN_PROT_QAB_REQUEST_FRAME 16
+#define WLAN_PROT_QAB_RESPONSE_FRAME 17
+#define WLAN_PROT_QMF_POLICY 18
+#define WLAN_PROT_QMF_POLICY_CHANGE 19
+#define WLAN_PROT_QLOAD_REQUEST 20
+#define WLAN_PROT_QLOAD_REPORT 21
+#define WLAN_PROT_HCCA_TXOP_ADVERTISEMENT 22
+#define WLAN_PROT_HCCA_TXOP_RESPONSE 23
+#define WLAN_PROT_CHANNEL_AVAILABILITY_QUERY 25
+#define WLAN_PROT_CHANNEL_SCHEDULE_MANAGEMENT 26
+#define WLAN_PROT_CONTACT_VERIFICATION_SIGNAL 27
+#define WLAN_PROT_GDD_ENABLEMENT_REQ 28
+#define WLAN_PROT_GDD_ENABLEMENT_RESP 29
+#define WLAN_PROT_NETWORK_CHANNEL_CONTROL 30
+#define WLAN_PROT_WHITE_SPACE_MAP_ANNOUNCEMENT 31
/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
#define WLAN_SA_QUERY_REQUEST 0
@@ -362,10 +590,14 @@
#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
-/* Radio Measurement capabilities (from RRM Capabilities IE) */
+/* Radio Measurement capabilities (from RM Enabled Capabilities element)
+ * IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */
/* byte 1 (out of 5) */
#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
+#define WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE BIT(4)
+#define WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE BIT(5)
+#define WLAN_RRM_CAPS_BEACON_REPORT_TABLE BIT(6)
/* byte 2 (out of 5) */
#define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4)
/* byte 5 (out of 5) */
@@ -398,16 +630,18 @@
#define INTERWORKING_ANT_TEST 6
#define INTERWORKING_ANT_WILDCARD 15
-/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */
+/* Advertisement Protocol ID definitions (IEEE Std 802.11-2016, Table 9-215) */
enum adv_proto_id {
ACCESS_NETWORK_QUERY_PROTOCOL = 0,
MIH_INFO_SERVICE = 1,
MIH_CMD_AND_EVENT_DISCOVERY = 2,
EMERGENCY_ALERT_SYSTEM = 3,
+ REGISTERED_LOCATION_QUERY_PROTO = 4,
ADV_PROTO_VENDOR_SPECIFIC = 221
};
-/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */
+/* Access Network Query Protocol info ID definitions (IEEE Std 802.11-2016,
+ * Table 9-271; P802.11ai) */
enum anqp_info_id {
ANQP_QUERY_LIST = 256,
ANQP_CAPABILITY_LIST = 257,
@@ -426,9 +660,14 @@ enum anqp_info_id {
ANQP_TDLS_CAPABILITY = 270,
ANQP_EMERGENCY_NAI = 271,
ANQP_NEIGHBOR_REPORT = 272,
+ ANQP_QUERY_AP_LIST = 273,
+ ANQP_AP_LIST_RESPONSE = 274,
+ ANQP_FILS_REALM_INFO = 275,
+ ANQP_CAG = 276,
ANQP_VENUE_URL = 277,
ANQP_ADVICE_OF_CHARGE = 278,
ANQP_LOCAL_CONTENT = 279,
+ ANQP_NETWORK_AUTH_TYPE_TIMESTAMP = 280,
ANQP_VENDOR_SPECIFIC = 56797
};
@@ -505,6 +744,11 @@ enum lci_req_subelem {
LCI_REQ_SUBELEM_MAX_AGE = 4,
};
+#define FILS_NONCE_LEN 16
+#define FILS_SESSION_LEN 8
+#define FILS_CACHE_ID_LEN 2
+#define FILS_MAX_KEY_AUTH_LEN 48
+
#ifdef _MSC_VER
#pragma pack(push, 1)
#endif /* _MSC_VER */
@@ -678,6 +922,16 @@ struct ieee80211_mgmt {
u8 variable[];
} STRUCT_PACKED bss_tm_query;
struct {
+ u8 action; /* 11 */
+ u8 dialog_token;
+ u8 req_info;
+ } STRUCT_PACKED coloc_intf_req;
+ struct {
+ u8 action; /* 12 */
+ u8 dialog_token;
+ u8 variable[];
+ } STRUCT_PACKED coloc_intf_report;
+ struct {
u8 action; /* 15 */
u8 variable[];
} STRUCT_PACKED slf_prot_action;
@@ -887,6 +1141,7 @@ struct ieee80211_ampe_ie {
#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2))
#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3))
#define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3))
+#define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT 2
#define VHT_CAP_RXLDPC ((u32) BIT(4))
#define VHT_CAP_SHORT_GI_80 ((u32) BIT(5))
#define VHT_CAP_SHORT_GI_160 ((u32) BIT(6))
@@ -953,6 +1208,8 @@ struct ieee80211_ampe_ie {
#define OSEN_IE_VENDOR_TYPE 0x506f9a12
#define MBO_IE_VENDOR_TYPE 0x506f9a16
#define MBO_OUI_TYPE 22
+#define OWE_IE_VENDOR_TYPE 0x506f9a1c
+#define OWE_OUI_TYPE 28
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -1072,6 +1329,7 @@ enum wmm_ac {
#define HS20_INDICATION_OUI_TYPE 16
#define HS20_ANQP_OUI_TYPE 17
#define HS20_OSEN_OUI_TYPE 18
+#define HS20_ROAMING_CONS_SEL_OUI_TYPE 29
#define HS20_STYPE_QUERY_LIST 1
#define HS20_STYPE_CAPABILITY_LIST 2
#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
@@ -1082,21 +1340,27 @@ enum wmm_ac {
#define HS20_STYPE_OSU_PROVIDERS_LIST 8
#define HS20_STYPE_ICON_REQUEST 10
#define HS20_STYPE_ICON_BINARY_FILE 11
+#define HS20_STYPE_OPERATOR_ICON_METADATA 12
+#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13
#define HS20_DGAF_DISABLED 0x01
#define HS20_PPS_MO_ID_PRESENT 0x02
#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
+#ifndef HS20_VERSION
#define HS20_VERSION 0x10 /* Release 2 */
+#endif /* HS20_VERSION */
/* WNM-Notification WFA vendors specific subtypes */
#define HS20_WNM_SUB_REM_NEEDED 0
#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
+#define HS20_WNM_T_C_ACCEPTANCE 2
#define HS20_DEAUTH_REASON_CODE_BSS 0
#define HS20_DEAUTH_REASON_CODE_ESS 1
/* MBO v0.0_r19, 4.2: MBO Attributes */
/* Table 4-5: MBO Attributes */
+/* OCE v0.0.10, Table 4-3: OCE Attributes */
enum mbo_attr_id {
MBO_ATTR_ID_AP_CAPA_IND = 1,
MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2,
@@ -1106,6 +1370,10 @@ enum mbo_attr_id {
MBO_ATTR_ID_TRANSITION_REASON = 6,
MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7,
MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8,
+ OCE_ATTR_ID_CAPA_IND = 101,
+ OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102,
+ OCE_ATTR_ID_REDUCED_WAN_METRICS = 103,
+ OCE_ATTR_ID_RNR_COMPLETENESS = 104,
};
/* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */
@@ -1180,9 +1448,17 @@ enum wfa_wnm_notif_subelem_id {
WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3,
};
-/* MBO v0.0_r25, 4.3: MBO ANQP-elements */
+/* MBO v0.0_r27, 4.3: MBO ANQP-elements */
#define MBO_ANQP_OUI_TYPE 0x12
-#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1
+#define MBO_ANQP_SUBTYPE_QUERY_LIST 1
+#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 2
+#define MAX_MBO_ANQP_SUBTYPE MBO_ANQP_SUBTYPE_CELL_CONN_PREF
+
+/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */
+#define OCE_RELEASE 1
+#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define OCE_IS_STA_CFON BIT(3)
+#define OCE_IS_NON_OCE_AP_PRESENT BIT(4)
/* Wi-Fi Direct (P2P) */
@@ -1331,7 +1607,9 @@ enum wifi_display_subelem {
WFD_SUBELEM_COUPLED_SINK = 6,
WFD_SUBELEM_EXT_CAPAB = 7,
WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
- WFD_SUBELEM_SESSION_INFO = 9
+ WFD_SUBELEM_SESSION_INFO = 9,
+ WFD_SUBELEM_MAC_INFO = 10,
+ WFD_SUBELEM_R2_DEVICE_INFO = 11,
};
/* 802.11s */
@@ -1363,41 +1641,6 @@ enum plink_action_field {
#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
-/* cipher suite selectors */
-#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
-#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01
-#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02
-/* reserved: 0x000FAC03 */
-#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04
-#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05
-#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06
-#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07
-#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08
-#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09
-#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A
-#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B
-#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C
-#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D
-
-#define WLAN_CIPHER_SUITE_SMS4 0x00147201
-
-#define WLAN_CIPHER_SUITE_CKIP 0x00409600
-#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601
-#define WLAN_CIPHER_SUITE_CMIC 0x00409602
-#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */
-
-/* AKM suite selectors */
-#define WLAN_AKM_SUITE_8021X 0x000FAC01
-#define WLAN_AKM_SUITE_PSK 0x000FAC02
-#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03
-#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04
-#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
-#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
-#define WLAN_AKM_SUITE_8021X_SUITE_B 0x000FAC11
-#define WLAN_AKM_SUITE_8021X_SUITE_B_192 0x000FAC12
-#define WLAN_AKM_SUITE_CCKM 0x00409600
-#define WLAN_AKM_SUITE_OSEN 0x506f9a01
-
/* IEEE 802.11v - WNM Action field values */
enum wnm_action {
@@ -1559,6 +1802,102 @@ struct rrm_link_measurement_report {
u8 variable[0];
} STRUCT_PACKED;
+/* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */
+struct rrm_measurement_request_element {
+ u8 eid; /* Element ID */
+ u8 len; /* Length */
+ u8 token; /* Measurement Token */
+ u8 mode; /* Measurement Request Mode */
+ u8 type; /* Measurement Type */
+ u8 variable[0]; /* Measurement Request */
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */
+#define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0)
+#define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1)
+#define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2)
+#define MEASUREMENT_REQUEST_MODE_REPORT BIT(3)
+#define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4)
+
+/* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */
+struct rrm_measurement_beacon_request {
+ u8 oper_class; /* Operating Class */
+ u8 channel; /* Channel Number */
+ le16 rand_interval; /* Randomization Interval (in TUs) */
+ le16 duration; /* Measurement Duration (in TUs) */
+ u8 mode; /* Measurement Mode */
+ u8 bssid[ETH_ALEN]; /* BSSID */
+ u8 variable[0]; /* Optional Subelements */
+} STRUCT_PACKED;
+
+/*
+ * IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon
+ * request
+ */
+enum beacon_report_mode {
+ BEACON_REPORT_MODE_PASSIVE = 0,
+ BEACON_REPORT_MODE_ACTIVE = 1,
+ BEACON_REPORT_MODE_TABLE = 2,
+};
+
+/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */
+#define WLAN_BEACON_REQUEST_SUBELEM_SSID 0
+#define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */
+#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */
+#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10
+#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */
+#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221
+
+/*
+ * IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values
+ */
+enum beacon_report_detail {
+ /* No fixed-length fields or elements */
+ BEACON_REPORT_DETAIL_NONE = 0,
+ /* All fixed-length fields and any requested elements in the Request
+ * element if present */
+ BEACON_REPORT_DETAIL_REQUESTED_ONLY = 1,
+ /* All fixed-length fields and elements (default, used when Reporting
+ * Detail subelement is not included in a Beacon request) */
+ BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2,
+};
+
+/* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */
+struct rrm_measurement_report_element {
+ u8 eid; /* Element ID */
+ u8 len; /* Length */
+ u8 token; /* Measurement Token */
+ u8 mode; /* Measurement Report Mode */
+ u8 type; /* Measurement Type */
+ u8 variable[0]; /* Measurement Report */
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */
+#define MEASUREMENT_REPORT_MODE_ACCEPT 0
+#define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0)
+#define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1)
+#define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2)
+
+/* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */
+struct rrm_measurement_beacon_report {
+ u8 op_class; /* Operating Class */
+ u8 channel; /* Channel Number */
+ le64 start_time; /* Actual Measurement Start Time
+ * (in TSF of the BSS requesting the measurement) */
+ le16 duration; /* in TUs */
+ u8 report_info; /* Reported Frame Information */
+ u8 rcpi; /* RCPI */
+ u8 rsni; /* RSNI */
+ u8 bssid[ETH_ALEN]; /* BSSID */
+ u8 antenna_id; /* Antenna ID */
+ le32 parent_tsf; /* Parent TSF */
+ u8 variable[0]; /* Optional Subelements */
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
+#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1
+#define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221
+
/* IEEE Std 802.11ad-2012 - Multi-band element */
struct multi_band_ie {
u8 eid; /* WLAN_EID_MULTI_BAND */
@@ -1660,4 +1999,55 @@ enum nr_chan_width {
NR_CHAN_WIDTH_80P80 = 4,
};
+struct ieee80211_he_capabilities {
+ u8 he_mac_capab_info[5];
+ u8 he_phy_capab_info[9];
+ u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */
+ /* PPE Thresholds (optional) */
+} STRUCT_PACKED;
+
+struct ieee80211_he_operation {
+ u32 he_oper_params;
+ u8 he_mcs_nss_set[2];
+ u8 vht_op_info_chwidth;
+ u8 vht_op_info_chan_center_freq_seg0_idx;
+ u8 vht_op_info_chan_center_freq_seg1_idx;
+ /* Followed by conditional MaxBSSID Indicator subfield (u8) */
+} STRUCT_PACKED;
+
+/* HE Capabilities Information defines */
+#define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3
+#define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7))
+#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4
+#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB ((u8) BIT(0))
+#define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX 4
+#define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1))
+
+/* HE Operation defines */
+#define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(0) | BIT(1) | \
+ BIT(2) | BIT(3) | \
+ BIT(4) | BIT(5)))
+#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(6) | BIT(7) | \
+ BIT(8)))
+#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 6
+#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(9))
+#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(10) | BIT(11) | \
+ BIT(12) | BIT(13) | \
+ BIT(14) | BIT(15) | \
+ BIT(16) | BIT(17) | \
+ BIT(18) | BIT(19)))
+#define HE_OPERATION_RTS_THRESHOLD_OFFSET 10
+#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(20))
+#define HE_OPERATION_MAX_BSSID_INDICATOR_MASK ((u32) (BIT(21) | BIT(22) | \
+ BIT(23) | BIT(24) | \
+ BIT(25) | BIT(26) | \
+ BIT(27) | BIT(28)))
+#define HE_OPERATION_MAX_BSSID_INDICATOR_OFFSET 21
+#define HE_OPERATION_TX_BSSID_INDICATOR ((u32) BIT(29))
+#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(30))
+#define HE_OPERATION_BSS_DUAL_BEACON ((u32) BIT(31))
+
+/* DPP Public Action frame identifiers - OUI_WFA */
+#define DPP_OUI_TYPE 0x1A
+
#endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h
index a0c1d1bfafc4..e7acff108eb3 100644
--- a/src/common/ieee802_1x_defs.h
+++ b/src/common/ieee802_1x_defs.h
@@ -12,6 +12,8 @@
#define CS_ID_LEN 8
#define CS_ID_GCM_AES_128 0x0080020001000001ULL
#define CS_NAME_GCM_AES_128 "GCM-AES-128"
+#define CS_ID_GCM_AES_256 0x0080c20001000002ULL
+#define CS_NAME_GCM_AES_256 "GCM-AES-256"
enum macsec_policy {
/**
@@ -25,6 +27,12 @@ enum macsec_policy {
* Disabled MACsec - do not secure sessions.
*/
DO_NOT_SECURE,
+
+ /**
+ * Should secure sessions, and try to use encryption.
+ * Like @SHOULD_SECURE, this follows the key server's decision.
+ */
+ SHOULD_ENCRYPT,
};
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index 8dff30382b60..b85c6c347a79 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -9,6 +9,7 @@
#ifndef PRIVSEP_COMMANDS_H
#define PRIVSEP_COMMANDS_H
+#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
enum privsep_cmd {
@@ -29,8 +30,17 @@ enum privsep_cmd {
PRIVSEP_CMD_AUTHENTICATE,
};
-struct privsep_cmd_authenticate
-{
+#define PRIVSEP_MAX_SCAN_FREQS 50
+
+struct privsep_cmd_scan {
+ unsigned int num_ssids;
+ u8 ssids[WPAS_MAX_SCAN_SSIDS][32];
+ u8 ssid_lens[WPAS_MAX_SCAN_SSIDS];
+ unsigned int num_freqs;
+ u16 freqs[PRIVSEP_MAX_SCAN_FREQS];
+};
+
+struct privsep_cmd_authenticate {
int freq;
u8 bssid[ETH_ALEN];
u8 ssid[SSID_MAX_LEN];
@@ -42,13 +52,12 @@ struct privsep_cmd_authenticate
int wep_tx_keyidx;
int local_state_change;
int p2p;
- size_t sae_data_len;
+ size_t auth_data_len;
/* followed by ie_len bytes of ie */
- /* followed by sae_data_len bytes of sae_data */
+ /* followed by auth_data_len bytes of auth_data */
};
-struct privsep_cmd_associate
-{
+struct privsep_cmd_associate {
u8 bssid[ETH_ALEN];
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
@@ -64,8 +73,7 @@ struct privsep_cmd_associate
/* followed by wpa_ie_len bytes of wpa_ie */
};
-struct privsep_cmd_set_key
-{
+struct privsep_cmd_set_key {
int alg;
u8 addr[ETH_ALEN];
int key_idx;
@@ -84,7 +92,6 @@ enum privsep_event {
PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
PRIVSEP_EVENT_INTERFACE_STATUS,
PRIVSEP_EVENT_PMKID_CANDIDATE,
- PRIVSEP_EVENT_STKSTART,
PRIVSEP_EVENT_FT_RESPONSE,
PRIVSEP_EVENT_RX_EAPOL,
PRIVSEP_EVENT_SCAN_STARTED,
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index adaec890b58d..7c75d0804553 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,6 +1,7 @@
/*
* Qualcomm Atheros OUI and vendor specific assignments
- * Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -49,7 +50,10 @@ enum qca_radiotap_vendor_ids {
*
* @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
* NAN Request/Response and NAN Indication messages. These messages are
- * interpreted between the framework and the firmware component.
+ * interpreted between the framework and the firmware component. While
+ * sending the command from userspace to the driver, payload is not
+ * encapsulated inside any attribute. Attribute QCA_WLAN_VENDOR_ATTR_NAN
+ * is used when receiving vendor events in userspace from the driver.
*
* @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
* used to configure PMK to the driver even when not connected. This can
@@ -90,6 +94,75 @@ enum qca_radiotap_vendor_ids {
* which supports DFS offloading, to indicate a radar pattern has been
* detected. The channel is now unusable.
*
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap
+ * based on enum wifi_logger_supported_features. Attributes defined in
+ * enum qca_wlan_vendor_attr_get_logger_features.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA: Get the ring data from a particular
+ * logger ring, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID is passed as the
+ * attribute for this command. Attributes defined in
+ * enum qca_wlan_vendor_attr_wifi_logger_start.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES: Get the supported TDLS
+ * capabilities of the driver, parameters includes the attributes defined
+ * in enum qca_wlan_vendor_attr_tdls_get_capabilities.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS: Vendor command used to offload
+ * sending of certain periodic IP packet to firmware, attributes defined in
+ * enum qca_wlan_vendor_attr_offloaded_packets.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI: Command used to configure RSSI
+ * monitoring, defines min and max RSSI which are configured for RSSI
+ * monitoring. Also used to notify the RSSI breach and provides the BSSID
+ * and RSSI value that was breached. Attributes defined in
+ * enum qca_wlan_vendor_attr_rssi_monitoring.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NDP: Command used for performing various NAN
+ * Data Path (NDP) related operations, attributes defined in
+ * enum qca_wlan_vendor_attr_ndp_params.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD: Command used to enable/disable
+ * Neighbour Discovery offload, attributes defined in
+ * enum qca_wlan_vendor_attr_nd_offload.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: Used to set/get the various
+ * configuration parameter for BPF packet filter, attributes defined in
+ * enum qca_wlan_vendor_attr_packet_filter.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE: Gets the driver-firmware
+ * maximum supported size, attributes defined in
+ * enum qca_wlan_vendor_drv_info.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS: Command to get various
+ * data about wake reasons and datapath IP statistics, attributes defined
+ * in enum qca_wlan_vendor_attr_wake_stats.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration
+ * for IEEE 802.11 communicating outside the context of a basic service
+ * set, called OCB command. Uses the attributes defines in
+ * enum qca_wlan_vendor_attr_ocb_set_config.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME: Command used to set OCB
+ * UTC time. Use the attributes defines in
+ * enum qca_wlan_vendor_attr_ocb_set_utc_time.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT: Command used to start
+ * sending OCB timing advert frames. Uses the attributes defines in
+ * enum qca_wlan_vendor_attr_ocb_start_timing_advert.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT: Command used to stop
+ * OCB timing advert. Uses the attributes defines in
+ * enum qca_wlan_vendor_attr_ocb_stop_timing_advert.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER: Command used to get TSF
+ * timer value. Uses the attributes defines in
+ * enum qca_wlan_vendor_attr_ocb_get_tsf_resp.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES: Command/event to update the
+ * link properties of the respective interface. As an event, is used
+ * to notify the connected station's status. The attributes for this
+ * command are defined in enum qca_wlan_vendor_attr_link_properties.
+ *
* @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to
* start the P2P Listen offload function in device and pass the listen
* channel, period, interval, count, device types, and vendor specific
@@ -164,8 +237,11 @@ enum qca_radiotap_vendor_ids {
*
* @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of
* arrival) measurement with a single peer. Specify peer MAC address in
- * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and measurement type in
- * QCA_WLAN_VENDOR_ATTR_AOA_TYPE. Measurement result is reported in
+ * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and optionally frequency (MHz) in
+ * QCA_WLAN_VENDOR_ATTR_FREQ (if not specified, locate peer in kernel
+ * scan results cache and use the frequency from there).
+ * Also specify measurement type in QCA_WLAN_VENDOR_ATTR_AOA_TYPE.
+ * Measurement result is reported in
* QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event.
*
* @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify
@@ -185,6 +261,244 @@ enum qca_radiotap_vendor_ids {
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a
* specific chain.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG: Get low level
+ * configuration for a DMG RF sector. Specify sector index in
+ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
+ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and RF modules
+ * to return sector information for in
+ * QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK. Returns sector configuration
+ * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. Also return the
+ * exact time where information was captured in
+ * QCA_WLAN_VENDOR_ATTR_TSF.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG: Set low level
+ * configuration for a DMG RF sector. Specify sector index in
+ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
+ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and sector configuration
+ * for one or more DMG RF modules in
+ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR: Get selected
+ * DMG RF sector for a station. This is the sector that the HW
+ * will use to communicate with the station. Specify the MAC address
+ * of associated station/AP/PCP in QCA_WLAN_VENDOR_ATTR_MAC_ADDR (not
+ * needed for unassociated station). Specify sector type to return in
+ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE. Returns the selected
+ * sector index in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
+ * Also return the exact time where the information was captured
+ * in QCA_WLAN_VENDOR_ATTR_TSF.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR: Set the
+ * selected DMG RF sector for a station. This is the sector that
+ * the HW will use to communicate with the station.
+ * Specify the MAC address of associated station/AP/PCP in
+ * QCA_WLAN_VENDOR_ATTR_MAC_ADDR, the sector type to select in
+ * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and the sector index
+ * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
+ * The selected sector will be locked such that it will not be
+ * modified like it normally does (for example when station
+ * moves around). To unlock the selected sector for a station
+ * pass the special value 0xFFFF in the sector index. To unlock
+ * all connected stations also pass a broadcast MAC address.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS: Configure the TDLS behavior
+ * in the host driver. The different TDLS configurations are defined
+ * by the attributes in enum qca_wlan_vendor_attr_tdls_configuration.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: Query device IEEE 802.11ax HE
+ * capabilities. The response uses the attributes defined in
+ * enum qca_wlan_vendor_attr_get_he_capabilities.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN: Abort an ongoing vendor scan that was
+ * started with QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN. This command
+ * carries the scan cookie of the corresponding scan request. The scan
+ * cookie is represented by QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS: Set the Specific
+ * Absorption Rate (SAR) power limits. A critical regulation for
+ * FCC compliance, OEMs require methods to set SAR limits on TX
+ * power of WLAN/WWAN. enum qca_vendor_attr_sar_limits
+ * attributes are used with this command.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS: This command/event is used by the
+ * host driver for offloading the implementation of Auto Channel Selection
+ * (ACS) to an external user space entity. This interface is used as the
+ * event from the host driver to the user space entity and also as the
+ * request from the user space entity to the host driver. The event from
+ * the host driver is used by the user space entity as an indication to
+ * start the ACS functionality. The attributes used by this event are
+ * represented by the enum qca_wlan_vendor_attr_external_acs_event.
+ * User space entity uses the same interface to inform the host driver with
+ * selected channels after the ACS operation using the attributes defined
+ * by enum qca_wlan_vendor_attr_external_acs_channels.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE: Vendor event carrying the
+ * requisite information leading to a power save failure. The information
+ * carried as part of this event is represented by the
+ * enum qca_attr_chip_power_save_failure attributes.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET: Start/Stop the NUD statistics
+ * collection. Uses attributes defined in enum qca_attr_nud_stats_set.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET: Get the NUD statistics. These
+ * statistics are represented by the enum qca_attr_nud_stats_get
+ * attributes.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: Sub-command to fetch
+ * the BSS transition status, whether accept or reject, for a list of
+ * candidate BSSIDs provided by the userspace. This uses the vendor
+ * attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. The userspace shall specify
+ * the attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and an
+ * array of QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID nested in
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO in the request. In the response
+ * the driver shall specify array of
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID and
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS pairs nested in
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a
+ * specific QCA module. The trace levels are represented by
+ * enum qca_attr_trace_level attributes.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT: Set the Beam Refinement
+ * Protocol antenna limit in different modes. See enum
+ * qca_wlan_vendor_attr_brp_ant_limit_mode.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan
+ * parameters are specified by enum qca_wlan_vendor_attr_spectral_scan.
+ * This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE)
+ * identifying the operation in success case.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses
+ * a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START to identify the scan to
+ * be stopped.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS: Set the active Type Of Service on the
+ * specific interface. This can be used to modify some of the low level
+ * scan parameters (off channel dwell time, home channel time) in the
+ * driver/firmware. These parameters are maintained within the host driver.
+ * This command is valid only when the interface is in the connected state.
+ * These scan parameters shall be reset by the driver/firmware once
+ * disconnected. The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_active_tos.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_HANG: Event indicating to the user space that the
+ * driver has detected an internal failure. This event carries the
+ * information indicating the reason that triggered this detection. The
+ * attributes for this command are defined in
+ * enum qca_wlan_vendor_attr_hang.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG: Get the current values
+ * of spectral parameters used. The spectral scan parameters are specified
+ * by enum qca_wlan_vendor_attr_spectral_scan.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS: Get the debug stats
+ * for spectral scan functionality. The debug stats are specified by
+ * enum qca_wlan_vendor_attr_spectral_diag_stats.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO: Get spectral
+ * scan system capabilities. The capabilities are specified
+ * by enum qca_wlan_vendor_attr_spectral_cap.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS: Get the current
+ * status of spectral scan. The status values are specified
+ * by enum qca_wlan_vendor_attr_spectral_scan_status.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING: Sub-command to flush
+ * peer pending packets. Specify the peer MAC address in
+ * QCA_WLAN_VENDOR_ATTR_PEER_ADDR and the access category of the packets
+ * in QCA_WLAN_VENDOR_ATTR_AC. The attributes are listed
+ * in enum qca_wlan_vendor_attr_flush_pending.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO: Get vendor specific Representative
+ * RF Operating Parameter (RROP) information. The attributes for this
+ * information are defined in enum qca_wlan_vendor_attr_rrop_info. This is
+ * intended for use by external Auto Channel Selection applications.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS: Get the Specific Absorption Rate
+ * (SAR) power limits. This is a companion to the command
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS and is used to retrieve the
+ * settings currently in use. The attributes returned by this command are
+ * defined by enum qca_vendor_attr_sar_limits.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO: Provides the current behavior of
+ * the WLAN hardware MAC. Also, provides the WLAN netdev interface
+ * information attached to the respective MAC.
+ * This works both as a query (user space asks the current mode) or event
+ * interface (driver advertising the current mode to the user space).
+ * Driver does not trigger this event for temporary hardware mode changes.
+ * Mode changes w.r.t Wi-Fi connection update (VIZ creation / deletion,
+ * channel change, etc.) are updated with this event. Attributes for this
+ * interface are defined in enum qca_wlan_vendor_attr_mac.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH: Set MSDU queue depth threshold
+ * per peer per TID. Attributes for this command are define in
+ * enum qca_wlan_set_qdepth_thresh_attr.
+ * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD: Provides the thermal shutdown action
+ * guide for WLAN driver. Request to suspend of driver and FW if the
+ * temperature is higher than the suspend threshold; resume action is
+ * requested to driver if the temperature is lower than the resume
+ * threshold. In user poll mode, request temperature data by user. For test
+ * purpose, getting thermal shutdown configuration parameters is needed.
+ * Attributes for this interface are defined in
+ * enum qca_wlan_vendor_attr_thermal_cmd.
+ * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT: Thermal events reported from
+ * driver. Thermal temperature and indication of resume completion are
+ * reported as thermal events. The attributes for this command are defined
+ * in enum qca_wlan_vendor_attr_thermal_event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION: Sub command to set WiFi
+ * test configuration. Attributes for this command are defined in
+ * enum qca_wlan_vendor_attr_wifi_test_config.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER: This command is used to configure an
+ * RX filter to receive frames from stations that are active on the
+ * operating channel, but not associated with the local device (e.g., STAs
+ * associated with other APs). Filtering is done based on a list of BSSIDs
+ * and STA MAC addresses added by the user. This command is also used to
+ * fetch the statistics of unassociated stations. The attributes used with
+ * this command are defined in enum qca_wlan_vendor_attr_bss_filter.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NAN_EXT: An extendable version of NAN vendor
+ * command. The earlier command for NAN, QCA_NL80211_VENDOR_SUBCMD_NAN,
+ * carried a payload which was a binary blob of data. The command was not
+ * extendable to send more information. The newer version carries the
+ * legacy blob encapsulated within an attribute and can be extended with
+ * additional vendor attributes that can enhance the NAN command interface.
+ * @QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT: Event to indicate scan triggered
+ * or stopped within driver/firmware in order to initiate roaming. The
+ * attributes used with this event are defined in enum
+ * qca_wlan_vendor_attr_roam_scan. Some drivers may not send these events
+ * in few cases, e.g., if the host processor is sleeping when this event
+ * is generated in firmware.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG: This command is used to
+ * configure parameters per peer to capture Channel Frequency Response
+ * (CFR) and enable Periodic CFR capture. The attributes for this command
+ * are defined in enum qca_wlan_vendor_peer_cfr_capture_attr.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT: Event to indicate changes
+ * in throughput dynamically. The driver estimates the throughput based on
+ * number of packets being transmitted/received per second and indicates
+ * the changes in throughput to user space. Userspace tools can use this
+ * information to configure kernel's TCP parameters in order to achieve
+ * peak throughput. Optionally, the driver will also send guidance on
+ * modifications to kernel's TCP parameters which can be referred by
+ * userspace tools. The attributes used with this event are defined in enum
+ * qca_wlan_vendor_attr_throughput_change.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG: This command is used to set
+ * priorities among different types of traffic during coex scenarios.
+ * Current supported prioritization is among WLAN/BT/ZIGBEE with different
+ * profiles mentioned in enum qca_coex_config_profiles. The associated
+ * attributes used with this command are defined in enum
+ * qca_vendor_attr_coex_config.
+ *
+ * Based on the config provided, FW will boost the weight and prioritize
+ * the traffic for that subsystem (WLAN/BT/Zigbee).
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -194,7 +508,7 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11,
QCA_NL80211_VENDOR_SUBCMD_NAN = 12,
- QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
+ QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
@@ -236,11 +550,33 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58,
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59,
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60,
- /* 61-73 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO = 61,
+ QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START = 62,
+ QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP = 63,
+ QCA_NL80211_VENDOR_SUBCMD_ROAM = 64,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST = 65,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SSID_HOTLIST = 66,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_FOUND = 67,
+ QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_LOST = 68,
+ QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST = 69,
+ QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST = 70,
+ QCA_NL80211_VENDOR_SUBCMD_PNO_RESET_PASSPOINT_LIST = 71,
+ QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND = 72,
+ QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND = 73,
/* Wi-Fi configuration subcommands */
QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74,
QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75,
- /* 76-90 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET = 76,
+ QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA = 77,
+ QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES = 78,
+ QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79,
+ QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80,
+ QCA_NL80211_VENDOR_SUBCMD_NDP = 81,
+ QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD = 82,
+ QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER = 83,
+ QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE = 84,
+ QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS = 85,
+ /* 86-90 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
@@ -285,21 +621,66 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136,
QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137,
QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138,
+ /* DMG low level RF sector operations */
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
+ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
+ QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS = 143,
+ QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES = 144,
+ QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN = 145,
+ QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146,
+ QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS = 147,
+ QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE = 148,
+ QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET = 149,
+ QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150,
+ QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151,
+ QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152,
+ QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START = 154,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155,
+ QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156,
+ QCA_NL80211_VENDOR_SUBCMD_HANG = 157,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG = 158,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS = 159,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO = 160,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS = 161,
+ /* Flush peer pending data */
+ QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING = 162,
+ QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO = 163,
+ QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS = 164,
+ QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO = 165,
+ QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH = 166,
+ /* Thermal shutdown commands to protect wifi chip */
+ QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD = 167,
+ QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT = 168,
+ /* Wi-Fi test configuration subcommand */
+ QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION = 169,
+ /* Frame filter operations for other BSSs/unassociated STAs */
+ QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER = 170,
+ QCA_NL80211_VENDOR_SUBCMD_NAN_EXT = 171,
+ QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT = 172,
+ QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173,
+ QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174,
+ QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175,
};
-
enum qca_wlan_vendor_attr {
QCA_WLAN_VENDOR_ATTR_INVALID = 0,
/* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
QCA_WLAN_VENDOR_ATTR_DFS = 1,
- /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */
+ /* Used only when driver sends vendor events to the userspace under the
+ * command QCA_NL80211_VENDOR_SUBCMD_NAN. Not used when userspace sends
+ * commands to the driver.
+ */
QCA_WLAN_VENDOR_ATTR_NAN = 2,
/* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3,
/* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
QCA_WLAN_VENDOR_ATTR_IFINDEX = 4,
/* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined
- * by enum qca_roaming_policy. */
+ * by enum qca_roaming_policy.
+ */
QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
@@ -387,17 +768,87 @@ enum qca_wlan_vendor_attr {
QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
* to specify the chain number (unsigned 32 bit value) to inquire
- * the corresponding antenna RSSI value */
+ * the corresponding antenna RSSI value
+ */
QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
- * to report the specific antenna RSSI value (unsigned 32 bit value) */
+ * to report the specific antenna RSSI value (unsigned 32 bit value)
+ */
QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27,
+ /* Frequency in MHz, various uses. Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_FREQ = 28,
+ /* TSF timer value, unsigned 64 bit value.
+ * May be returned by various commands.
+ */
+ QCA_WLAN_VENDOR_ATTR_TSF = 29,
+ /* DMG RF sector index, unsigned 16 bit number. Valid values are
+ * 0..127 for sector indices or 65535 as special value used to
+ * unlock sector selection in
+ * QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR.
+ */
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX = 30,
+ /* DMG RF sector type, unsigned 8 bit value. One of the values
+ * in enum qca_wlan_vendor_attr_dmg_rf_sector_type.
+ */
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE = 31,
+ /* Bitmask of DMG RF modules for which information is requested. Each
+ * bit corresponds to an RF module with the same index as the bit
+ * number. Unsigned 32 bit number but only low 8 bits can be set since
+ * all DMG chips currently have up to 8 RF modules.
+ */
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK = 32,
+ /* Array of nested attributes where each entry is DMG RF sector
+ * configuration for a single RF module.
+ * Attributes for each entry are taken from enum
+ * qca_wlan_vendor_attr_dmg_rf_sector_cfg.
+ * Specified in QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG
+ * and returned by QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG.
+ */
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG = 33,
+ /* Used in QCA_NL80211_VENDOR_SUBCMD_STATS_EXT command
+ * to report frame aggregation statistics to userspace.
+ */
+ QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM = 34,
+ QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO = 35,
+ /* Unsigned 8-bit value representing MBO transition reason code as
+ * provided by the AP used by subcommand
+ * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS. This is
+ * specified by the userspace in the request to the driver.
+ */
+ QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON = 36,
+ /* Array of nested attributes, BSSID and status code, used by subcommand
+ * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, where each
+ * entry is taken from enum qca_wlan_vendor_attr_btm_candidate_info.
+ * The userspace space specifies the list/array of candidate BSSIDs in
+ * the order of preference in the request. The driver specifies the
+ * status code, for each BSSID in the list, in the response. The
+ * acceptable candidates are listed in the order preferred by the
+ * driver.
+ */
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37,
+ /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
+ * See enum qca_wlan_vendor_attr_brp_ant_limit_mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE = 38,
+ /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
+ * to define the number of antennas to use for BRP.
+ * different purpose in each ANT_LIMIT_MODE:
+ * DISABLE - ignored
+ * EFFECTIVE - upper limit to number of antennas to be used
+ * FORCE - exact number of antennas to be used
+ * unsigned 8 bit value
+ */
+ QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39,
+ /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
+ * to report the corresponding antenna index to the chain RSSI value
+ */
+ QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
};
-
enum qca_roaming_policy {
QCA_ROAMING_NOT_ALLOWED,
QCA_ROAMING_ALLOWED_WITHIN_ESS,
@@ -413,6 +864,38 @@ enum qca_wlan_vendor_attr_roam_auth {
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
+ /* Indicates the status of re-association requested by user space for
+ * the BSSID specified by QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID.
+ * Type u16.
+ * Represents the status code from AP. Use
+ * %WLAN_STATUS_UNSPECIFIED_FAILURE if the device cannot give you the
+ * real status code for failures.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS,
+ /* This attribute indicates that the old association was maintained when
+ * a re-association is requested by user space and that re-association
+ * attempt fails (i.e., cannot connect to the requested BSS, but can
+ * remain associated with the BSS with which the association was in
+ * place when being requested to roam). Used along with
+ * WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS to indicate the current
+ * re-association status. Type flag.
+ * This attribute is applicable only for re-association failure cases.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RETAIN_CONNECTION,
+ /* This attribute specifies the PMK if one was newly generated during
+ * FILS roaming. This is added to the PMKSA cache and is used in
+ * subsequent connections with PMKSA caching.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK = 11,
+ /* This attribute specifies the PMKID used/generated for the current
+ * FILS roam. This is used in subsequent connections with PMKSA caching.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID = 12,
+ /* A 16-bit unsigned value specifying the next sequence number to use
+ * in ERP message in the currently associated realm. This is used in
+ * doing subsequent ERP based connections in the same realm.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -493,13 +976,24 @@ enum qca_wlan_vendor_acs_hw_mode {
* @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
* band selection based on channel selection results.
* @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
- * simultaneous off-channel operations.
+ * simultaneous off-channel operations.
* @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P
* Listen offload; a mechanism where the station's firmware takes care of
* responding to incoming Probe Request frames received from other P2P
* Devices whilst in Listen state, rather than having the user space
* wpa_supplicant do it. Information from received P2P requests are
* forwarded from firmware to host whenever the host processor wakes up.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_STA: Device supports all OCE non-AP STA
+ * specific features.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_AP: Device supports all OCE AP specific
+ * features.
+ * @QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON: Device supports OCE STA-CFON
+ * specific features only. If a Device sets this bit but not the
+ * %QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that
+ * this Device may not support all OCE AP functionalities but can support
+ * only OCE STA-CFON functionalities.
+ * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
+ * managed regulatory.
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
@@ -507,6 +1001,10 @@ enum qca_wlan_vendor_features {
QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1,
QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3,
+ QCA_WLAN_VENDOR_FEATURE_OCE_STA = 4,
+ QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5,
+ QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6,
+ QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -532,6 +1030,112 @@ enum qca_wlan_vendor_attr_data_offload_ind {
QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
};
+/**
+ * enum qca_wlan_vendor_attr_ocb_set_config - Vendor subcmd attributes to set
+ * OCB config
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: Number of channels in the
+ * configuration
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: Size of the schedule
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: Array of channels
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: Array of channels to be
+ * scheduled
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: Array of NDL channel
+ * information
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: Array of NDL
+ * active state configuration
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: Configuration flags such as
+ * OCB_CONFIG_FLAG_80211_FRAME_MODE
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM: Default TX parameters to
+ * use in the case that a packet is sent without a TX control header
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION: Max duration after the
+ * last TA received that the local time set by TA is synchronous to other
+ * communicating OCB STAs.
+ */
+enum qca_wlan_vendor_attr_ocb_set_config {
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT = 1,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE = 2,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY = 3,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY = 4,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY = 5,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY = 6,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS = 7,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM = 8,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION = 9,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_set_utc_time - Vendor subcmd attributes to set
+ * UTC time
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: The UTC time as an array of
+ * 10 bytes
+ * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: The time error as an array of
+ * 5 bytes
+ */
+enum qca_wlan_vendor_attr_ocb_set_utc_time {
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE = 1,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR = 2,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX =
+ QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_start_timing_advert - Vendor subcmd attributes
+ * to start sending timing advert frames
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: Cannel frequency
+ * on which to send the frames
+ * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: Number of times
+ * the frame is sent in 5 seconds
+ */
+enum qca_wlan_vendor_attr_ocb_start_timing_advert {
+ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ = 1,
+ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE = 2,
+ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX =
+ QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_stop_timing_advert - Vendor subcmd attributes
+ * to stop timing advert
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: The channel
+ * frequency on which to stop the timing advert
+ */
+enum qca_wlan_vendor_attr_ocb_stop_timing_advert {
+ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ = 1,
+ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX =
+ QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ocb_get_tsf_response - Vendor subcmd attributes to
+ * get TSF timer value
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: Higher 32 bits of the
+ * timer
+ * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: Lower 32 bits of the timer
+ */
+enum qca_wlan_vendor_attr_ocb_get_tsf_resp {
+ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH = 1,
+ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW = 2,
+ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX =
+ QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1
+};
+
enum qca_vendor_attr_get_preferred_freq_list {
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
/* A 32-unsigned value; the interface type/mode for which the preferred
@@ -544,6 +1148,12 @@ enum qca_vendor_attr_get_preferred_freq_list {
* from kernel space to user space.
*/
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
+ /* An array of nested values as per enum qca_wlan_vendor_attr_pcl
+ * attribute. Each element contains frequency (MHz), weight, and flag
+ * bit mask indicating how the frequency should be used in P2P
+ * negotiation; sent from kernel space to user space.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX =
@@ -679,11 +1289,39 @@ enum qca_vendor_attr_wisa_cmd {
* vendor specific element is defined by the latest P802.11ax draft.
* Please note that the draft is still work in progress and this element
* payload is subject to change.
+ *
+ * @QCA_VENDOR_ELEM_RAPS: RAPS element (OFDMA-based Random Access Parameter Set
+ * element).
+ * This element can be used for pre-standard publication testing of HE
+ * before P802.11ax draft assigns the element ID extension. The payload of
+ * this vendor specific element is defined by the latest P802.11ax draft
+ * (not including the Element ID Extension field). Please note that the
+ * draft is still work in progress and this element payload is subject to
+ * change.
+ *
+ * @QCA_VENDOR_ELEM_MU_EDCA_PARAMS: MU EDCA Parameter Set element.
+ * This element can be used for pre-standard publication testing of HE
+ * before P802.11ax draft assigns the element ID extension. The payload of
+ * this vendor specific element is defined by the latest P802.11ax draft
+ * (not including the Element ID Extension field). Please note that the
+ * draft is still work in progress and this element payload is subject to
+ * change.
+ *
+ * @QCA_VENDOR_ELEM_BSS_COLOR_CHANGE: BSS Color Change Announcement element.
+ * This element can be used for pre-standard publication testing of HE
+ * before P802.11ax draft assigns the element ID extension. The payload of
+ * this vendor specific element is defined by the latest P802.11ax draft
+ * (not including the Element ID Extension field). Please note that the
+ * draft is still work in progress and this element payload is subject to
+ * change.
*/
enum qca_vendor_element_id {
QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
QCA_VENDOR_ELEM_HE_CAPAB = 1,
QCA_VENDOR_ELEM_HE_OPER = 2,
+ QCA_VENDOR_ELEM_RAPS = 3,
+ QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4,
+ QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5,
};
/**
@@ -696,29 +1334,32 @@ enum qca_vendor_element_id {
* @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported
* rates to be included
* @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests
- * at non CCK rate in 2GHz band
+ * at non CCK rate in 2GHz band
* @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags
* @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the
- * driver for the specific scan request
+ * driver for the specific scan request
* @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan
- * request decoded as in enum scan_status
+ * request decoded as in enum scan_status
* @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation
- * scan flag is set
+ * scan flag is set
* @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
- * randomisation
+ * randomisation
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_BSSID: 6-byte MAC address representing the
+ * specific BSSID to scan for.
*/
enum qca_wlan_vendor_attr_scan {
QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
- QCA_WLAN_VENDOR_ATTR_SCAN_IE,
- QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES,
- QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS,
- QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES,
- QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE,
- QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS,
- QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE,
- QCA_WLAN_VENDOR_ATTR_SCAN_STATUS,
- QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
- QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+ QCA_WLAN_VENDOR_ATTR_SCAN_IE = 1,
+ QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES = 2,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS = 3,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES = 4,
+ QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE = 5,
+ QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS = 6,
+ QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE = 7,
+ QCA_WLAN_VENDOR_ATTR_SCAN_STATUS = 8,
+ QCA_WLAN_VENDOR_ATTR_SCAN_MAC = 9,
+ QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK = 10,
+ QCA_WLAN_VENDOR_ATTR_SCAN_BSSID = 11,
QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
@@ -726,10 +1367,10 @@ enum qca_wlan_vendor_attr_scan {
/**
* enum scan_status - Specifies the valid values the vendor scan attribute
- * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
+ * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
*
* @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with
- * new scan results
+ * new scan results
* @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between
*/
enum scan_status {
@@ -776,7 +1417,8 @@ enum qca_vendor_attr_txpower_scale {
enum qca_vendor_attr_txpower_decr_db {
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID,
/* 8-bit unsigned value to indicate the reduction of TX power in dB for
- * a virtual interface. */
+ * a virtual interface.
+ */
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST,
@@ -826,29 +1468,37 @@ enum qca_wlan_vendor_attr_config {
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7,
/* 8-bit unsigned value to configure the maximum TX MPDU for
- * aggregation. */
+ * aggregation.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8,
/* 8-bit unsigned value to configure the maximum RX MPDU for
- * aggregation. */
+ * aggregation.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9,
/* 8-bit unsigned value to configure the Non aggregrate/11g sw
- * retry threshold (0 disable, 31 max). */
+ * retry threshold (0 disable, 31 max).
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10,
/* 8-bit unsigned value to configure the aggregrate sw
- * retry threshold (0 disable, 31 max). */
+ * retry threshold (0 disable, 31 max).
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11,
/* 8-bit unsigned value to configure the MGMT frame
- * retry threshold (0 disable, 31 max). */
+ * retry threshold (0 disable, 31 max).
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12,
/* 8-bit unsigned value to configure the CTRL frame
- * retry threshold (0 disable, 31 max). */
+ * retry threshold (0 disable, 31 max).
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13,
/* 8-bit unsigned value to configure the propagation delay for
- * 2G/5G band (0~63, units in us) */
+ * 2G/5G band (0~63, units in us)
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14,
/* Unsigned 32-bit value to configure the number of unicast TX fail
* packet count. The peer is disconnected once this threshold is
- * reached. */
+ * reached.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15,
/* Attribute used to set scan default IEs to the driver.
*
@@ -859,7 +1509,8 @@ enum qca_wlan_vendor_attr_config {
* merged with the IEs received along with scan request coming to the
* driver. If a particular IE is present in the scan default IEs but not
* present in the scan request, then that IE should be added to the IEs
- * sent in the Probe Request frames for that scan request. */
+ * sent in the Probe Request frames for that scan request.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16,
/* Unsigned 32-bit attribute for generic commands */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17,
@@ -868,42 +1519,183 @@ enum qca_wlan_vendor_attr_config {
/* Unsigned 32-bit data attribute for generic command response */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19,
/* Unsigned 32-bit length attribute for
- * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20,
/* Unsigned 32-bit flags attribute for
- * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21,
/* Unsigned 32-bit, defining the access policy.
* See enum qca_access_policy. Used with
- * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22,
/* Sets the list of full set of IEs for which a specific access policy
* has to be applied. Used along with
* QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access.
- * Zero length payload can be used to clear this access constraint. */
+ * Zero length payload can be used to clear this access constraint.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23,
/* Unsigned 32-bit, specifies the interface index (netdev) for which the
* corresponding configurations are applied. If the interface index is
* not specified, the configurations are attributed to the respective
- * wiphy. */
+ * wiphy.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24,
/* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */
QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25,
/* 8-bit unsigned value to configure the driver and below layers to
* ignore the assoc disallowed set by APs while connecting
- * 1-Ignore, 0-Don't ignore */
+ * 1-Ignore, 0-Don't ignore
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26,
/* 32-bit unsigned value to trigger antenna diversity features:
- * 1-Enable, 0-Disable */
+ * 1-Enable, 0-Disable
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27,
/* 32-bit unsigned value to configure specific chain antenna */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28,
/* 32-bit unsigned value to trigger cycle selftest
- * 1-Enable, 0-Disable */
+ * 1-Enable, 0-Disable
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29,
/* 32-bit unsigned to configure the cycle time of selftest
- * the unit is micro-second */
+ * the unit is micro-second
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30,
+ /* 32-bit unsigned value to set reorder timeout for AC_VO */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31,
+ /* 32-bit unsigned value to set reorder timeout for AC_VI */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO = 32,
+ /* 32-bit unsigned value to set reorder timeout for AC_BE */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT = 33,
+ /* 32-bit unsigned value to set reorder timeout for AC_BK */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34,
+ /* 6-byte MAC address to point out the specific peer */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35,
+ /* 32-bit unsigned value to set window size for specific peer */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36,
+ /* 8-bit unsigned value to set the beacon miss threshold in 2.4 GHz */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37,
+ /* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38,
+ /* 32-bit unsigned value to configure 5 or 10 MHz channel width for
+ * station device while in disconnect state. The attribute use the
+ * value of enum nl80211_chan_width: NL80211_CHAN_WIDTH_5 means 5 MHz,
+ * NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or
+ * 10 MHz channel width, the station will not connect to a BSS using 20
+ * MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to
+ * clear this constraint.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39,
+ /* 32-bit unsigned value to configure the propagation absolute delay
+ * for 2G/5G band (units in us)
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40,
+ /* 32-bit unsigned value to set probe period */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41,
+ /* 32-bit unsigned value to set stay period */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD = 42,
+ /* 32-bit unsigned value to set snr diff */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF = 43,
+ /* 32-bit unsigned value to set probe dwell time */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME = 44,
+ /* 32-bit unsigned value to set mgmt snr weight */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT = 45,
+ /* 32-bit unsigned value to set data snr weight */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT = 46,
+ /* 32-bit unsigned value to set ack snr weight */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT = 47,
+ /* 32-bit unsigned value to configure the listen interval.
+ * This is in units of beacon intervals. This configuration alters
+ * the negotiated listen interval with the AP during the connection.
+ * It is highly recommended to configure a value less than or equal to
+ * the one negotiated during the association. Configuring any greater
+ * value can have adverse effects (frame loss, AP disassociating STA,
+ * etc.).
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL = 48,
+ /*
+ * 8 bit unsigned value that is set on an AP/GO virtual interface to
+ * disable operations that would cause the AP/GO to leave its operating
+ * channel.
+ *
+ * This will restrict the scans to the AP/GO operating channel and the
+ * channels of the other band, if DBS is supported.A STA/CLI interface
+ * brought up after this setting is enabled, will be restricted to
+ * connecting to devices only on the AP/GO interface's operating channel
+ * or on the other band in DBS case. P2P supported channel list is
+ * modified, to only include AP interface's operating-channel and the
+ * channels of the other band if DBS is supported.
+ *
+ * These restrictions are only applicable as long as the AP/GO interface
+ * is alive. If the AP/GO interface is brought down then this
+ * setting/restriction is forgotten.
+ *
+ * If this variable is set on an AP/GO interface while a multi-channel
+ * concurrent session is active, it has no effect on the operation of
+ * the current interfaces, other than restricting the scan to the AP/GO
+ * operating channel and the other band channels if DBS is supported.
+ * However, if the STA is brought down and restarted then the new STA
+ * connection will either be formed on the AP/GO channel or on the
+ * other band in a DBS case. This is because of the scan being
+ * restricted on these channels as mentioned above.
+ *
+ * 1-Restrict / 0-Don't restrict offchannel operations.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL = 49,
+ /*
+ * 8 bit unsigned value to enable/disable LRO (Large Receive Offload)
+ * on an interface.
+ * 1 - Enable, 0 - Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LRO = 50,
+
+ /*
+ * 8 bit unsigned value to globally enable/disable scan
+ * 1 - Enable, 0 - Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51,
+
+ /* 8-bit unsigned value to set the total beacon miss count
+ * This parameter will set the total beacon miss count.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52,
+
+ /* Unsigned 32-bit value to configure the number of continuous
+ * Beacon Miss which shall be used by the firmware to penalize
+ * the RSSI for BTC.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS_BTC = 53,
+
+ /* 8-bit unsigned value to configure the driver and below layers to
+ * enable/disable all FILS features.
+ * 0-enable, 1-disable
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54,
+
+ /* 16-bit unsigned value to configure the level of WLAN latency
+ * module. See enum qca_wlan_vendor_attr_config_latency_level.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL = 55,
+
+ /* 8-bit unsigned value indicating the driver to use the RSNE as-is from
+ * the connect interface. Exclusively used for the scenarios where the
+ * device is used as a test bed device with special functionality and
+ * not recommended for production. This helps driver to not validate the
+ * RSNE passed from user space and thus allow arbitrary IE data to be
+ * used for testing purposes.
+ * 1-enable, 0-disable.
+ * Applications set/reset this configuration. If not reset, this
+ * parameter remains in use until the driver is unloaded.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE = 56,
+
+ /* 8-bit unsigned value to trigger green Tx power saving.
+ * 1-Enable, 0-Disable
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57,
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
@@ -937,7 +1729,8 @@ enum qca_wlan_vendor_attr_sap_config {
enum qca_wlan_vendor_attr_sap_conditional_chan_switch {
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0,
/* Priority based frequency list (an array of u32 values in host byte
- * order) */
+ * order)
+ */
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1,
/* Status of the conditional switch (u32).
* 0: Success, Non-zero: Failure
@@ -972,6 +1765,36 @@ enum qca_wlan_gpio_attr {
};
/**
+ * qca_wlan_set_qdepth_thresh_attr - Parameters for setting
+ * MSDUQ depth threshold per peer per tid in the target
+ *
+ * Associated Vendor Command:
+ * QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH
+ */
+enum qca_wlan_set_qdepth_thresh_attr {
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_INVALID = 0,
+ /* 6-byte MAC address */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAC_ADDR,
+ /* Unsigned 32-bit attribute for holding the TID */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_TID,
+ /* Unsigned 32-bit attribute for holding the update mask
+ * bit 0 - Update high priority msdu qdepth threshold
+ * bit 1 - Update low priority msdu qdepth threshold
+ * bit 2 - Update UDP msdu qdepth threshold
+ * bit 3 - Update Non UDP msdu qdepth threshold
+ * rest of bits are reserved
+ */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_UPDATE_MASK,
+ /* Unsigned 32-bit attribute for holding the threshold value */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_VALUE,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST,
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAX =
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability
*/
enum qca_wlan_vendor_attr_get_hw_capability {
@@ -1143,6 +1966,12 @@ enum qca_wlan_vendor_attr_get_hw_capability {
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME: u64
+ * Absolute timestamp from 1970/1/1, unit in ms. After receiving the
+ * message, user layer APP could call gettimeofday to get another
+ * timestamp and calculate transfer delay for the message.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME: u32
+ * Real period for this measurement, unit in us.
*/
enum qca_wlan_vendor_attr_ll_stats_ext {
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0,
@@ -1236,6 +2065,9 @@ enum qca_wlan_vendor_attr_ll_stats_ext {
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME,
+
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX =
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1
@@ -1289,7 +2121,7 @@ enum qca_wlan_vendor_attr_loc_capa {
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver
* can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION
* will be supported if set.
-* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder
* supports immediate (ASAP) response.
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone
* AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS.
@@ -1320,6 +2152,10 @@ enum qca_wlan_vendor_attr_loc_capa_flags {
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA
* measurement every <value> bursts. If 0 or not specified,
* AOA measurements will be disabled for this peer.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where
+ * the measurement frames are exchanged. Optional; if not
+ * specified, try to locate the peer in the kernel scan
+ * results cache and use frequency from there.
*/
enum qca_wlan_vendor_attr_ftm_peer_info {
QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID,
@@ -1328,6 +2164,7 @@ enum qca_wlan_vendor_attr_ftm_peer_info {
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ,
/* keep last */
QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX =
@@ -1587,4 +2424,3824 @@ enum qca_wlan_vendor_attr_encryption_test {
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1
};
+/**
+ * enum qca_wlan_vendor_attr_dmg_rf_sector_type - Type of
+ * sector for DMG RF sector operations.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX: RX sector
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX: TX sector
+ */
+enum qca_wlan_vendor_attr_dmg_rf_sector_type {
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX
+};
+
+/**
+ * BRP antenna limit mode
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
+ * antenna limit, BRP will be performed as usual.
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE: Define maximal
+ * antennas limit. the hardware may use less antennas than the
+ * maximum limit.
+ * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE: The hardware will
+ * use exactly the specified number of antennas for BRP.
+ */
+enum qca_wlan_vendor_attr_brp_ant_limit_mode {
+ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE,
+ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE,
+ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE,
+ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_MAX
+};
+
+/**
+ * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for
+ * DMG RF sector configuration for a single RF module.
+ * The values are defined in a compact way which closely matches
+ * the way it is stored in HW registers.
+ * The configuration provides values for 32 antennas and 8 distribution
+ * amplifiers, and together describes the characteristics of the RF
+ * sector - such as a beam in some direction with some gain.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX: Index
+ * of RF module for this configuration.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0: Bit 0 of edge
+ * amplifier gain index. Unsigned 32 bit number containing
+ * bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1: Bit 1 of edge
+ * amplifier gain index. Unsigned 32 bit number containing
+ * bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2: Bit 2 of edge
+ * amplifier gain index. Unsigned 32 bit number containing
+ * bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI: Phase values
+ * for first 16 antennas, 2 bits per antenna.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO: Phase values
+ * for last 16 antennas, 2 bits per antenna.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16: Contains
+ * DTYPE values (3 bits) for each distribution amplifier, followed
+ * by X16 switch bits for each distribution amplifier. There are
+ * total of 8 distribution amplifiers.
+ */
+enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0 = 2,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1 = 3,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2 = 4,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI = 5,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO = 6,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16 = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MAX =
+ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_attr_ll_stats_set {
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING = 2,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX =
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_clr {
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0,
+ /* Unsigned 32bit bitmap for clearing statistics
+ * All radio statistics 0x00000001
+ * cca_busy_time (within radio statistics) 0x00000002
+ * All channel stats (within radio statistics) 0x00000004
+ * All scan statistics (within radio statistics) 0x00000008
+ * All interface statistics 0x00000010
+ * All tx rate statistics (within interface statistics) 0x00000020
+ * All ac statistics (with in interface statistics) 0x00000040
+ * All contention (min, max, avg) statistics (within ac statisctics)
+ * 0x00000080.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK = 1,
+ /* Unsigned 8 bit value: Request to stop statistics collection */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ = 2,
+
+ /* Unsigned 32 bit bitmap: Response from the driver
+ * for the cleared statistics
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK = 3,
+ /* Unsigned 8 bit value: Response from driver/firmware
+ * for the stop request
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP = 4,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX =
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_get {
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0,
+ /* Unsigned 32 bit value provided by the caller issuing the GET stats
+ * command. When reporting the stats results, the driver uses the same
+ * value to indicate which GET request the results correspond to.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID = 1,
+ /* Unsigned 32 bit value - bit mask to identify what statistics are
+ * requested for retrieval.
+ * Radio Statistics 0x00000001
+ * Interface Statistics 0x00000020
+ * All Peer Statistics 0x00000040
+ * Peer Statistics 0x00000080
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK = 2,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX =
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_results {
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0,
+ /* Unsigned 32bit value. Used by the driver; must match the request id
+ * provided with the QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET command.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1,
+
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX = 2,
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX = 3,
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX = 4,
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX = 5,
+ /* Signed 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT = 6,
+ /* Signed 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA = 7,
+ /* Signed 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK = 8,
+
+ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are
+ * nested within the interface stats.
+ */
+
+ /* Interface mode, e.g., STA, SOFTAP, IBSS, etc.
+ * Type = enum wifi_interface_mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE = 9,
+ /* Interface MAC address. An array of 6 Unsigned int8 */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR = 10,
+ /* Type = enum wifi_connection_state, e.g., DISCONNECTED,
+ * AUTHENTICATING, etc. valid for STA, CLI only.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE = 11,
+ /* Type = enum wifi_roam_state. Roaming state, e.g., IDLE or ACTIVE
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING = 12,
+ /* Unsigned 32 bit value. WIFI_CAPABILITY_XXX */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES = 13,
+ /* NULL terminated SSID. An array of 33 Unsigned 8bit values */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID = 14,
+ /* BSSID. An array of 6 unsigned 8 bit values */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID = 15,
+ /* Country string advertised by AP. An array of 3 unsigned 8 bit
+ * values.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR = 16,
+ /* Country string for this association. An array of 3 unsigned 8 bit
+ * values.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR = 17,
+
+ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could
+ * be nested within the interface stats.
+ */
+
+ /* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC = 18,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU = 19,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU = 20,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST = 21,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST = 22,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU = 23,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU = 24,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST = 25,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES = 26,
+ /* Unsigned int 32 value corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT = 27,
+ /* Unsigned int 32 values corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG = 28,
+ /* Unsigned int 32 values corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN = 29,
+ /* Unsigned int 32 values corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX = 30,
+ /* Unsigned int 32 values corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG = 31,
+ /* Unsigned int 32 values corresponding to respective AC */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES = 32,
+ /* Unsigned 32 bit value. Number of peers */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS = 33,
+
+ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are
+ * nested within the interface stats.
+ */
+
+ /* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE = 34,
+ /* MAC addr corresponding to respective peer. An array of 6 unsigned
+ * 8 bit values.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS = 35,
+ /* Unsigned int 32 bit value representing capabilities corresponding
+ * to respective peer.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES = 36,
+ /* Unsigned 32 bit value. Number of rates */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES = 37,
+
+ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
+ * are nested within the rate stat.
+ */
+
+ /* Wi-Fi Rate - separate attributes defined for individual fields */
+
+ /* Unsigned int 8 bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE = 38,
+ /* Unsigned int 8 bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS = 39,
+ /* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40,
+ /* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std
+ * in the units of 0.5 Mbps HT/VHT it would be MCS index
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41,
+
+ /* Unsigned 32 bit value. Bit rate in units of 100 kbps */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42,
+
+ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be
+ * nested within the peer info stats.
+ */
+
+ /* Unsigned int 32 bit value. Number of successfully transmitted data
+ * packets, i.e., with ACK received corresponding to the respective
+ * rate.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU = 43,
+ /* Unsigned int 32 bit value. Number of received data packets
+ * corresponding to the respective rate.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU = 44,
+ /* Unsigned int 32 bit value. Number of data packet losses, i.e., no ACK
+ * received corresponding to the respective rate.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST = 45,
+ /* Unsigned int 32 bit value. Total number of data packet retries for
+ * the respective rate.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES = 46,
+ /* Unsigned int 32 bit value. Total number of short data packet retries
+ * for the respective rate.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT = 47,
+ /* Unsigned int 32 bit value. Total number of long data packet retries
+ * for the respective rate.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG = 48,
+
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID = 49,
+ /* Unsigned 32 bit value. Total number of msecs the radio is awake
+ * accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME = 50,
+ /* Unsigned 32 bit value. Total number of msecs the radio is
+ * transmitting accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME = 51,
+ /* Unsigned 32 bit value. Total number of msecs the radio is in active
+ * receive accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME = 52,
+ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
+ * to all scan accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN = 53,
+ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
+ * to NAN accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD = 54,
+ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
+ * to GSCAN accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN = 55,
+ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
+ * to roam scan accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN = 56,
+ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
+ * to PNO scan accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN = 57,
+ /* Unsigned 32 bit value. Total number of msecs the radio is awake due
+ * to Hotspot 2.0 scans and GAS exchange accruing over time.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 = 58,
+ /* Unsigned 32 bit value. Number of channels. */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS = 59,
+
+ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* could
+ * be nested within the channel stats.
+ */
+
+ /* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80 */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH = 60,
+ /* Unsigned 32 bit value. Primary 20 MHz channel. */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ = 61,
+ /* Unsigned 32 bit value. Center frequency (MHz) first segment. */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 = 62,
+ /* Unsigned 32 bit value. Center frequency (MHz) second segment. */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 = 63,
+
+ /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* could be
+ * nested within the radio stats.
+ */
+
+ /* Unsigned int 32 bit value representing total number of msecs the
+ * radio is awake on that channel accruing over time, corresponding to
+ * the respective channel.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME = 64,
+ /* Unsigned int 32 bit value representing total number of msecs the CCA
+ * register is busy accruing over time corresponding to the respective
+ * channel.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME = 65,
+
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66,
+
+ /* Signifies the nested list of channel attributes
+ * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_*
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67,
+
+ /* Signifies the nested list of peer info attributes
+ * QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_*
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO = 68,
+
+ /* Signifies the nested list of rate info attributes
+ * QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO = 69,
+
+ /* Signifies the nested list of wmm info attributes
+ * QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_*
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO = 70,
+
+ /* Unsigned 8 bit value. Used by the driver; if set to 1, it indicates
+ * that more stats, e.g., peers or radio, are to follow in the next
+ * QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event.
+ * Otherwise, it is set to 0.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA = 71,
+
+ /* Unsigned 64 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET = 72,
+
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED = 73,
+
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED = 74,
+
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME = 75,
+
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE = 76,
+
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS = 77,
+
+ /* Number of msecs the radio spent in transmitting for each power level
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL = 78,
+
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT = 79,
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT = 80,
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT = 81,
+ /* Unsigned 32 bit value */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82,
+
+ /* Unsigned int 32 value.
+ * Pending MSDUs corresponding to respective AC.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_type {
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS = 3,
+
+ /* keep last */
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST,
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_MAX =
+ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_configuration - Attributes for
+ * TDLS configuration to the host driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE: Configure the TDLS trigger
+ * mode in the host driver. enum qca_wlan_vendor_tdls_trigger_mode
+ * represents the different TDLS trigger modes.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD: Duration (u32) within
+ * which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD number
+ * of packets shall meet the criteria for implicit TDLS setup.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD: Number (u32) of Tx/Rx packets
+ * within a duration QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD
+ * to initiate a TDLS setup.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD: Time (u32) to initiate
+ * a TDLS Discovery to the peer.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT: Max number (u32) of
+ * discovery attempts to know the TDLS capability of the peer. A peer is
+ * marked as TDLS not capable if there is no response for all the attempts.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT: Represents a duration (u32)
+ * within which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD
+ * number of TX / RX frames meet the criteria for TDLS teardown.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD: Minimum number (u32)
+ * of Tx/Rx packets within a duration
+ * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT to tear down a TDLS link.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD: Threshold
+ * corresponding to the RSSI of the peer below which a TDLS setup is
+ * triggered.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD: Threshold
+ * corresponding to the RSSI of the peer above which a TDLS teardown is
+ * triggered.
+ */
+enum qca_wlan_vendor_attr_tdls_configuration {
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE = 1,
+
+ /* Attributes configuring the TDLS Implicit Trigger */
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD = 2,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD = 3,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD = 4,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT = 5,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT = 6,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD = 7,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD = 8,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD = 9,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_tdls_trigger_mode: Represents the TDLS trigger mode in
+ * the driver
+ *
+ * The following are the different values for
+ * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: The trigger to initiate/teardown
+ * the TDLS connection to a respective peer comes from the user space.
+ * wpa_supplicant provides the commands TDLS_SETUP, TDLS_TEARDOWN,
+ * TDLS_DISCOVER to do this.
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: Host driver triggers this TDLS
+ * setup/teardown to the eligible peer once the configured criteria
+ * (such as TX/RX threshold, RSSI) is met. The attributes
+ * in QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IMPLICIT_PARAMS correspond to
+ * the different configuration criteria for the TDLS trigger from the
+ * host driver.
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: Enables the driver to trigger
+ * the TDLS setup / teardown through the implicit mode only to the
+ * configured MAC addresses (wpa_supplicant, with tdls_external_control=1,
+ * configures the MAC address through TDLS_SETUP / TDLS_TEARDOWN commands).
+ * External mode works on top of the implicit mode. Thus the host driver
+ * is expected to configure in TDLS Implicit mode too to operate in
+ * External mode.
+ * Configuring External mode alone without Implicit mode is invalid.
+ *
+ * All the above implementations work as expected only when the host driver
+ * advertises the capability WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP - representing
+ * that the TDLS message exchange is not internal to the host driver, but
+ * depends on wpa_supplicant to do the message exchange.
+ */
+enum qca_wlan_vendor_tdls_trigger_mode {
+ QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = 1 << 0,
+ QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = 1 << 1,
+ QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = 1 << 2,
+};
+
+/**
+ * enum qca_vendor_attr_sar_limits_selections - Source of SAR power limits
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0: Select SAR profile #0
+ * that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1: Select SAR profile #1
+ * that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2: Select SAR profile #2
+ * that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3: Select SAR profile #3
+ * that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4: Select SAR profile #4
+ * that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE: Do not select any
+ * source of SAR power limits, thereby disabling the SAR power
+ * limit feature.
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: Select the SAR power
+ * limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power
+ * limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
+ *
+ * This enumerates the valid set of values that may be supplied for
+ * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of
+ * the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command or in
+ * the response to an instance of the
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command.
+ */
+enum qca_vendor_attr_sar_limits_selections {
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 = 0,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1 = 1,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2 = 2,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3 = 3,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4 = 4,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7,
+};
+
+/**
+ * enum qca_vendor_attr_sar_limits_spec_modulations -
+ * SAR limits specification modulation
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK -
+ * CCK modulation
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM -
+ * OFDM modulation
+ *
+ * This enumerates the valid set of values that may be supplied for
+ * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION in an
+ * instance of attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC in an
+ * instance of the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor
+ * command or in the response to an instance of the
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command.
+ */
+enum qca_vendor_attr_sar_limits_spec_modulations {
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK = 0,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM = 1,
+};
+
+/**
+ * enum qca_vendor_attr_sar_limits - Attributes for SAR power limits
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE: Optional (u32) value to
+ * select which SAR power limit table should be used. Valid
+ * values are enumerated in enum
+ * %qca_vendor_attr_sar_limits_selections. The existing SAR
+ * power limit selection is unchanged if this attribute is not
+ * present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS: Optional (u32) value
+ * which specifies the number of SAR power limit specifications
+ * which will follow.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC: Nested array of SAR power
+ * limit specifications. The number of specifications is
+ * specified by @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS. Each
+ * specification contains a set of
+ * QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_* attributes. A
+ * specification is uniquely identified by the attributes
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND,
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN, and
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION and always
+ * contains as a payload the attribute
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT,
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX.
+ * Either %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT or
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX is
+ * needed based upon the value of
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND: Optional (u32) value to
+ * indicate for which band this specification applies. Valid
+ * values are enumerated in enum %nl80211_band (although not all
+ * bands may be supported by a given device). If the attribute is
+ * not supplied then the specification will be applied to all
+ * supported bands.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN: Optional (u32) value
+ * to indicate for which antenna chain this specification
+ * applies, i.e. 1 for chain 1, 2 for chain 2, etc. If the
+ * attribute is not supplied then the specification will be
+ * applied to all chains.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION: Optional (u32)
+ * value to indicate for which modulation scheme this
+ * specification applies. Valid values are enumerated in enum
+ * %qca_vendor_attr_sar_limits_spec_modulations. If the attribute
+ * is not supplied then the specification will be applied to all
+ * modulation schemes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT: Required (u32)
+ * value to specify the actual power limit value in units of 0.5
+ * dBm (i.e., a value of 11 represents 5.5 dBm).
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX: Required (u32)
+ * value to indicate SAR V2 indices (0 - 11) to select SAR V2 profiles.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0.
+ *
+ * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS
+ * and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS.
+ */
+enum qca_vendor_attr_sar_limits {
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE = 1,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS = 2,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC = 3,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND = 4,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN = 5,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8,
+
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX =
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command.
+ */
+enum qca_wlan_vendor_attr_get_wifi_info {
+ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1,
+ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX =
+ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_wifi_logger_start: Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START sub command.
+ */
+enum qca_wlan_vendor_attr_wifi_logger_start {
+ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL = 2,
+ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_GET_MAX =
+ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_logger_results {
+ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_INVALID = 0,
+
+ /* Unsigned 32-bit value; must match the request Id supplied by
+ * Wi-Fi HAL in the corresponding subcmd NL msg.
+ */
+ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_REQUEST_ID = 1,
+
+ /* Unsigned 32-bit value; used to indicate the size of memory
+ * dump to be allocated.
+ */
+ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX =
+ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_roaming_config_params {
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_INVALID = 0,
+
+ QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD = 1,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID = 2,
+
+ /* Attributes for wifi_set_ssid_white_list */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS = 3,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST = 4,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID = 5,
+
+ /* Attributes for set_roam_params */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD = 6,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD = 7,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR = 8,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR = 9,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST = 10,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS = 11,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER = 12,
+
+ /* Attribute for set_lazy_roam */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE = 13,
+
+ /* Attribute for set_lazy_roam with preferences */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS = 14,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID = 15,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17,
+
+ /* Attribute for set_blacklist bssid params */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_roam_subcmd: Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_ROAM sub command.
+ */
+enum qca_wlan_vendor_attr_roam_subcmd {
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST = 1,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM = 3,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS = 4,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PARAMS = 5,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID = 6,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_gscan_config_params {
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_INVALID = 0,
+
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID = 1,
+
+ /* Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS sub command.
+ */
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND
+ = 2,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS
+ = 3,
+
+ /* Attributes for input params used by
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_START sub command.
+ */
+
+ /* Unsigned 32-bit value; channel frequency */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CHANNEL = 4,
+ /* Unsigned 32-bit value; dwell time in ms. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_DWELL_TIME = 5,
+ /* Unsigned 8-bit value; 0: active; 1: passive; N/A for DFS */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_PASSIVE = 6,
+ /* Unsigned 8-bit value; channel class */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CLASS = 7,
+
+ /* Unsigned 8-bit value; bucket index, 0 based */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_INDEX = 8,
+ /* Unsigned 8-bit value; band. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BAND = 9,
+ /* Unsigned 32-bit value; desired period, in ms. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_PERIOD = 10,
+ /* Unsigned 8-bit value; report events semantics. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_REPORT_EVENTS = 11,
+ /* Unsigned 32-bit value. Followed by a nested array of
+ * GSCAN_CHANNEL_SPEC_* attributes.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS = 12,
+
+ /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_* attributes.
+ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC = 13,
+
+ /* Unsigned 32-bit value; base timer period in ms. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_BASE_PERIOD = 14,
+ /* Unsigned 32-bit value; number of APs to store in each scan in the
+ * BSSID/RSSI history buffer (keep the highest RSSI APs).
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN = 15,
+ /* Unsigned 8-bit value; in %, when scan buffer is this much full, wake
+ * up AP.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT
+ = 16,
+
+ /* Unsigned 8-bit value; number of scan bucket specs; followed by a
+ * nested array of_GSCAN_BUCKET_SPEC_* attributes and values. The size
+ * of the array is determined by NUM_BUCKETS.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS = 17,
+
+ /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_* attributes.
+ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC = 18,
+
+ /* Unsigned 8-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH
+ = 19,
+ /* Unsigned 32-bit value; maximum number of results to be returned. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX
+ = 20,
+
+ /* An array of 6 x unsigned 8-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID = 21,
+ /* Signed 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW = 22,
+ /* Signed 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH = 23,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_CHANNEL = 24,
+
+ /* Number of hotlist APs as unsigned 32-bit value, followed by a nested
+ * array of AP_THRESHOLD_PARAM attributes and values. The size of the
+ * array is determined by NUM_AP.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_NUM_AP = 25,
+
+ /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_* attributes.
+ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM = 26,
+
+ /* Unsigned 32-bit value; number of samples for averaging RSSI. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE
+ = 27,
+ /* Unsigned 32-bit value; number of samples to confirm AP loss. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE
+ = 28,
+ /* Unsigned 32-bit value; number of APs breaching threshold. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING = 29,
+ /* Unsigned 32-bit value; number of APs. Followed by an array of
+ * AP_THRESHOLD_PARAM attributes. Size of the array is NUM_AP.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP = 30,
+ /* Unsigned 32-bit value; number of samples to confirm AP loss. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE
+ = 31,
+ /* Unsigned 32-bit value. If max_period is non zero or different than
+ * period, then this bucket is an exponential backoff bucket.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_MAX_PERIOD = 32,
+ /* Unsigned 32-bit value. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BASE = 33,
+ /* Unsigned 32-bit value. For exponential back off bucket, number of
+ * scans to perform for a given period.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_STEP_COUNT = 34,
+ /* Unsigned 8-bit value; in number of scans, wake up AP after these
+ * many scans.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS
+ = 35,
+
+ /* Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST sub command.
+ */
+ /* Unsigned 3-2bit value; number of samples to confirm SSID loss. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE
+ = 36,
+ /* Number of hotlist SSIDs as unsigned 32-bit value, followed by a
+ * nested array of SSID_THRESHOLD_PARAM_* attributes and values. The
+ * size of the array is determined by NUM_SSID.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID = 37,
+ /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_*
+ * attributes.
+ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM = 38,
+
+ /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_SSID = 39,
+ /* Unsigned 8-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_BAND = 40,
+ /* Signed 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW = 41,
+ /* Signed 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH = 42,
+ /* Unsigned 32-bit value; a bitmask with additional gscan config flag.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CONFIGURATION_FLAGS = 43,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_MAX =
+ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_gscan_results {
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_INVALID = 0,
+
+ /* Unsigned 32-bit value; must match the request Id supplied by
+ * Wi-Fi HAL in the corresponding subcmd NL msg.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID = 1,
+
+ /* Unsigned 32-bit value; used to indicate the status response from
+ * firmware/driver for the vendor sub-command.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_STATUS = 2,
+
+ /* GSCAN Valid Channels attributes */
+ /* Unsigned 32bit value; followed by a nested array of CHANNELS. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS = 3,
+ /* An array of NUM_CHANNELS x unsigned 32-bit value integers
+ * representing channel numbers.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS = 4,
+
+ /* GSCAN Capabilities attributes */
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE = 5,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS = 6,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN
+ = 7,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE
+ = 8,
+ /* Signed 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD
+ = 9,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS = 10,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS
+ = 11,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES
+ = 12,
+
+ /* GSCAN Attributes used with
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE sub-command.
+ */
+
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE = 13,
+
+ /* GSCAN attributes used with
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT sub-command.
+ */
+
+ /* An array of NUM_RESULTS_AVAILABLE x
+ * QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_*
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST = 14,
+
+ /* Unsigned 64-bit value; age of sample at the time of retrieval */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP = 15,
+ /* 33 x unsigned 8-bit value; NULL terminated SSID */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID = 16,
+ /* An array of 6 x unsigned 8-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID = 17,
+ /* Unsigned 32-bit value; channel frequency in MHz */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL = 18,
+ /* Signed 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI = 19,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT = 20,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD = 21,
+ /* Unsigned 16-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD = 22,
+ /* Unsigned 16-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CAPABILITY = 23,
+ /* Unsigned 32-bit value; size of the IE DATA blob */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_LENGTH = 24,
+ /* An array of IE_LENGTH x unsigned 8-bit value; blob of all the
+ * information elements found in the beacon; this data should be a
+ * packed list of wifi_information_element objects, one after the
+ * other.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_DATA = 25,
+
+ /* Unsigned 8-bit value; set by driver to indicate more scan results are
+ * available.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA = 26,
+
+ /* GSCAN attributes for
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT sub-command.
+ */
+ /* Unsigned 8-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_TYPE = 27,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_STATUS = 28,
+
+ /* GSCAN attributes for
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND sub-command.
+ */
+ /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
+ * to indicate number of results.
+ * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the
+ * list of results.
+ */
+
+ /* GSCAN attributes for
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE sub-command.
+ */
+ /* An array of 6 x unsigned 8-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID = 29,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL
+ = 30,
+ /* Unsigned 32-bit value. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI
+ = 31,
+ /* A nested array of signed 32-bit RSSI values. Size of the array is
+ * determined by (NUM_RSSI of SIGNIFICANT_CHANGE_RESULT_NUM_RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST
+ = 32,
+
+ /* GSCAN attributes used with
+ * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS sub-command.
+ */
+ /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
+ * to indicate number of gscan cached results returned.
+ * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST to indicate
+ * the list of gscan cached results.
+ */
+
+ /* An array of NUM_RESULTS_AVAILABLE x
+ * QCA_NL80211_VENDOR_ATTR_GSCAN_CACHED_RESULTS_*
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST = 33,
+ /* Unsigned 32-bit value; a unique identifier for the scan unit. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID = 34,
+ /* Unsigned 32-bit value; a bitmask w/additional information about scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS = 35,
+ /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
+ * to indicate number of wifi scan results/bssids retrieved by the scan.
+ * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the
+ * list of wifi scan results returned for each cached result block.
+ */
+
+ /* GSCAN attributes for
+ * QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND sub-command.
+ */
+ /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE for
+ * number of results.
+ * Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested
+ * list of wifi scan results returned for each
+ * wifi_passpoint_match_result block.
+ * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE.
+ */
+
+ /* GSCAN attributes for
+ * QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND sub-command.
+ */
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES
+ = 36,
+ /* A nested array of
+ * QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_*
+ * attributes. Array size =
+ * *_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST = 37,
+
+ /* Unsigned 32-bit value; network block id for the matched network */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID = 38,
+ /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested
+ * list of wifi scan results returned for each
+ * wifi_passpoint_match_result block.
+ */
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN = 39,
+ /* An array size of PASSPOINT_MATCH_ANQP_LEN of unsigned 8-bit values;
+ * ANQP data in the information_element format.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP = 40,
+
+ /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS = 41,
+ /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS = 42,
+ /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID
+ = 43,
+ /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID
+ = 44,
+
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED = 45,
+
+ /* Unsigned 32-bit value; a GSCAN Capabilities attribute.
+ * This is used to limit the maximum number of BSSIDs while sending
+ * the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with attributes
+ * QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID and
+ * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID.
+ */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID = 46,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX =
+ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_pno_config_params {
+ QCA_WLAN_VENDOR_ATTR_PNO_INVALID = 0,
+ /* Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST sub command.
+ */
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM = 1,
+ /* Array of nested QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_*
+ * attributes. Array size =
+ * QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM.
+ */
+ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY = 2,
+
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID = 3,
+ /* An array of 256 x unsigned 8-bit value; NULL terminated UTF-8 encoded
+ * realm, 0 if unspecified.
+ */
+ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM = 4,
+ /* An array of 16 x unsigned 32-bit value; roaming consortium ids to
+ * match, 0 if unspecified.
+ */
+ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID = 5,
+ /* An array of 6 x unsigned 8-bit value; MCC/MNC combination, 0s if
+ * unspecified.
+ */
+ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN = 6,
+
+ /* Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST sub command.
+ */
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS = 7,
+ /* Array of nested
+ * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_*
+ * attributes. Array size =
+ * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS.
+ */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST = 8,
+ /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID = 9,
+ /* Signed 8-bit value; threshold for considering this SSID as found,
+ * required granularity for this threshold is 4 dBm to 8 dBm.
+ */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_RSSI_THRESHOLD
+ = 10,
+ /* Unsigned 8-bit value; WIFI_PNO_FLAG_XXX */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS = 11,
+ /* Unsigned 8-bit value; auth bit field for matching WPA IE */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT = 12,
+ /* Unsigned 8-bit to indicate ePNO type;
+ * It takes values from qca_wlan_epno_type
+ */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_TYPE = 13,
+
+ /* Nested attribute to send the channel list */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_CHANNEL_LIST = 14,
+
+ /* Unsigned 32-bit value; indicates the interval between PNO scan
+ * cycles in msec.
+ */
+ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_SCAN_INTERVAL = 15,
+ QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI = 16,
+ QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI = 17,
+ QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX = 18,
+ QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS = 19,
+ QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20,
+ QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21,
+ QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22,
+ /* Unsigned 32-bit value, representing the PNO Request ID */
+ QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID = 23,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_PNO_MAX =
+ QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_acs_select_reason: This represents the different reasons why
+ * the ACS has to be triggered. These values are used by
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON and
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON
+ */
+enum qca_wlan_vendor_acs_select_reason {
+ /* Represents the reason that the ACS triggered during the AP start */
+ QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT,
+ /* Represents the reason that DFS found with the current channel */
+ QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS,
+ /* Represents the reason that LTE co-exist in the current band. */
+ QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX,
+};
+
+/**
+ * qca_wlan_vendor_attr_external_acs_policy: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This represents the
+ * external ACS policies to select the channels w.r.t. the PCL weights.
+ * (QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL represents the channels and
+ * their PCL weights.)
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY: Mandatory to
+ * select a channel with non-zero PCL weight.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED: Prefer a
+ * channel with non-zero PCL weight.
+ *
+ */
+enum qca_wlan_vendor_attr_external_acs_policy {
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY,
+};
+
+/**
+ * qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel.
+ * This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS.
+ */
+enum qca_wlan_vendor_channel_prop_flags {
+ /* Bits 0, 1, 2, and 3 are reserved */
+
+ /* Turbo channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_TURBO = 1 << 4,
+ /* CCK channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_CCK = 1 << 5,
+ /* OFDM channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_OFDM = 1 << 6,
+ /* 2.4 GHz spectrum channel. */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_2GHZ = 1 << 7,
+ /* 5 GHz spectrum channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_5GHZ = 1 << 8,
+ /* Only passive scan allowed */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_PASSIVE = 1 << 9,
+ /* Dynamic CCK-OFDM channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_DYN = 1 << 10,
+ /* GFSK channel (FHSS PHY) */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_GFSK = 1 << 11,
+ /* Radar found on channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_RADAR = 1 << 12,
+ /* 11a static turbo channel only */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_STURBO = 1 << 13,
+ /* Half rate channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HALF = 1 << 14,
+ /* Quarter rate channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_QUARTER = 1 << 15,
+ /* HT 20 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT20 = 1 << 16,
+ /* HT 40 with extension channel above */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40PLUS = 1 << 17,
+ /* HT 40 with extension channel below */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40MINUS = 1 << 18,
+ /* HT 40 intolerant */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOL = 1 << 19,
+ /* VHT 20 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT20 = 1 << 20,
+ /* VHT 40 with extension channel above */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40PLUS = 1 << 21,
+ /* VHT 40 with extension channel below */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40MINUS = 1 << 22,
+ /* VHT 80 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80 = 1 << 23,
+ /* HT 40 intolerant mark bit for ACS use */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOLMARK = 1 << 24,
+ /* Channel temporarily blocked due to noise */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_BLOCKED = 1 << 25,
+ /* VHT 160 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160 = 1 << 26,
+ /* VHT 80+80 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80 = 1 << 27,
+ /* HE 20 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE20 = 1 << 28,
+ /* HE 40 with extension channel above */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40PLUS = 1 << 29,
+ /* HE 40 with extension channel below */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS = 1 << 30,
+ /* HE 40 intolerant */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL = 1 << 31,
+};
+
+/**
+ * qca_wlan_vendor_channel_prop_flags_2: This represents the flags for a
+ * channel, and is a continuation of qca_wlan_vendor_channel_prop_flags. This is
+ * used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2.
+ */
+enum qca_wlan_vendor_channel_prop_flags_2 {
+ /* HE 40 intolerant mark bit for ACS use */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOLMARK = 1 << 0,
+ /* HE 80 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80 = 1 << 1,
+ /* HE 160 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE160 = 1 << 2,
+ /* HE 80+80 channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80_80 = 1 << 3,
+};
+
+/**
+ * qca_wlan_vendor_channel_prop_flags_ext: This represent the extended flags for
+ * each channel. This is used by
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT.
+ */
+enum qca_wlan_vendor_channel_prop_flags_ext {
+ /* Radar found on channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_RADAR_FOUND = 1 << 0,
+ /* DFS required on channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS = 1 << 1,
+ /* DFS required on channel for 2nd band of 80+80 */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CFREQ2 = 1 << 2,
+ /* If channel has been checked for DFS */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CLEAR = 1 << 3,
+ /* Excluded in 802.11d */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_11D_EXCLUDED = 1 << 4,
+ /* Channel Switch Announcement received on this channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CSA_RECEIVED = 1 << 5,
+ /* Ad-hoc is not allowed */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC = 1 << 6,
+ /* Station only channel */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7,
+ /* DFS radar history for slave device (STA mode) */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR = 1 << 8,
+ /* DFS CAC valid for slave device (STA mode) */
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID = 1 << 9,
+};
+
+/**
+ * qca_wlan_vendor_external_acs_event_chan_info_attr: Represents per channel
+ * information. These attributes are sent as part of
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO. Each set of the following
+ * attributes correspond to a single channel.
+ */
+enum qca_wlan_vendor_external_acs_event_chan_info_attr {
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_INVALID = 0,
+
+ /* A bitmask (u32) with flags specified in
+ * enum qca_wlan_vendor_channel_prop_flags.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS = 1,
+ /* A bitmask (u32) with flags specified in
+ * enum qca_wlan_vendor_channel_prop_flags_ext.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT = 2,
+ /* frequency in MHz (u32) */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ = 3,
+ /* maximum regulatory transmission power (u32) */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER = 4,
+ /* maximum transmission power (u32) */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER = 5,
+ /* minimum transmission power (u32) */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER = 6,
+ /* regulatory class id (u8) */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID = 7,
+ /* maximum antenna gain in (u8) */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN = 8,
+ /* VHT segment 0 (u8) */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9,
+ /* VHT segment 1 (u8) */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10,
+ /* A bitmask (u32) with flags specified in
+ * enum qca_wlan_vendor_channel_prop_flags_2.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_pcl: Represents attributes for
+ * preferred channel list (PCL). These attributes are sent as part of
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL and
+ * QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST.
+ */
+enum qca_wlan_vendor_attr_pcl {
+ QCA_WLAN_VENDOR_ATTR_PCL_INVALID = 0,
+
+ /* Channel number (u8) */
+ QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL = 1,
+ /* Channel weightage (u8) */
+ QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT = 2,
+ /* Channel frequency (u32) in MHz */
+ QCA_WLAN_VENDOR_ATTR_PCL_FREQ = 3,
+ /* Channel flags (u32)
+ * bit 0 set: channel to be used for GO role,
+ * bit 1 set: channel to be used on CLI role,
+ * bit 2 set: channel must be considered for operating channel
+ * selection & peer chosen operating channel should be
+ * one of the channels with this flag set,
+ * bit 3 set: channel should be excluded in GO negotiation
+ */
+ QCA_WLAN_VENDOR_ATTR_PCL_FLAG = 4,
+};
+
+/**
+ * qca_wlan_vendor_attr_external_acs_event: Attribute to vendor sub-command
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This attribute will be sent by
+ * host driver.
+ */
+enum qca_wlan_vendor_attr_external_acs_event {
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_INVALID = 0,
+
+ /* This reason (u8) refers to enum qca_wlan_vendor_acs_select_reason.
+ * This helps ACS module to understand why ACS needs to be started.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON = 1,
+ /* Flag attribute to indicate if driver supports spectral scanning */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED = 2,
+ /* Flag attribute to indicate if 11ac is offloaded to firmware */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3,
+ /* Flag attribute to indicate if driver provides additional channel
+ * capability as part of scan operation
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4,
+ /* Flag attribute to indicate interface status is UP */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AP_UP = 5,
+ /* Operating mode (u8) of interface. Takes one of enum nl80211_iftype
+ * values.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 6,
+ /* Channel width (u8). It takes one of enum nl80211_chan_width values.
+ * This is the upper bound of channel width. ACS logic should try to get
+ * a channel with the specified width and if not found, look for lower
+ * values.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH = 7,
+ /* This (u8) will hold values of one of enum nl80211_bands */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 8,
+ /* PHY/HW mode (u8). Takes one of enum qca_wlan_vendor_acs_hw_mode
+ * values
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 9,
+ /* Array of (u32) supported frequency list among which ACS should choose
+ * best frequency.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST = 10,
+ /* Preferred channel list by the driver which will have array of nested
+ * values as per enum qca_wlan_vendor_attr_pcl attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL = 11,
+ /* Array of nested attribute for each channel. It takes attr as defined
+ * in enum qca_wlan_vendor_external_acs_event_chan_info_attr.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12,
+ /* External ACS policy such as PCL mandatory, PCL preferred, etc.
+ * It uses values defined in enum
+ * qca_wlan_vendor_attr_external_acs_policy.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13,
+ /* Reference RF Operating Parameter (RROP) availability information
+ * (u16). It uses values defined in enum
+ * qca_wlan_vendor_attr_rropavail_info.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_RROPAVAIL_INFO = 14,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAX =
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carries a list of channels
+ * in priority order as decided after ACS operation in userspace.
+ */
+enum qca_wlan_vendor_attr_external_acs_channels {
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
+
+ /* One of reason code (u8) from enum qca_wlan_vendor_acs_select_reason
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON = 1,
+
+ /* Array of nested values for each channel with following attributes:
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1,
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST = 2,
+ /* This (u8) will hold values of one of enum nl80211_bands */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND = 3,
+ /* Primary channel (u8) */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY = 4,
+ /* Secondary channel (u8) used for HT 40 MHz channels */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY = 5,
+ /* VHT seg0 channel (u8) */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 = 6,
+ /* VHT seg1 channel (u8) */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 = 7,
+ /* Channel width (u8). Takes one of enum nl80211_chan_width values. */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX =
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST - 1
+};
+
+enum qca_chip_power_save_failure_reason {
+ /* Indicates if the reason for the failure is due to a protocol
+ * layer/module.
+ */
+ QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0,
+ /* Indicates if the reason for the failure is due to a hardware issue.
+ */
+ QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1,
+};
+
+/**
+ * qca_attr_chip_power_save_failure: Attributes to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE. This carries the requisite
+ * information leading to the power save failure.
+ */
+enum qca_attr_chip_power_save_failure {
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0,
+ /* Reason to cause the power save failure.
+ * These reasons are represented by
+ * enum qca_chip_power_save_failure_reason.
+ */
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1,
+
+ /* keep last */
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST,
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX =
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_nud_stats_data_pkt_flags: Flag representing the various
+ * data types for which the stats have to get collected.
+ */
+enum qca_wlan_vendor_nud_stats_data_pkt_flags {
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_ARP = 1 << 0,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_DNS = 1 << 1,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_HANDSHAKE = 1 << 2,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV4 = 1 << 3,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV6 = 1 << 4,
+ /* Used by QCA_ATTR_NUD_STATS_PKT_TYPE only in nud stats get
+ * to represent the stats of respective data type.
+ */
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN = 1 << 5,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN_ACK = 1 << 6,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_ACK = 1 << 7,
+};
+
+enum qca_wlan_vendor_nud_stats_set_data_pkt_info {
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_INVALID = 0,
+ /* Represents the data packet type to be monitored (u32).
+ * Host driver tracks the stats corresponding to each data frame
+ * represented by these flags.
+ * These data packets are represented by
+ * enum qca_wlan_vendor_nud_stats_data_pkt_flags
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE = 1,
+ /* Name corresponding to the DNS frame for which the respective DNS
+ * stats have to get monitored (string). Max string length 255.
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME = 2,
+ /* source port on which the respective proto stats have to get
+ * collected (u32).
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT = 3,
+ /* destination port on which the respective proto stats have to get
+ * collected (u32).
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT = 4,
+ /* IPv4 address for which the destined data packets have to be
+ * monitored. (in network byte order), u32.
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4 = 5,
+ /* IPv6 address for which the destined data packets have to be
+ * monitored. (in network byte order), 16 bytes array.
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6 = 6,
+
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST,
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_MAX =
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_nud_stats_set: Attributes to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET. This carries the requisite
+ * information to start/stop the NUD statistics collection.
+ */
+enum qca_attr_nud_stats_set {
+ QCA_ATTR_NUD_STATS_SET_INVALID = 0,
+
+ /* Flag to start/stop the NUD statistics collection.
+ * Start - If included, Stop - If not included
+ */
+ QCA_ATTR_NUD_STATS_SET_START = 1,
+ /* IPv4 address of the default gateway (in network byte order), u32 */
+ QCA_ATTR_NUD_STATS_GW_IPV4 = 2,
+ /* Represents the list of data packet types to be monitored.
+ * Host driver tracks the stats corresponding to each data frame
+ * represented by these flags.
+ * These data packets are represented by
+ * enum qca_wlan_vendor_nud_stats_set_data_pkt_info
+ */
+ QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO = 3,
+
+ /* keep last */
+ QCA_ATTR_NUD_STATS_SET_LAST,
+ QCA_ATTR_NUD_STATS_SET_MAX =
+ QCA_ATTR_NUD_STATS_SET_LAST - 1,
+};
+
+enum qca_attr_nud_data_stats {
+ QCA_ATTR_NUD_DATA_STATS_INVALID = 0,
+ /* Data packet type for which the stats are collected (u32).
+ * Represented by enum qca_wlan_vendor_nud_stats_data_pkt_flags
+ */
+ QCA_ATTR_NUD_STATS_PKT_TYPE = 1,
+ /* Name corresponding to the DNS frame for which the respective DNS
+ * stats are monitored (string). Max string length 255.
+ */
+ QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME = 2,
+ /* source port on which the respective proto stats are collected (u32).
+ */
+ QCA_ATTR_NUD_STATS_PKT_SRC_PORT = 3,
+ /* destination port on which the respective proto stats are collected
+ * (u32).
+ */
+ QCA_ATTR_NUD_STATS_PKT_DEST_PORT = 4,
+ /* IPv4 address for which the destined data packets have to be
+ * monitored. (in network byte order), u32.
+ */
+ QCA_ATTR_NUD_STATS_PKT_DEST_IPV4 = 5,
+ /* IPv6 address for which the destined data packets have to be
+ * monitored. (in network byte order), 16 bytes array.
+ */
+ QCA_ATTR_NUD_STATS_PKT_DEST_IPV6 = 6,
+ /* Data packet Request count received from netdev (u32). */
+ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV = 7,
+ /* Data packet Request count sent to lower MAC from upper MAC (u32). */
+ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC = 8,
+ /* Data packet Request count received by lower MAC from upper MAC
+ * (u32)
+ */
+ QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC = 9,
+ /* Data packet Request count successfully transmitted by the device
+ * (u32)
+ */
+ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS = 10,
+ /* Data packet Response count received by lower MAC (u32) */
+ QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC = 11,
+ /* Data packet Response count received by upper MAC (u32) */
+ QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC = 12,
+ /* Data packet Response count delivered to netdev (u32) */
+ QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV = 13,
+ /* Data Packet Response count that are dropped out of order (u32) */
+ QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP = 14,
+
+ /* keep last */
+ QCA_ATTR_NUD_DATA_STATS_LAST,
+ QCA_ATTR_NUD_DATA_STATS_MAX =
+ QCA_ATTR_NUD_DATA_STATS_LAST - 1,
+};
+
+/**
+ * qca_attr_nud_stats_get: Attributes to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET. This carries the requisite
+ * NUD statistics collected when queried.
+ */
+enum qca_attr_nud_stats_get {
+ QCA_ATTR_NUD_STATS_GET_INVALID = 0,
+ /* ARP Request count from netdev (u32) */
+ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV = 1,
+ /* ARP Request count sent to lower MAC from upper MAC (u32) */
+ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC = 2,
+ /* ARP Request count received by lower MAC from upper MAC (u32) */
+ QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC = 3,
+ /* ARP Request count successfully transmitted by the device (u32) */
+ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS = 4,
+ /* ARP Response count received by lower MAC (u32) */
+ QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC = 5,
+ /* ARP Response count received by upper MAC (u32) */
+ QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC = 6,
+ /* ARP Response count delivered to netdev (u32) */
+ QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV = 7,
+ /* ARP Response count dropped due to out of order reception (u32) */
+ QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP = 8,
+ /* Flag indicating if the station's link to the AP is active.
+ * Active Link - If included, Inactive link - If not included
+ */
+ QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE = 9,
+ /* Flag indicating if there is any duplicate address detected (DAD).
+ * Yes - If detected, No - If not detected.
+ */
+ QCA_ATTR_NUD_STATS_IS_DAD = 10,
+ /* List of Data packet types for which the stats are requested.
+ * This list does not carry ARP stats as they are done by the
+ * above attributes. Represented by enum qca_attr_nud_data_stats.
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_STATS = 11,
+
+ /* keep last */
+ QCA_ATTR_NUD_STATS_GET_LAST,
+ QCA_ATTR_NUD_STATS_GET_MAX =
+ QCA_ATTR_NUD_STATS_GET_LAST - 1,
+};
+
+enum qca_wlan_btm_candidate_status {
+ QCA_STATUS_ACCEPT = 0,
+ QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED = 1,
+ QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED = 2,
+ QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY = 3,
+ QCA_STATUS_REJECT_LOW_RSSI = 4,
+ QCA_STATUS_REJECT_HIGH_INTERFERENCE = 5,
+ QCA_STATUS_REJECT_UNKNOWN = 6,
+};
+
+enum qca_wlan_vendor_attr_btm_candidate_info {
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_INVALID = 0,
+
+ /* 6-byte MAC address representing the BSSID of transition candidate */
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID = 1,
+ /* Unsigned 32-bit value from enum qca_wlan_btm_candidate_status
+ * returned by the driver. It says whether the BSSID provided in
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID is acceptable by
+ * the driver, if not it specifies the reason for rejection.
+ * Note that the user-space can overwrite the transition reject reason
+ * codes provided by driver based on more information.
+ */
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST - 1,
+};
+
+enum qca_attr_trace_level {
+ QCA_ATTR_TRACE_LEVEL_INVALID = 0,
+ /*
+ * Nested array of the following attributes:
+ * QCA_ATTR_TRACE_LEVEL_MODULE,
+ * QCA_ATTR_TRACE_LEVEL_MASK.
+ */
+ QCA_ATTR_TRACE_LEVEL_PARAM = 1,
+ /*
+ * Specific QCA host driver module. Please refer to the QCA host
+ * driver implementation to get the specific module ID.
+ */
+ QCA_ATTR_TRACE_LEVEL_MODULE = 2,
+ /* Different trace level masks represented in the QCA host driver. */
+ QCA_ATTR_TRACE_LEVEL_MASK = 3,
+
+ /* keep last */
+ QCA_ATTR_TRACE_LEVEL_AFTER_LAST,
+ QCA_ATTR_TRACE_LEVEL_MAX =
+ QCA_ATTR_TRACE_LEVEL_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_he_capabilities - IEEE 802.11ax HE capabilities
+ */
+enum qca_wlan_vendor_attr_get_he_capabilities {
+ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0,
+ /* Whether HE capabilities is supported
+ * (u8 attribute: 0 = not supported, 1 = supported)
+ */
+ QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED = 1,
+ /* HE PHY capabilities, array of 3 u32 values */
+ QCA_WLAN_VENDOR_ATTR_PHY_CAPAB = 2,
+ /* HE MAC capabilities (u32 attribute) */
+ QCA_WLAN_VENDOR_ATTR_MAC_CAPAB = 3,
+ /* HE MCS map (u32 attribute) */
+ QCA_WLAN_VENDOR_ATTR_HE_MCS = 4,
+ /* Number of SS (u32 attribute) */
+ QCA_WLAN_VENDOR_ATTR_NUM_SS = 5,
+ /* RU count (u32 attribute) */
+ QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK = 6,
+ /* PPE threshold data, array of 8 u32 values */
+ QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX =
+ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_spectral_scan - Spectral scan config parameters
+ */
+enum qca_wlan_vendor_attr_spectral_scan {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INVALID = 0,
+ /* Number of times the chip enters spectral scan mode before
+ * deactivating spectral scans. When set to 0, chip will enter spectral
+ * scan mode continuously. u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_COUNT = 1,
+ /* Spectral scan period. Period increment resolution is 256*Tclk,
+ * where Tclk = 1/44 MHz (Gmode), 1/40 MHz (Amode). u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_PERIOD = 2,
+ /* Spectral scan priority. u32 attribute. */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PRIORITY = 3,
+ /* Number of FFT data points to compute. u32 attribute. */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_SIZE = 4,
+ /* Enable targeted gain change before starting the spectral scan FFT.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_GC_ENA = 5,
+ /* Restart a queued spectral scan. u32 attribute. */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RESTART_ENA = 6,
+ /* Noise floor reference number for the calculation of bin power.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NOISE_FLOOR_REF = 7,
+ /* Disallow spectral scan triggers after TX/RX packets by setting
+ * this delay value to roughly SIFS time period or greater.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INIT_DELAY = 8,
+ /* Number of strong bins (inclusive) per sub-channel, below
+ * which a signal is declared a narrow band tone. u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NB_TONE_THR = 9,
+ /* Specify the threshold over which a bin is declared strong (for
+ * scan bandwidth analysis). u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_STR_BIN_THR = 10,
+ /* Spectral scan report mode. u32 attribute. */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_WB_RPT_MODE = 11,
+ /* RSSI report mode, if the ADC RSSI is below
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR,
+ * then FFTs will not trigger, but timestamps and summaries get
+ * reported. u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_RPT_MODE = 12,
+ /* ADC RSSI must be greater than or equal to this threshold (signed dB)
+ * to ensure spectral scan reporting with normal error code.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR = 13,
+ /* Format of frequency bin magnitude for spectral scan triggered FFTs:
+ * 0: linear magnitude, 1: log magnitude (20*log10(lin_mag)).
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PWR_FORMAT = 14,
+ /* Format of FFT report to software for spectral scan triggered FFTs.
+ * 0: No FFT report (only spectral scan summary report)
+ * 1: 2-dword summary of metrics for each completed FFT + spectral scan
+ * report
+ * 2: 2-dword summary of metrics for each completed FFT + 1x-oversampled
+ * bins (in-band) per FFT + spectral scan summary report
+ * 3: 2-dword summary of metrics for each completed FFT + 2x-oversampled
+ * bins (all) per FFT + spectral scan summary report
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RPT_MODE = 15,
+ /* Number of LSBs to shift out in order to scale the FFT bins.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BIN_SCALE = 16,
+ /* Set to 1 (with spectral_scan_pwr_format=1), to report bin magnitudes
+ * in dBm power. u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DBM_ADJ = 17,
+ /* Per chain enable mask to select input ADC for search FFT.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_CHN_MASK = 18,
+ /* An unsigned 64-bit integer provided by host driver to identify the
+ * spectral scan request. This attribute is included in the scan
+ * response message for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START
+ * and used as an attribute in
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP to identify the
+ * specific scan to be stopped.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19,
+ /* Skip interval for FFT reports. u32 attribute */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_PERIOD = 20,
+ /* Set to report only one set of FFT results.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SHORT_REPORT = 21,
+ /* Debug level for spectral module in driver.
+ * 0 : Verbosity level 0
+ * 1 : Verbosity level 1
+ * 2 : Verbosity level 2
+ * 3 : Matched filterID display
+ * 4 : One time dump of FFT report
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DEBUG_LEVEL = 22,
+ /* Type of spectral scan request. u32 attribute.
+ * It uses values defined in enum
+ * qca_wlan_vendor_attr_spectral_scan_request_type.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_spectral_diag_stats - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS.
+ */
+enum qca_wlan_vendor_attr_spectral_diag_stats {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_INVALID = 0,
+ /* Number of spectral TLV signature mismatches.
+ * u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SIG_MISMATCH = 1,
+ /* Number of spectral phyerror events with insufficient length when
+ * parsing for secondary 80 search FFT report. u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SEC80_SFFT_INSUFFLEN = 2,
+ /* Number of spectral phyerror events without secondary 80
+ * search FFT report. u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_NOSEC80_SFFT = 3,
+ /* Number of spectral phyerror events with vht operation segment 1 id
+ * mismatches in search fft report. u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG1ID_MISMATCH = 4,
+ /* Number of spectral phyerror events with vht operation segment 2 id
+ * mismatches in search fft report. u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG2ID_MISMATCH = 5,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_spectral_cap - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO.
+ */
+enum qca_wlan_vendor_attr_spectral_cap {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_INVALID = 0,
+ /* Flag attribute to indicate phydiag capability */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_PHYDIAG = 1,
+ /* Flag attribute to indicate radar detection capability */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RADAR = 2,
+ /* Flag attribute to indicate spectral capability */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_SPECTRAL = 3,
+ /* Flag attribute to indicate advanced spectral capability */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_ADVANCED_SPECTRAL = 4,
+ /* Spectral hardware generation. u32 attribute.
+ * It uses values defined in enum
+ * qca_wlan_vendor_spectral_scan_cap_hw_gen.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_spectral_scan_status - used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS.
+ */
+enum qca_wlan_vendor_attr_spectral_scan_status {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_INVALID = 0,
+ /* Flag attribute to indicate whether spectral scan is enabled */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1,
+ /* Flag attribute to indicate whether spectral scan is in progress*/
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_spectral_scan_request_type: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. This represents the
+ * spectral scan request types.
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG: Request to
+ * set the spectral parameters and start scan.
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN: Request to
+ * only set the spectral parameters.
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG: Request to
+ * only start the spectral scan.
+ */
+enum qca_wlan_vendor_attr_spectral_scan_request_type {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG,
+};
+
+/**
+ * qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the
+ * spectral hardware generation.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1: generation 1
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2: generation 2
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3: generation 3
+ */
+enum qca_wlan_vendor_spectral_scan_cap_hw_gen {
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1 = 0,
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2 = 1,
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3 = 2,
+};
+
+enum qca_wlan_vendor_tos {
+ QCA_WLAN_VENDOR_TOS_BK = 0,
+ QCA_WLAN_VENDOR_TOS_BE = 1,
+ QCA_WLAN_VENDOR_TOS_VI = 2,
+ QCA_WLAN_VENDOR_TOS_VO = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_active_tos - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS.
+ */
+enum qca_wlan_vendor_attr_active_tos {
+ QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_INVALID = 0,
+ /* Type Of Service - Represented by qca_wlan_vendor_tos */
+ QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS = 1,
+ /* Flag attribute representing the start (attribute included) or stop
+ * (attribute not included) of the respective TOS.
+ */
+ QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START = 2,
+};
+
+enum qca_wlan_vendor_hang_reason {
+ /* Unspecified reason */
+ QCA_WLAN_HANG_REASON_UNSPECIFIED = 0,
+ /* No Map for the MAC entry for the received frame */
+ QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND = 1,
+ /* Peer deletion timeout happened */
+ QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT = 2,
+ /* Peer unmap timeout */
+ QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT = 3,
+ /* Scan request timed out */
+ QCA_WLAN_HANG_SCAN_REQ_EXPIRED = 4,
+ /* Consecutive Scan attempt failures */
+ QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES = 5,
+ /* Unable to get the message buffer */
+ QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE = 6,
+ /* Current command processing is timedout */
+ QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT = 7,
+ /* Timeout for an ACK from FW for suspend request */
+ QCA_WLAN_HANG_SUSPEND_TIMEOUT = 8,
+ /* Timeout for an ACK from FW for resume request */
+ QCA_WLAN_HANG_RESUME_TIMEOUT = 9,
+ /* Transmission timeout for consecutive data frames */
+ QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT = 10,
+ /* Timeout for the TX completion status of data frame */
+ QCA_WLAN_HANG_TX_COMPLETE_TIMEOUT = 11,
+ /* DXE failure for TX/RX, DXE resource unavailability */
+ QCA_WLAN_HANG_DXE_FAILURE = 12,
+ /* WMI pending commands exceed the maximum count */
+ QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS = 13,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_hang - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_HANG.
+ */
+enum qca_wlan_vendor_attr_hang {
+ QCA_WLAN_VENDOR_ATTR_HANG_INVALID = 0,
+ /* Reason for the hang - u32 attribute with a value from enum
+ * qca_wlan_vendor_hang_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_HANG_REASON = 1,
+
+ QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_HANG_MAX =
+ QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_flush_pending - Attributes for
+ * flushing pending traffic in firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_ADDR: Configure peer MAC address.
+ * @QCA_WLAN_VENDOR_ATTR_AC: Configure access category of the pending
+ * packets. It is u8 value with bit 0~3 represent AC_BE, AC_BK,
+ * AC_VI, AC_VO respectively. Set the corresponding bit to 1 to
+ * flush packets with access category.
+ */
+enum qca_wlan_vendor_attr_flush_pending {
+ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_PEER_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_AC = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative
+ * RF Operating Parameter (RROP) information is available, and if so, at which
+ * point in the application-driver interaction sequence it can be retrieved by
+ * the application from the driver. This point may vary by architecture and
+ * other factors. This is a u16 value.
+ */
+enum qca_wlan_vendor_attr_rropavail_info {
+ /* RROP information is unavailable. */
+ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_UNAVAILABLE,
+ /* RROP information is available and the application can retrieve the
+ * information after receiving an QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS
+ * event from the driver.
+ */
+ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_EXTERNAL_ACS_START,
+ /* RROP information is available only after a vendor specific scan
+ * (requested using QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) has
+ * successfully completed. The application can retrieve the information
+ * after receiving the QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE event from
+ * the driver.
+ */
+ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_VSCAN_END,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rrop_info - Specifies vendor specific
+ * Representative RF Operating Parameter (RROP) information. It is sent for the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO. This information is
+ * intended for use by external Auto Channel Selection applications. It provides
+ * guidance values for some RF parameters that are used by the system during
+ * operation. These values could vary by channel, band, radio, and so on.
+ */
+enum qca_wlan_vendor_attr_rrop_info {
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_INVALID = 0,
+
+ /* Representative Tx Power List (RTPL) which has an array of nested
+ * values as per attributes in enum qca_wlan_vendor_attr_rtplinst.
+ */
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_RTPL = 1,
+
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rtplinst - Specifies attributes for individual list
+ * entry instances in the Representative Tx Power List (RTPL). It provides
+ * simplified power values intended for helping external Auto channel Selection
+ * applications compare potential Tx power performance between channels, other
+ * operating conditions remaining identical. These values are not necessarily
+ * the actual Tx power values that will be used by the system. They are also not
+ * necessarily the max or average values that will be used. Instead, they are
+ * relative, summarized keys for algorithmic use computed by the driver or
+ * underlying firmware considering a number of vendor specific factors.
+ */
+enum qca_wlan_vendor_attr_rtplinst {
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_INVALID = 0,
+
+ /* Primary channel number (u8) */
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY = 1,
+ /* Representative Tx power in dBm (s32) with emphasis on throughput. */
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_THROUGHPUT = 2,
+ /* Representative Tx power in dBm (s32) with emphasis on range. */
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_RANGE = 3,
+
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_MAX =
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_latency_level - Level for
+ * wlan latency module.
+ *
+ * There will be various of Wi-Fi functionality like scan/roaming/adaptive
+ * power saving which would causing data exchange out of service, this
+ * would be a big impact on latency. For latency sensitive applications over
+ * Wi-Fi are intolerant to such operations and thus would configure them
+ * to meet their respective needs. It is well understood by such applications
+ * that altering the default behavior would degrade the Wi-Fi functionality
+ * w.r.t the above pointed WLAN operations.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL:
+ * Default WLAN operation level which throughput orientated.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE:
+ * Use moderate level to improve latency by limit scan duration.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW:
+ * Use low latency level to benifit application like concurrent
+ * downloading or video streaming via constraint scan/adaptive PS.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW:
+ * Use ultra low latency level to benefit for gaming/voice
+ * application via constraint scan/roaming/adaptive PS.
+ */
+enum qca_wlan_vendor_attr_config_latency_level {
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL = 1,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE = 2,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW = 3,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MAX =
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_wlan_mac - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO.
+ */
+enum qca_wlan_vendor_attr_mac {
+ QCA_WLAN_VENDOR_ATTR_MAC_INVALID = 0,
+
+ /* MAC mode info list which has an array of nested values as
+ * per attributes in enum qca_wlan_vendor_attr_mac_mode_info.
+ */
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAC_MAX =
+ QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mac_iface_info - Information of the connected
+ * Wi-Fi netdev interface on a respective MAC.
+ * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO.
+ */
+enum qca_wlan_vendor_attr_mac_iface_info {
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_INVALID = 0,
+ /* Wi-Fi netdev's interface index (u32) */
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX = 1,
+ /* Associated frequency in MHz of the connected Wi-Fi interface (u32) */
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mac_info - Points to MAC the information.
+ * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_INFO of the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO.
+ */
+enum qca_wlan_vendor_attr_mac_info {
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_INVALID = 0,
+ /* Hardware MAC ID associated for the MAC (u32) */
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID = 1,
+ /* Band supported by the MAC at a given point.
+ * This is a u32 bitmask of BIT(NL80211_BAND_*) as described in %enum
+ * nl80211_band.
+ */
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND = 2,
+ /* Refers to list of WLAN netdev interfaces associated with this MAC.
+ * Represented by enum qca_wlan_vendor_attr_mac_iface_info.
+ */
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_logger_features - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET.
+ */
+enum qca_wlan_vendor_attr_get_logger_features {
+ QCA_WLAN_VENDOR_ATTR_LOGGER_INVALID = 0,
+ /* Unsigned 32-bit enum value of wifi_logger_supported_features */
+ QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED = 1,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LOGGER_MAX =
+ QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST - 1,
+};
+
+/**
+ * enum wifi_logger_supported_features - Values for supported logger features
+ */
+enum wifi_logger_supported_features {
+ WIFI_LOGGER_MEMORY_DUMP_FEATURE = (1 << (0)),
+ WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_FEATURE = (1 << (1)),
+ WIFI_LOGGER_CONNECT_EVENT_FEATURE = (1 << (2)),
+ WIFI_LOGGER_POWER_EVENT_FEATURE = (1 << (3)),
+ WIFI_LOGGER_WAKE_LOCK_FEATURE = (1 << (4)),
+ WIFI_LOGGER_VERBOSE_FEATURE = (1 << (5)),
+ WIFI_LOGGER_WATCHDOG_TIMER_FEATURE = (1 << (6)),
+ WIFI_LOGGER_DRIVER_DUMP_FEATURE = (1 << (7)),
+ WIFI_LOGGER_PACKET_FATE_FEATURE = (1 << (8)),
+};
+
+/**
+ * enum qca_wlan_tdls_caps_features_supported - Values for TDLS get
+ * capabilities features
+ */
+enum qca_wlan_tdls_caps_features_supported {
+ WIFI_TDLS_SUPPORT = (1 << (0)),
+ WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT = (1 << (1)),
+ WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2))
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_get_capabilities - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES.
+ */
+enum qca_wlan_vendor_attr_tdls_get_capabilities {
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_INVALID = 0,
+ /* Indicates the max concurrent sessions */
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS,
+ /* Indicates the support for features */
+ /* Unsigned 32-bit bitmap qca_wlan_tdls_caps_features_supported
+ */
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_offloaded_packets_sending_control - Offload packets control
+ * command used as value for the attribute
+ * QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL.
+ */
+enum qca_wlan_offloaded_packets_sending_control {
+ QCA_WLAN_OFFLOADED_PACKETS_SENDING_CONTROL_INVALID = 0,
+ QCA_WLAN_OFFLOADED_PACKETS_SENDING_START,
+ QCA_WLAN_OFFLOADED_PACKETS_SENDING_STOP
+};
+
+/**
+ * enum qca_wlan_vendor_attr_offloaded_packets - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS.
+ */
+enum qca_wlan_vendor_attr_offloaded_packets {
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID = 0,
+ /* Takes valid value from the enum
+ * qca_wlan_offloaded_packets_sending_control
+ * Unsigned 32-bit value
+ */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID,
+ /* array of u8 len: Max packet size */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA,
+ /* 6-byte MAC address used to represent source MAC address */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR,
+ /* 6-byte MAC address used to represent destination MAC address */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR,
+ /* Unsigned 32-bit value, in milli seconds */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX =
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_rssi_monitoring_control - RSSI control commands used as values
+ * by the attribute QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL.
+ */
+enum qca_wlan_rssi_monitoring_control {
+ QCA_WLAN_RSSI_MONITORING_CONTROL_INVALID = 0,
+ QCA_WLAN_RSSI_MONITORING_START,
+ QCA_WLAN_RSSI_MONITORING_STOP,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rssi_monitoring - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI.
+ */
+enum qca_wlan_vendor_attr_rssi_monitoring {
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_INVALID = 0,
+ /* Takes valid value from the enum
+ * qca_wlan_rssi_monitoring_control
+ * Unsigned 32-bit value enum qca_wlan_rssi_monitoring_control
+ */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
+ /* Signed 8-bit value in dBm */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI,
+ /* Signed 8-bit value in dBm */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI,
+ /* attributes to be used/received in callback */
+ /* 6-byte MAC address used to represent current BSSID MAC address */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID,
+ /* Signed 8-bit value indicating the current RSSI */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX =
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ndp_params - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_NDP.
+ */
+enum qca_wlan_vendor_attr_ndp_params {
+ QCA_WLAN_VENDOR_ATTR_NDP_PARAM_INVALID = 0,
+ /* Unsigned 32-bit value
+ * enum of sub commands values in qca_wlan_ndp_sub_cmd
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD,
+ /* Unsigned 16-bit value */
+ QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
+ /* NL attributes for data used NDP SUB cmds */
+ /* Unsigned 32-bit value indicating a service info */
+ QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID,
+ /* Unsigned 32-bit value; channel frequency in MHz */
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL,
+ /* Interface Discovery MAC address. An array of 6 Unsigned int8 */
+ QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR,
+ /* Interface name on which NDP is being created */
+ QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR,
+ /* Unsigned 32-bit value for security */
+ /* CONFIG_SECURITY is deprecated, use NCS_SK_TYPE/PMK/SCID instead */
+ QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY,
+ /* Unsigned 32-bit value for QoS */
+ QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS,
+ /* Array of u8: len = QCA_WLAN_VENDOR_ATTR_NAN_DP_APP_INFO_LEN */
+ QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO,
+ /* Unsigned 32-bit value for NDP instance Id */
+ QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID,
+ /* Array of instance Ids */
+ QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY,
+ /* Unsigned 32-bit value for initiator/responder NDP response code
+ * accept/reject
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE,
+ /* NDI MAC address. An array of 6 Unsigned int8 */
+ QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR,
+ /* Unsigned 32-bit value errors types returned by driver
+ * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy
+ * NanStatusType includes these values.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
+ /* Unsigned 32-bit value error values returned by driver
+ * The nan_i.h in AOSP project platform/hardware/qcom/wlan
+ * NanInternalStatusType includes these values.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ /* Unsigned 32-bit value for Channel setup configuration
+ * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy
+ * NanDataPathChannelCfg includes these values.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG,
+ /* Unsigned 32-bit value for Cipher Suite Shared Key Type */
+ QCA_WLAN_VENDOR_ATTR_NDP_CSID,
+ /* Array of u8: len = NAN_PMK_INFO_LEN 32 bytes */
+ QCA_WLAN_VENDOR_ATTR_NDP_PMK,
+ /* Security Context Identifier that contains the PMKID
+ * Array of u8: len = NAN_SCID_BUF_LEN 1024 bytes
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_SCID,
+ /* Array of u8: len = NAN_SECURITY_MAX_PASSPHRASE_LEN 63 bytes */
+ QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE,
+ /* Array of u8: len = NAN_MAX_SERVICE_NAME_LEN 255 bytes */
+ QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME,
+ /* Unsigned 32-bit bitmap indicating schedule update
+ * BIT_0: NSS Update
+ * BIT_1: Channel list update
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON,
+ /* Unsigned 32-bit value for NSS */
+ QCA_WLAN_VENDOR_ATTR_NDP_NSS,
+ /* Unsigned 32-bit value for NUMBER NDP CHANNEL */
+ QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS,
+ /* Unsigned 32-bit value for CHANNEL BANDWIDTH
+ * 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH,
+ /* Array of channel/band width */
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO,
+ /* IPv6 address used by NDP (in network byte order), 16 bytes array.
+ * This attribute is used and optional for ndp request, ndp response,
+ * ndp indication, and ndp confirm.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR = 27,
+ /* Unsigned 16-bit value indicating transport port used by NDP.
+ * This attribute is used and optional for ndp response, ndp indication,
+ * and ndp confirm.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT = 28,
+ /* Unsigned 8-bit value indicating protocol used by NDP and assigned by
+ * the Internet Assigned Numbers Authority (IANA) as per:
+ * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+ * This attribute is used and optional for ndp response, ndp indication,
+ * and ndp confirm.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL = 29,
+ /* Unsigned 8-bit value indicating if NDP remote peer supports NAN NDPE.
+ * 1:support 0:not support
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_NDPE_SUPPORT = 30,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX =
+ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_ndp_sub_cmd {
+ QCA_WLAN_VENDOR_ATTR_NDP_INVALID = 0,
+ /* Command to create a NAN data path interface */
+ QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE = 1,
+ /* Command to delete a NAN data path interface */
+ QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE = 2,
+ /* Command to initiate a NAN data path session */
+ QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST = 3,
+ /* Command to notify if the NAN data path session was sent */
+ QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE = 4,
+ /* Command to respond to NAN data path session */
+ QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST = 5,
+ /* Command to notify on the responder about the response */
+ QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE = 6,
+ /* Command to initiate a NAN data path end */
+ QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST = 7,
+ /* Command to notify the if end request was sent */
+ QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE = 8,
+ /* Command to notify the peer about the end request */
+ QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 9,
+ /* Command to confirm the NAN data path session is complete */
+ QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 10,
+ /* Command to indicate the peer about the end request being received */
+ QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11,
+ /* Command to indicate the peer of schedule update */
+ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12
+};
+
+/**
+ * enum qca_wlan_vendor_attr_nd_offload - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD.
+ */
+enum qca_wlan_vendor_attr_nd_offload {
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_INVALID = 0,
+ /* Flag to set Neighbour Discovery offload */
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG,
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX =
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST - 1,
+};
+
+/**
+ * enum packet_filter_sub_cmd - Packet filter sub commands
+ */
+enum packet_filter_sub_cmd {
+ /**
+ * Write packet filter program and/or data. The driver/firmware should
+ * disable APF before writing into local buffer and re-enable APF after
+ * writing is done.
+ */
+ QCA_WLAN_SET_PACKET_FILTER = 1,
+ /* Get packet filter feature capabilities from driver */
+ QCA_WLAN_GET_PACKET_FILTER = 2,
+ /**
+ * Write packet filter program and/or data. User space will send the
+ * %QCA_WLAN_DISABLE_PACKET_FILTER command before issuing this command
+ * and will send the %QCA_WLAN_ENABLE_PACKET_FILTER afterwards. The key
+ * difference from that %QCA_WLAN_SET_PACKET_FILTER is the control over
+ * enable/disable is given to user space with this command. Also,
+ * user space sends the length of program portion in the buffer within
+ * %QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH.
+ */
+ QCA_WLAN_WRITE_PACKET_FILTER = 3,
+ /* Read packet filter program and/or data */
+ QCA_WLAN_READ_PACKET_FILTER = 4,
+ /* Enable APF feature */
+ QCA_WLAN_ENABLE_PACKET_FILTER = 5,
+ /* Disable APF feature */
+ QCA_WLAN_DISABLE_PACKET_FILTER = 6,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_packet_filter - BPF control commands used by
+ * vendor QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER.
+ */
+enum qca_wlan_vendor_attr_packet_filter {
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID = 0,
+ /* Unsigned 32-bit enum passed using packet_filter_sub_cmd */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD,
+ /* Unsigned 32-bit value indicating the packet filter version */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION,
+ /* Unsigned 32-bit value indicating the packet filter id */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID,
+ /**
+ * Unsigned 32-bit value indicating the packet filter size including
+ * program + data.
+ */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE,
+ /* Unsigned 32-bit value indicating the packet filter current offset */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET,
+ /* Program and/or data in bytes */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM,
+ /* Unsigned 32-bit value of the length of the program section in packet
+ * filter buffer.
+ */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX =
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_drv_info - WLAN driver info used by vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE.
+ */
+enum qca_wlan_vendor_drv_info {
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_INVALID = 0,
+ /* Maximum Message size info between firmware & HOST
+ * Unsigned 32-bit value
+ */
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_wake_stats - Wake lock stats used by vendor
+ * command QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS.
+ */
+enum qca_wlan_vendor_attr_wake_stats {
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_INVALID = 0,
+ /* Unsigned 32-bit value indicating the total count of wake event */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE,
+ /* Array of individual wake count, each index representing wake reason
+ */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR,
+ /* Unsigned 32-bit value representing wake count array */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ,
+ /* Unsigned 32-bit total wake count value of driver/fw */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE,
+ /* Array of wake stats of driver/fw */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR,
+ /* Unsigned 32-bit total wake count value of driver/fw */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ,
+ /* Unsigned 32-bit total wake count value of packets received */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE,
+ /* Unsigned 32-bit wake count value unicast packets received */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT,
+ /* Unsigned 32-bit wake count value multicast packets received */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT,
+ /* Unsigned 32-bit wake count value broadcast packets received */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT,
+ /* Unsigned 32-bit wake count value of ICMP packets */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT,
+ /* Unsigned 32-bit wake count value of ICMP6 packets */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT,
+ /* Unsigned 32-bit value ICMP6 router advertisement */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA,
+ /* Unsigned 32-bit value ICMP6 neighbor advertisement */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA,
+ /* Unsigned 32-bit value ICMP6 neighbor solicitation */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS,
+ /* Unsigned 32-bit wake count value of receive side ICMP4 multicast */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT,
+ /* Unsigned 32-bit wake count value of receive side ICMP6 multicast */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT,
+ /* Unsigned 32-bit wake count value of receive side multicast */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT,
+ /* Unsigned 32-bit wake count value of a given RSSI breach */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RSSI_BREACH_CNT,
+ /* Unsigned 32-bit wake count value of low RSSI */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_LOW_RSSI_CNT,
+ /* Unsigned 32-bit value GSCAN count */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_GSCAN_CNT,
+ /* Unsigned 32-bit value PNO complete count */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_COMPLETE_CNT,
+ /* Unsigned 32-bit value PNO match count */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_MATCH_CNT,
+ /* keep last */
+ QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX =
+ QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_thermal_cmd - Vendor subcmd attributes to set
+ * cmd value. Used for NL attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
+ */
+enum qca_wlan_vendor_attr_thermal_cmd {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_INVALID = 0,
+ /* The value of command, driver will implement different operations
+ * according to this value. It uses values defined in
+ * enum qca_wlan_vendor_attr_thermal_cmd_type.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX =
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST - 1
+};
+
+/**
+ * qca_wlan_vendor_attr_thermal_cmd_type: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD. This represents the
+ * thermal command types sent to driver.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS: Request to
+ * get thermal shutdown configuration parameters for display. Parameters
+ * responded from driver are defined in
+ * enum qca_wlan_vendor_attr_get_thermal_params_rsp.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE: Request to
+ * get temperature. Host should respond with a temperature data. It is defined
+ * in enum qca_wlan_vendor_attr_thermal_get_temperature.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND: Request to execute thermal
+ * suspend action.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME: Request to execute thermal
+ * resume action.
+ */
+enum qca_wlan_vendor_attr_thermal_cmd_type {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_thermal_get_temperature - vendor subcmd attributes
+ * to get chip temperature by user.
+ * enum values are used for NL attributes for data used by
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE command for data used
+ * by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
+ */
+enum qca_wlan_vendor_attr_thermal_get_temperature {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_INVALID = 0,
+ /* Temperature value (degree Celsius) from driver.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_DATA,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_MAX =
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_thermal_params_rsp - vendor subcmd attributes
+ * to get configuration parameters of thermal shutdown feature. Enum values are
+ * used by QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS command for data
+ * used by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
+ */
+enum qca_wlan_vendor_attr_get_thermal_params_rsp {
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_INVALID = 0,
+ /* Indicate if the thermal shutdown feature is enabled.
+ * NLA_FLAG attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_EN,
+ /* Indicate if the auto mode is enabled.
+ * Enable: Driver triggers the suspend/resume action.
+ * Disable: User space triggers the suspend/resume action.
+ * NLA_FLAG attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_AUTO_EN,
+ /* Thermal resume threshold (degree Celsius). Issue the resume command
+ * if the temperature value is lower than this threshold.
+ * u16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_RESUME_THRESH,
+ /* Thermal warning threshold (degree Celsius). FW reports temperature
+ * to driver if it's higher than this threshold.
+ * u16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_WARNING_THRESH,
+ /* Thermal suspend threshold (degree Celsius). Issue the suspend command
+ * if the temperature value is higher than this threshold.
+ * u16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SUSPEND_THRESH,
+ /* FW reports temperature data periodically at this interval (ms).
+ * u16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SAMPLE_RATE,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_MAX =
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_thermal_event - vendor subcmd attributes to
+ * report thermal events from driver to user space.
+ * enum values are used for NL attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT sub command.
+ */
+enum qca_wlan_vendor_attr_thermal_event {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_INVALID = 0,
+ /* Temperature value (degree Celsius) from driver.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_TEMPERATURE,
+ /* Indication of resume completion from power save mode.
+ * NLA_FLAG attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_MAX =
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * enum he_fragmentation_val - HE fragmentation support values
+ * Indicates level of dynamic fragmentation that is supported by
+ * a STA as a recipient.
+ * HE fragmentation values are defined in IEEE P802.11ax/D2.0, 9.4.2.237.2
+ * (HE MAC Capabilities Information field) and are used in HE Capabilities
+ * element to advertise the support. These values are validated in the driver
+ * to check the device capability and advertised in the HE Capabilities
+ * element. These values are used to configure testbed device to allow the
+ * advertised hardware capabilities to be downgraded for testing purposes.
+ *
+ * @HE_FRAG_DISABLE: no support for dynamic fragmentation
+ * @HE_FRAG_LEVEL1: support for dynamic fragments that are
+ * contained within an MPDU or S-MPDU, no support for dynamic fragments
+ * within an A-MPDU that is not an S-MPDU.
+ * @HE_FRAG_LEVEL2: support for dynamic fragments that are
+ * contained within an MPDU or S-MPDU and support for up to one dynamic
+ * fragment for each MSDU, each A-MSDU if supported by the recipient, and
+ * each MMPDU within an A-MPDU or multi-TID A-MPDU that is not an
+ * MPDU or S-MPDU.
+ * @HE_FRAG_LEVEL3: support for dynamic fragments that are
+ * contained within an MPDU or S-MPDU and support for multiple dynamic
+ * fragments for each MSDU and for each A-MSDU if supported by the
+ * recipient within an A-MPDU or multi-TID AMPDU and up to one dynamic
+ * fragment for each MMPDU in a multi-TID A-MPDU that is not an S-MPDU.
+ */
+enum he_fragmentation_val {
+ HE_FRAG_DISABLE,
+ HE_FRAG_LEVEL1,
+ HE_FRAG_LEVEL2,
+ HE_FRAG_LEVEL3,
+};
+
+/**
+ * enum he_mcs_config - HE MCS support configuration
+ *
+ * Configures the HE Tx/Rx MCS map in HE capability IE for given bandwidth.
+ * These values are used in driver to configure the HE MCS map to advertise
+ * Tx/Rx MCS map in HE capability and these values are applied for all the
+ * streams supported by the device. To configure MCS for different bandwidths,
+ * vendor command needs to be sent using this attribute with appropriate value.
+ * For example, to configure HE_80_MCS_0_7, send vendor command using HE MCS
+ * attribute with HE_80_MCS0_7. And to configure HE MCS for HE_160_MCS0_11
+ * send this command using HE MCS config attribute with value HE_160_MCS0_11.
+ * These values are used to configure testbed device to allow the advertised
+ * hardware capabilities to be downgraded for testing purposes. The enum values
+ * are defined such that BIT[1:0] indicates the MCS map value. Values 3,7 and
+ * 11 are not used as BIT[1:0] value is 3 which is used to disable MCS map.
+ * These values are validated in the driver before setting the MCS map and
+ * driver returns error if the input is other than these enum values.
+ *
+ * @HE_80_MCS0_7: support for HE 80/40/20 MHz MCS 0 to 7
+ * @HE_80_MCS0_9: support for HE 80/40/20 MHz MCS 0 to 9
+ * @HE_80_MCS0_11: support for HE 80/40/20 MHz MCS 0 to 11
+ * @HE_160_MCS0_7: support for HE 160 MHz MCS 0 to 7
+ * @HE_160_MCS0_9: support for HE 160 MHz MCS 0 to 9
+ * @HE_160_MCS0_11: support for HE 160 MHz MCS 0 to 11
+ * @HE_80P80_MCS0_7: support for HE 80p80 MHz MCS 0 to 7
+ * @HE_80P80_MCS0_9: support for HE 80p80 MHz MCS 0 to 9
+ * @HE_80P80_MCS0_11: support for HE 80p80 MHz MCS 0 to 11
+ */
+enum he_mcs_config {
+ HE_80_MCS0_7 = 0,
+ HE_80_MCS0_9 = 1,
+ HE_80_MCS0_11 = 2,
+ HE_160_MCS0_7 = 4,
+ HE_160_MCS0_9 = 5,
+ HE_160_MCS0_11 = 6,
+ HE_80P80_MCS0_7 = 8,
+ HE_80P80_MCS0_9 = 9,
+ HE_80P80_MCS0_11 = 10,
+};
+
+/**
+ * enum qca_wlan_ba_session_config - BA session configuration
+ *
+ * Indicates the configuration values for BA session configuration attribute.
+ *
+ * @QCA_WLAN_ADD_BA: Establish a new BA session with given configuration.
+ * @QCA_WLAN_DELETE_BA: Delete the existing BA session for given TID.
+ */
+enum qca_wlan_ba_session_config {
+ QCA_WLAN_ADD_BA = 1,
+ QCA_WLAN_DELETE_BA = 2,
+};
+
+/**
+ * enum qca_wlan_ac_type - Access category type
+ *
+ * Indicates the access category type value.
+ *
+ * @QCA_WLAN_AC_BE: BE access category
+ * @QCA_WLAN_AC_BK: BK access category
+ * @QCA_WLAN_AC_VI: VI access category
+ * @QCA_WLAN_AC_VO: VO access category
+ * @QCA_WLAN_AC_ALL: All ACs
+ */
+enum qca_wlan_ac_type {
+ QCA_WLAN_AC_BE = 0,
+ QCA_WLAN_AC_BK = 1,
+ QCA_WLAN_AC_VI = 2,
+ QCA_WLAN_AC_VO = 3,
+ QCA_WLAN_AC_ALL = 4,
+};
+
+/**
+ * enum qca_wlan_he_ltf_cfg - HE LTF configuration
+ *
+ * Indicates the HE LTF configuration value.
+ *
+ * @QCA_WLAN_HE_LTF_AUTO: HE-LTF is automatically set to the mandatory HE-LTF,
+ * based on the GI setting
+ * @QCA_WLAN_HE_LTF_1X: 1X HE LTF is 3.2us LTF
+ * @QCA_WLAN_HE_LTF_2X: 2X HE LTF is 6.4us LTF
+ * @QCA_WLAN_HE_LTF_4X: 4X HE LTF is 12.8us LTF
+ */
+enum qca_wlan_he_ltf_cfg {
+ QCA_WLAN_HE_LTF_AUTO = 0,
+ QCA_WLAN_HE_LTF_1X = 1,
+ QCA_WLAN_HE_LTF_2X = 2,
+ QCA_WLAN_HE_LTF_4X = 3,
+};
+
+/**
+ * enum qca_wlan_he_mac_padding_dur - HE trigger frame MAC padding duration
+ *
+ * Indicates the HE trigger frame MAC padding duration value.
+ *
+ * @QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME: no additional time required to
+ * process the trigger frame.
+ * @QCA_WLAN_HE_8US_OF_PROCESS_TIME: indicates the 8us of processing time for
+ * trigger frame.
+ * @QCA_WLAN_HE_16US_OF_PROCESS_TIME: indicates the 16us of processing time for
+ * trigger frame.
+ */
+enum qca_wlan_he_mac_padding_dur {
+ QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME = 0,
+ QCA_WLAN_HE_8US_OF_PROCESS_TIME = 1,
+ QCA_WLAN_HE_16US_OF_PROCESS_TIME = 2,
+};
+
+/**
+ * enum qca_wlan_he_om_ctrl_ch_bw - HE OM control field BW configuration
+ *
+ * Indicates the HE Operating mode control channel width setting value.
+ *
+ * @QCA_WLAN_HE_OM_CTRL_BW_20M: Primary 20 MHz
+ * @QCA_WLAN_HE_OM_CTRL_BW_40M: Primary 40 MHz
+ * @QCA_WLAN_HE_OM_CTRL_BW_80M: Primary 80 MHz
+ * @QCA_WLAN_HE_OM_CTRL_BW_160M: 160 MHz and 80+80 MHz
+ */
+enum qca_wlan_he_om_ctrl_ch_bw {
+ QCA_WLAN_HE_OM_CTRL_BW_20M = 0,
+ QCA_WLAN_HE_OM_CTRL_BW_40M = 1,
+ QCA_WLAN_HE_OM_CTRL_BW_80M = 2,
+ QCA_WLAN_HE_OM_CTRL_BW_160M = 3,
+};
+
+/* Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
+ */
+enum qca_wlan_vendor_attr_wifi_test_config {
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_INVALID = 0,
+ /* 8-bit unsigned value to configure the driver to enable/disable
+ * WMM feature. This attribute is used to configure testbed device.
+ * 1-enable, 0-disable
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE = 1,
+
+ /* 8-bit unsigned value to configure the driver to accept/reject
+ * the addba request from peer. This attribute is used to configure
+ * the testbed device.
+ * 1-accept addba, 0-reject addba
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ = 2,
+
+ /* 8-bit unsigned value to configure the driver to send or not to
+ * send the addba request to peer.
+ * This attribute is used to configure the testbed device.
+ * 1-send addba, 0-do not send addba
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ = 3,
+
+ /* 8-bit unsigned value to indicate the HE fragmentation support.
+ * Uses enum he_fragmentation_val values.
+ * This attribute is used to configure the testbed device to
+ * allow the advertised hardware capabilities to be downgraded
+ * for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION = 4,
+
+ /* 8-bit unsigned value to indicate the HE MCS support.
+ * Uses enum he_mcs_config values.
+ * This attribute is used to configure the testbed device to
+ * allow the advertised hardware capabilities to be downgraded
+ * for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS = 5,
+
+ /* 8-bit unsigned value to configure the driver to allow or not to
+ * allow the connection with WEP/TKIP in HT/VHT/HE modes.
+ * This attribute is used to configure the testbed device.
+ * 1-allow WEP/TKIP in HT/VHT/HE, 0-do not allow WEP/TKIP.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE = 6,
+
+ /* 8-bit unsigned value to configure the driver to add a
+ * new BA session or delete the existing BA session for
+ * given TID. ADDBA command uses the buffer size and TID
+ * configuration if user specifies the values else default
+ * value for buffer size is used for all TIDs if the TID
+ * also not specified. For DEL_BA command TID value is
+ * required to process the command.
+ * Uses enum qca_wlan_ba_session_config values.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION = 7,
+
+ /* 16-bit unsigned value to configure the buffer size in addba
+ * request and response frames.
+ * This attribute is used to configure the testbed device.
+ * The range of the value is 0 to 256.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE = 8,
+
+ /* 8-bit unsigned value to configure the buffer size in addba
+ * request and response frames.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID = 9,
+
+ /* 8-bit unsigned value to configure the no ack policy.
+ * To configure no ack policy, access category value is
+ * required to process the command.
+ * This attribute is used to configure the testbed device.
+ * 1 - enable no ack, 0 - disable no ack.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK = 10,
+
+ /* 8-bit unsigned value to configure the AC for no ack policy
+ * This attribute is used to configure the testbed device.
+ * Uses the enum qca_wlan_ac_type values.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC = 11,
+
+ /* 8-bit unsigned value to configure the HE LTF
+ * This attribute is used to configure the testbed device.
+ * Uses the enum qca_wlan_he_ltf_cfg values.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF = 12,
+
+ /* 8-bit unsigned value to configure the tx beamformee.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE = 13,
+
+ /* 8-bit unsigned value to configure the tx beamformee number
+ * of space-time streams.
+ * This attribute is used to configure the testbed device.
+ * The range of the value is 0 to 8.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS = 14,
+
+ /* 8-bit unsigned value to configure the MU EDCA params for given AC
+ * This attribute is used to configure the testbed device.
+ * Uses the enum qca_wlan_ac_type values.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC = 15,
+
+ /* 8-bit unsigned value to configure the MU EDCA AIFSN for given AC
+ * To configure MU EDCA AIFSN value, MU EDCA access category value
+ * is required to process the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AIFSN = 16,
+
+ /* 8-bit unsigned value to configure the MU EDCA ECW min value for
+ * given AC.
+ * To configure MU EDCA ECW min value, MU EDCA access category value
+ * is required to process the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMIN = 17,
+
+ /* 8-bit unsigned value to configure the MU EDCA ECW max value for
+ * given AC.
+ * To configure MU EDCA ECW max value, MU EDCA access category value
+ * is required to process the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMAX = 18,
+
+ /* 8-bit unsigned value to configure the MU EDCA timer for given AC
+ * To configure MU EDCA timer value, MU EDCA access category value
+ * is required to process the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_TIMER = 19,
+
+ /* 8-bit unsigned value to configure the HE trigger frame MAC padding
+ * duration.
+ * This attribute is used to configure the testbed device.
+ * Uses the enum qca_wlan_he_mac_padding_dur values.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR = 20,
+
+ /* 8-bit unsigned value to override the MU EDCA params to defaults
+ * regardless of the AP beacon MU EDCA params. If it is enabled use
+ * the default values else use the MU EDCA params from AP beacon.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA = 21,
+
+ /* 8-bit unsigned value to configure the support for receiving
+ * an MPDU that contains an operating mode control subfield.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP = 22,
+
+ /* Nested attribute values required to setup the TWT session.
+ * enum qca_wlan_vendor_attr_twt_setup provides the necessary
+ * information to set up the session. It contains broadcast flags,
+ * set_up flags, trigger value, flow type, flow ID, wake interval
+ * exponent, protection, target wake time, wake duration, wake interval
+ * mantissa. These nested attributes are used to setup a host triggered
+ * TWT session.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP = 23,
+
+ /* This nested attribute is used to terminate the current TWT session.
+ * It does not currently carry any attributes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE = 24,
+
+ /* This nested attribute is used to suspend the current TWT session.
+ * It does not currently carry any attributes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SUSPEND = 25,
+
+ /* Nested attribute values to indicate the request for resume.
+ * This attribute is used to resume the TWT session.
+ * enum qca_wlan_vendor_attr_twt_resume provides the necessary
+ * parameters required to resume the TWT session.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME = 26,
+
+ /* 8-bit unsigned value to set the HE operating mode control
+ * (OM CTRL) Channel Width subfield.
+ * The Channel Width subfield indicates the operating channel width
+ * supported by the STA for both reception and transmission.
+ * Uses the enum qca_wlan_he_om_ctrl_ch_bw values.
+ * This setting is cleared with the
+ * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
+ * flag attribute to reset defaults.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW = 27,
+
+ /* 8-bit unsigned value to configure the number of spatial
+ * streams in HE operating mode control field.
+ * This setting is cleared with the
+ * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
+ * flag attribute to reset defaults.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS = 28,
+
+ /* Flag attribute to configure the UL MU disable bit in
+ * HE operating mode control field.
+ * This setting is cleared with the
+ * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
+ * flag attribute to reset defaults.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_UL_MU_DISABLE = 29,
+
+ /* Flag attribute to clear the previously set HE operating mode
+ * control field configuration.
+ * This attribute is used to configure the testbed device to reset
+ * defaults to clear any previously set HE operating mode control
+ * field configuration.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG = 30,
+
+ /* 8-bit unsigned value to configure HE single user PPDU
+ * transmission. By default this setting is disabled and it
+ * is disabled in the reset defaults of the device configuration.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU = 31,
+
+ /* 8-bit unsigned value to configure action frame transmission
+ * in HE trigger based PPDU transmission.
+ * By default this setting is disabled and it is disabled in
+ * the reset defaults of the device configuration.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
+ * The user can add/delete the filter by specifying the BSSID/STA MAC address in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, filter type in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, add/delete action in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user can get the
+ * statistics of an unassociated station by specifying the MAC address in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, station type in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, GET action in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user also can get
+ * the statistics of all unassociated stations by specifying the Broadcast MAC
+ * address (ff:ff:ff:ff:ff:ff) in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR with
+ * above procedure. In the response, driver shall specify statistics
+ * information nested in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS.
+ */
+enum qca_wlan_vendor_attr_bss_filter {
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR = 1,
+ /* Other BSS filter type, unsigned 8 bit value. One of the values
+ * in enum qca_wlan_vendor_bss_filter_type.
+ */
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE = 2,
+ /* Other BSS filter action, unsigned 8 bit value. One of the values
+ * in enum qca_wlan_vendor_bss_filter_action.
+ */
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION = 3,
+ /* Array of nested attributes where each entry is the statistics
+ * information of the specified station that belong to another BSS.
+ * Attributes for each entry are taken from enum
+ * qca_wlan_vendor_bss_filter_sta_stats.
+ * Other BSS station configured in
+ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER with filter type
+ * QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA.
+ * Statistics returned by QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER
+ * with filter action QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET.
+ */
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAX =
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_bss_filter_type - Type of
+ * filter used in other BSS filter operations. Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
+ *
+ * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID: BSSID filter
+ * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA: Station MAC address filter
+ */
+enum qca_wlan_vendor_bss_filter_type {
+ QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID,
+ QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA,
+};
+
+/**
+ * enum qca_wlan_vendor_bss_filter_action - Type of
+ * action in other BSS filter operations. Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
+ *
+ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD: Add filter
+ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL: Delete filter
+ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET: Get the statistics
+ */
+enum qca_wlan_vendor_bss_filter_action {
+ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD,
+ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL,
+ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET,
+};
+
+/**
+ * enum qca_wlan_vendor_bss_filter_sta_stats - Attributes for
+ * the statistics of a specific unassociated station belonging to another BSS.
+ * The statistics provides information of the unassociated station
+ * filtered by other BSS operation - such as MAC, signal value.
+ * Used by the vendor command QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
+ *
+ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC: MAC address of the station.
+ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI: Last received signal strength
+ * of the station. Unsigned 8 bit number containing RSSI.
+ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS: Time stamp of the host
+ * driver for the last received RSSI. Unsigned 64 bit number containing
+ * nanoseconds from the boottime.
+ */
+enum qca_wlan_vendor_bss_filter_sta_stats {
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC = 1,
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI = 2,
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAX =
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST - 1
+};
+
+/* enum qca_wlan_nan_subcmd_type - Type of NAN command used by attribute
+ * QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE as a part of vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT.
+ */
+enum qca_wlan_nan_ext_subcmd_type {
+ /* Subcmd of type NAN Enable Request */
+ QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ = 1,
+ /* Subcmd of type NAN Disable Request */
+ QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_nan_params - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT.
+ */
+enum qca_wlan_vendor_attr_nan_params {
+ QCA_WLAN_VENDOR_ATTR_NAN_INVALID = 0,
+ /* Carries NAN command for firmware component. Every vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT must contain this attribute with a
+ * payload containing the NAN command. NLA_BINARY attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA = 1,
+ /* Indicates the type of NAN command sent with
+ * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. enum qca_wlan_nan_ext_subcmd_type
+ * describes the possible range of values. This attribute is mandatory
+ * if the command being issued is either
+ * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ or
+ * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ. NLA_U32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE = 2,
+ /* Frequency (in MHz) of primary NAN discovery social channel in 2.4 GHz
+ * band. This attribute is mandatory when command type is
+ * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ. NLA_U32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ = 3,
+ /* Frequency (in MHz) of secondary NAN discovery social channel in 5 GHz
+ * band. This attribute is optional and should be included when command
+ * type is QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ and NAN discovery
+ * has to be started on 5GHz along with 2.4GHz. NLA_U32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX =
+ QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
+ * TWT (Target Wake Time) setup request. These attributes are sent as part of
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute.
+ * Disable (flag attribute not present) - Individual TWT
+ * Enable (flag attribute present) - Broadcast TWT.
+ * Individual means the session is between the STA and the AP.
+ * This session is established using a separate negotiation between
+ * STA and AP.
+ * Broadcast means the session is across multiple STAs and an AP. The
+ * configuration parameters are announced in Beacon frames by the AP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8).
+ * Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to
+ * specify the TWT request type
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute
+ * Enable (flag attribute present) - TWT with trigger support.
+ * Disable (flag attribute not present) - TWT without trigger support.
+ * Trigger means the AP will send the trigger frame to allow STA to send data.
+ * Without trigger, the STA will wait for the MU EDCA timer before
+ * transmitting the data.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8)
+ * 0 - Announced TWT - In this mode, STA may skip few service periods to
+ * save more power. If STA wants to wake up, it will send a PS-POLL/QoS
+ * NULL frame to AP.
+ * 1 - Unannounced TWT - The STA will wakeup during every SP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8)
+ * Flow ID is the unique identifier for each TWT session.
+ * Currently this is not required and dialog ID will be set to zero.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8)
+ * This attribute (exp) is used along with the mantissa to derive the
+ * wake interval using the following formula:
+ * pow(2,exp) = wake_intvl_us/wake_intvl_mantis
+ * Wake interval is the interval between 2 successive SP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute
+ * Enable (flag attribute present) - Protection required.
+ * Disable (flag attribute not present) - Protection not required.
+ * If protection is enabled, then the AP will use protection
+ * mechanism using RTS/CTS to self to reserve the airtime.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32)
+ * This attribute is used as the SP offset which is the offset from
+ * TSF after which the wake happens. The units are in microseconds. If
+ * this attribute is not provided, then the value will be set to zero.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32)
+ * This is the duration of the service period. The units are in TU.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32)
+ * This attribute is used to configure wake interval mantissa.
+ * The units are in TU.
+ */
+enum qca_wlan_vendor_attr_twt_setup {
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST = 1,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER = 3,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE = 4,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID = 5,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP = 6,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION = 7,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME = 8,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX =
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_twt_resume: Represents attributes for
+ * TWT (Target Wake Time) resume request. These attributes are sent as part of
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8)
+ * This attribute is used as the SP offset which is the offset from
+ * TSF after which the wake happens. The units are in microseconds.
+ * If this attribute is not provided, then the value will be set to
+ * zero.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32)
+ * This attribute represents the next TWT subfield size.
+ */
+enum qca_wlan_vendor_attr_twt_resume {
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAX =
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_twt_setup_req_type - Required (u8)
+ * Represents the setup type being requested for TWT.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT
+ * parameters but relying on AP to fill the parameters during the negotiation.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST: STA will provide all the suggested
+ * values which the AP may accept or AP may provide alternative parameters
+ * which the STA may accept.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any
+ * alternate parameters than the requested ones.
+ */
+enum qca_wlan_vendor_twt_setup_req_type {
+ QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1,
+ QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2,
+ QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3,
+};
+
+/**
+ * enum qca_wlan_roam_scan_event_type - Type of roam scan event
+ *
+ * Indicates the type of roam scan event sent by firmware/driver.
+ *
+ * @QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT: Roam scan trigger event type.
+ * @QCA_WLAN_ROAM_SCAN_STOP_EVENT: Roam scan stopped event type.
+ */
+enum qca_wlan_roam_scan_event_type {
+ QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT = 0,
+ QCA_WLAN_ROAM_SCAN_STOP_EVENT = 1,
+};
+
+/**
+ * enum qca_wlan_roam_scan_trigger_reason - Roam scan trigger reason
+ *
+ * Indicates the reason for triggering roam scan by firmware/driver.
+ *
+ * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI: Due to low RSSI of current AP.
+ * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER: Due to high packet error rate.
+ */
+enum qca_wlan_roam_scan_trigger_reason {
+ QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI = 0,
+ QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_scan - Vendor subcmd attributes to report
+ * roam scan related details from driver/firmware to user space. enum values
+ * are used for NL attributes sent with
+ * %QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT sub command.
+ */
+enum qca_wlan_vendor_attr_roam_scan {
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_INVALID = 0,
+ /* Encapsulates type of roam scan event being reported. enum
+ * qca_wlan_roam_scan_event_type describes the possible range of
+ * values. u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_EVENT_TYPE = 1,
+ /* Encapsulates reason for triggering roam scan. enum
+ * qca_wlan_roam_scan_trigger_reason describes the possible range of
+ * values. u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_TRIGGER_REASON = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_cfr_method - QCA vendor CFR methods used by
+ * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD as part of vendor
+ * command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG.
+ */
+enum qca_wlan_vendor_cfr_method {
+ /* CFR method using QOS Null frame */
+ QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL = 0,
+};
+
+/**
+ * enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer
+ * Channel Frequency Response capture parameters and enable periodic CFR
+ * capture.
+ */
+enum qca_wlan_vendor_peer_cfr_capture_attr {
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0,
+ /* 6-byte MAC address of the peer.
+ * This attribute is mandatory.
+ */
+ QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR = 1,
+ /* Enable peer CFR Capture, flag attribute.
+ * This attribute is mandatory to enable peer CFR capture.
+ * If this attribute is not present, peer CFR capture is disabled.
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE = 2,
+ /* BW of measurement, attribute uses the values in enum nl80211_chan_width
+ * Supported values: 20, 40, 80, 80+80, 160.
+ * Note that all targets may not support all bandwidths.
+ * u8 attribute. This attribute is mandatory if attribute
+ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH = 3,
+ /* Periodicity of CFR measurement in msec.
+ * Periodicity should be a multiple of Base timer.
+ * Current Base timer value supported is 10 msecs (default).
+ * 0 for one shot capture. u32 attribute.
+ * This attribute is mandatory if attribute
+ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY = 4,
+ /* Method used to capture Channel Frequency Response.
+ * Attribute uses the values defined in enum qca_wlan_vendor_cfr_method.
+ * u8 attribute. This attribute is mandatory if attribute
+ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD = 5,
+ /* Enable periodic CFR capture, flag attribute.
+ * This attribute is mandatory to enable Periodic CFR capture.
+ * If this attribute is not present, periodic CFR capture is disabled.
+ */
+ QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE = 6,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX =
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_throughput_level - Current throughput level
+ *
+ * Indicates the current level of throughput calculated by the driver. The
+ * driver may choose different thresholds to decide whether the throughput level
+ * is low or medium or high based on variety of parameters like physical link
+ * capacity of the current connection, the number of packets being dispatched
+ * per second, etc. The throughput level events might not be consistent with the
+ * actual current throughput value being observed.
+ *
+ * @QCA_WLAN_THROUGHPUT_LEVEL_LOW: Low level of throughput
+ * @QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM: Medium level of throughput
+ * @QCA_WLAN_THROUGHPUT_LEVEL_HIGH: High level of throughput
+ */
+enum qca_wlan_throughput_level {
+ QCA_WLAN_THROUGHPUT_LEVEL_LOW = 0,
+ QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM = 1,
+ QCA_WLAN_THROUGHPUT_LEVEL_HIGH = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_throughput_change - Vendor subcmd attributes to
+ * report throughput changes from the driver to user space. enum values are used
+ * for netlink attributes sent with
+ * %QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT sub command.
+ */
+enum qca_wlan_vendor_attr_throughput_change {
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_INVALID = 0,
+ /* Indicates the direction of throughput in which the change is being
+ * reported. u8 attribute. Value is 0 for TX and 1 for RX.
+ */
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION = 1,
+ /* Indicates the newly observed throughput level. enum
+ * qca_wlan_throughput_level describes the possible range of values.
+ * u8 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL = 2,
+ /* Indicates the driver's guidance on the new value to be set to
+ * kernel's TCP parameter tcp_limit_output_bytes. u32 attribute. The
+ * driver may optionally include this attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES = 3,
+ /* Indicates the driver's guidance on the new value to be set to
+ * kernel's TCP parameter tcp_adv_win_scale. s8 attribute. Possible
+ * values are from -31 to 31. The driver may optionally include this
+ * attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE = 4,
+ /* Indicates the driver's guidance on the new value to be set to
+ * kernel's TCP parameter tcp_delack_seg. u32 attribute. The driver may
+ * optionally include this attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_MAX =
+ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_coex_config_profiles - This enum defines different types of
+ * traffic streams that can be prioritized one over the other during coex
+ * scenarios.
+ * The types defined in this enum are categorized in the below manner.
+ * 0 - 31 values corresponds to WLAN
+ * 32 - 63 values corresponds to BT
+ * 64 - 95 values corresponds to Zigbee
+ * @QCA_WIFI_STA_DISCOVERY: Prioritize discovery frames for WLAN STA
+ * @QCA_WIFI_STA_CONNECTION: Prioritize connection frames for WLAN STA
+ * @QCA_WIFI_STA_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN STA
+ * @QCA_WIFI_STA_DATA : Prioritize data frames for WLAN STA
+ * @QCA_WIFI_STA_ALL: Priritize all frames for WLAN STA
+ * @QCA_WIFI_SAP_DISCOVERY: Prioritize discovery frames for WLAN SAP
+ * @QCA_WIFI_SAP_CONNECTION: Prioritize connection frames for WLAN SAP
+ * @QCA_WIFI_SAP_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN SAP
+ * @QCA_WIFI_SAP_DATA: Prioritize data frames for WLAN SAP
+ * @QCA_WIFI_SAP_ALL: Prioritize all frames for WLAN SAP
+ * @QCA_BT_A2DP: Prioritize BT A2DP
+ * @QCA_BT_BLE: Prioritize BT BLE
+ * @QCA_BT_SCO: Prioritize BT SCO
+ * @QCA_ZB_LOW: Prioritize Zigbee Low
+ * @QCA_ZB_HIGH: Prioritize Zigbee High
+ */
+enum qca_coex_config_profiles {
+ /* 0 - 31 corresponds to WLAN */
+ QCA_WIFI_STA_DISCOVERY = 0,
+ QCA_WIFI_STA_CONNECTION = 1,
+ QCA_WIFI_STA_CLASS_3_MGMT = 2,
+ QCA_WIFI_STA_DATA = 3,
+ QCA_WIFI_STA_ALL = 4,
+ QCA_WIFI_SAP_DISCOVERY = 5,
+ QCA_WIFI_SAP_CONNECTION = 6,
+ QCA_WIFI_SAP_CLASS_3_MGMT = 7,
+ QCA_WIFI_SAP_DATA = 8,
+ QCA_WIFI_SAP_ALL = 9,
+ /* 32 - 63 corresponds to BT */
+ QCA_BT_A2DP = 32,
+ QCA_BT_BLE = 33,
+ QCA_BT_SCO = 34,
+ /* 64 - 95 corresponds to Zigbee */
+ QCA_ZB_LOW = 64,
+ QCA_ZB_HIGH = 65
+};
+
+/**
+ * enum qca_vendor_attr_coex_config - Specifies vendor coex config attributes
+ *
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES: This attribute contains variable
+ * length array of 8-bit values from enum qca_coex_config_profiles.
+ * FW will prioritize the profiles in the order given in the array encapsulated
+ * in this attribute.
+ * For example:
+ * -----------------------------------------------------------------------
+ * | 1 | 34 | 32 | 65 |
+ * -----------------------------------------------------------------------
+ * If the attribute contains the values defined in above array then it means
+ * 1) Wifi STA connection has priority over BT_SCO, BT_A2DP and ZIGBEE HIGH.
+ * 2) BT_SCO has priority over BT_A2DP.
+ * 3) BT_A2DP has priority over ZIGBEE HIGH.
+ * Profiles which are not listed in this array shall not be preferred over the
+ * profiles which are listed in the array as a part of this attribute.
+ */
+enum qca_vendor_attr_coex_config {
+ QCA_VENDOR_ATTR_COEX_CONFIG_INVALID = 0,
+ QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES = 1,
+
+ /* Keep last */
+ QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST,
+ QCA_VENDOR_ATTR_COEX_CONFIG_MAX =
+ QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_link_properties - Represent the link properties.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer
+ * (STA/AP) for the connected link.
+ * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS: Attribute containing a
+ * &struct nl80211_sta_flag_update for the respective connected link. MAC
+ * address of the peer represented by
+ * QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR.
+ */
+enum qca_wlan_vendor_attr_link_properties {
+ QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_INVALID = 0,
+ /* 1 - 3 are reserved */
+ QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR = 4,
+ QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS = 5,
+
+ /* Keep last */
+ QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST,
+ QCA_VENDOR_ATTR_LINK_PROPERTIES_MAX =
+ QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 9f70f036ba76..981e788dc751 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -29,6 +29,8 @@ int sae_set_group(struct sae_data *sae, int group)
/* First, check if this is an ECC group */
tmp->ec = crypto_ec_init(group);
if (tmp->ec) {
+ wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d",
+ group);
sae->group = group;
tmp->prime_len = crypto_ec_prime_len(tmp->ec);
tmp->prime = crypto_ec_get_prime(tmp->ec);
@@ -39,6 +41,8 @@ int sae_set_group(struct sae_data *sae, int group)
/* Not an ECC group, check FFC */
tmp->dh = dh_groups_get(group);
if (tmp->dh) {
+ wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d",
+ group);
sae->group = group;
tmp->prime_len = tmp->dh->prime_len;
if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
@@ -66,6 +70,8 @@ int sae_set_group(struct sae_data *sae, int group)
}
/* Unsupported group */
+ wpa_printf(MSG_DEBUG,
+ "SAE: Group %d not supported by the crypto library", group);
return -1;
}
@@ -88,6 +94,7 @@ void sae_clear_temp_data(struct sae_data *sae)
crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
wpabuf_free(tmp->anti_clogging_token);
+ os_free(tmp->pw_id);
bin_clear_free(tmp, sizeof(*tmp));
sae->tmp = NULL;
}
@@ -417,12 +424,13 @@ static int get_random_qr_qnr(const u8 *prime, size_t prime_len,
static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
- size_t password_len)
+ size_t password_len, const char *identifier)
{
u8 counter, k = 40;
u8 addrs[2 * ETH_ALEN];
- const u8 *addr[2];
- size_t len[2];
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem;
u8 dummy_password[32];
size_t dummy_password_len;
int pwd_seed_odd = 0;
@@ -454,10 +462,13 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
+ if (identifier)
+ wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
+ identifier);
/*
* H(salt, ikm) = HMAC-SHA256(salt, ikm)
- * base = password
+ * base = password [|| identifier]
* pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
* base || counter)
*/
@@ -465,8 +476,15 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
addr[0] = password;
len[0] = password_len;
- addr[1] = &counter;
- len[1] = sizeof(counter);
+ num_elem = 1;
+ if (identifier) {
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ addr[num_elem] = &counter;
+ len[num_elem] = sizeof(counter);
+ num_elem++;
/*
* Continue for at least k iterations to protect against side-channel
@@ -484,8 +502,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
}
wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
- if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
- pwd_seed) < 0)
+ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+ addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
@@ -544,12 +562,13 @@ fail:
static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
- size_t password_len)
+ size_t password_len, const char *identifier)
{
u8 counter;
u8 addrs[2 * ETH_ALEN];
- const u8 *addr[2];
- size_t len[2];
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem;
int found = 0;
if (sae->tmp->pwe_ffc == NULL) {
@@ -564,14 +583,21 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
/*
* H(salt, ikm) = HMAC-SHA256(salt, ikm)
* pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
- * password || counter)
+ * password [|| identifier] || counter)
*/
sae_pwd_seed_key(addr1, addr2, addrs);
addr[0] = password;
len[0] = password_len;
- addr[1] = &counter;
- len[1] = sizeof(counter);
+ num_elem = 1;
+ if (identifier) {
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ addr[num_elem] = &counter;
+ len[num_elem] = sizeof(counter);
+ num_elem++;
for (counter = 1; !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
@@ -584,8 +610,8 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
}
wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
- if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
- pwd_seed) < 0)
+ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+ addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
if (res < 0)
@@ -696,13 +722,15 @@ fail:
int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len,
- struct sae_data *sae)
+ const char *identifier, struct sae_data *sae)
{
if (sae->tmp == NULL ||
(sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
- password_len) < 0) ||
+ password_len,
+ identifier) < 0) ||
(sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
- password_len) < 0) ||
+ password_len,
+ identifier) < 0) ||
sae_derive_commit(sae) < 0)
return -1;
return 0;
@@ -842,7 +870,7 @@ int sae_process_commit(struct sae_data *sae)
void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
- const struct wpabuf *token)
+ const struct wpabuf *token, const char *identifier)
{
u8 *pos;
@@ -876,6 +904,16 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
pos, sae->tmp->prime_len);
}
+
+ if (identifier) {
+ /* Password Identifier element */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + os_strlen(identifier));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER);
+ wpabuf_put_str(buf, identifier);
+ wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s",
+ identifier);
+ }
}
@@ -921,25 +959,70 @@ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
}
+static int sae_is_password_id_elem(const u8 *pos, const u8 *end)
+{
+ return end - pos >= 3 &&
+ pos[0] == WLAN_EID_EXTENSION &&
+ pos[1] >= 1 &&
+ end - pos - 2 >= pos[1] &&
+ pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER;
+}
+
+
static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
const u8 *end, const u8 **token,
size_t *token_len)
{
- if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) {
- size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
- sae->tmp->prime_len);
- wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
- if (token)
- *token = *pos;
- if (token_len)
- *token_len = tlen;
- *pos += tlen;
- } else {
- if (token)
- *token = NULL;
- if (token_len)
- *token_len = 0;
+ size_t scalar_elem_len, tlen;
+ const u8 *elem;
+
+ if (token)
+ *token = NULL;
+ if (token_len)
+ *token_len = 0;
+
+ scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len;
+ if (scalar_elem_len >= (size_t) (end - *pos))
+ return; /* No extra data beyond peer scalar and element */
+
+ /* It is a bit difficult to parse this now that there is an
+ * optional variable length Anti-Clogging Token field and
+ * optional variable length Password Identifier element in the
+ * frame. We are sending out fixed length Anti-Clogging Token
+ * fields, so use that length as a requirement for the received
+ * token and check for the presence of possible Password
+ * Identifier element based on the element header information.
+ */
+ tlen = end - (*pos + scalar_elem_len);
+
+ if (tlen < SHA256_MAC_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token",
+ (unsigned int) tlen);
+ return;
+ }
+
+ elem = *pos + scalar_elem_len;
+ if (sae_is_password_id_elem(elem, end)) {
+ /* Password Identifier element takes out all available
+ * extra octets, so there can be no Anti-Clogging token in
+ * this frame. */
+ return;
}
+
+ elem += SHA256_MAC_LEN;
+ if (sae_is_password_id_elem(elem, end)) {
+ /* Password Identifier element is included in the end, so
+ * remove its length from the Anti-Clogging token field. */
+ tlen -= 2 + elem[1];
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
+ if (token)
+ *token = *pos;
+ if (token_len)
+ *token_len = tlen;
+ *pos += tlen;
}
@@ -991,12 +1074,12 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
}
-static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
u8 prime[SAE_MAX_ECC_PRIME_LEN];
- if (2 * sae->tmp->prime_len > end - pos) {
+ if (2 * sae->tmp->prime_len > end - *pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
"commit-element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1007,8 +1090,8 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/* element x and y coordinates < p */
- if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
- os_memcmp(pos + sae->tmp->prime_len, prime,
+ if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 ||
+ os_memcmp(*pos + sae->tmp->prime_len, prime,
sae->tmp->prime_len) >= 0) {
wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
"element");
@@ -1016,13 +1099,13 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
}
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
- pos, sae->tmp->prime_len);
+ *pos, sae->tmp->prime_len);
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
- pos + sae->tmp->prime_len, sae->tmp->prime_len);
+ *pos + sae->tmp->prime_len, sae->tmp->prime_len);
crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
sae->tmp->peer_commit_element_ecc =
- crypto_ec_point_from_bin(sae->tmp->ec, pos);
+ crypto_ec_point_from_bin(sae->tmp->ec, *pos);
if (sae->tmp->peer_commit_element_ecc == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1032,27 +1115,29 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
+ *pos += 2 * sae->tmp->prime_len;
+
return WLAN_STATUS_SUCCESS;
}
-static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
struct crypto_bignum *res, *one;
const u8 one_bin[1] = { 0x01 };
- if (sae->tmp->prime_len > end - pos) {
+ if (sae->tmp->prime_len > end - *pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
"commit-element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
+ wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos,
sae->tmp->prime_len);
crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
sae->tmp->peer_commit_element_ffc =
- crypto_bignum_init_set(pos, sae->tmp->prime_len);
+ crypto_bignum_init_set(*pos, sae->tmp->prime_len);
if (sae->tmp->peer_commit_element_ffc == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/* 1 < element < p - 1 */
@@ -1080,11 +1165,13 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
}
crypto_bignum_deinit(res, 0);
+ *pos += sae->tmp->prime_len;
+
return WLAN_STATUS_SUCCESS;
}
-static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
if (sae->tmp->dh)
@@ -1093,6 +1180,44 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
}
+static int sae_parse_password_identifier(struct sae_data *sae,
+ const u8 *pos, const u8 *end)
+{
+ wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
+ pos, end - pos);
+ if (!sae_is_password_id_elem(pos, end)) {
+ if (sae->tmp->pw_id) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No Password Identifier included, but expected one (%s)",
+ sae->tmp->pw_id);
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ }
+ os_free(sae->tmp->pw_id);
+ sae->tmp->pw_id = NULL;
+ return WLAN_STATUS_SUCCESS; /* No Password Identifier */
+ }
+
+ if (sae->tmp->pw_id &&
+ (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) ||
+ os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: The included Password Identifier does not match the expected one (%s)",
+ sae->tmp->pw_id);
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ }
+
+ os_free(sae->tmp->pw_id);
+ sae->tmp->pw_id = os_malloc(pos[1]);
+ if (!sae->tmp->pw_id)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1);
+ sae->tmp->pw_id[pos[1] - 1] = '\0';
+ wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
+ sae->tmp->pw_id, pos[1] - 1);
+ return WLAN_STATUS_SUCCESS;
+}
+
+
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups)
{
@@ -1116,7 +1241,12 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
return res;
/* commit-element */
- res = sae_parse_commit_element(sae, pos, end);
+ res = sae_parse_commit_element(sae, &pos, end);
+ if (res != WLAN_STATUS_SUCCESS)
+ return res;
+
+ /* Optional Password Identifier element */
+ res = sae_parse_password_identifier(sae, pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
@@ -1235,7 +1365,8 @@ void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
/* Send-Confirm */
sc = wpabuf_put(buf, 0);
wpabuf_put_le16(buf, sae->send_confirm);
- sae->send_confirm++;
+ if (sae->send_confirm < 0xffff)
+ sae->send_confirm++;
if (sae->tmp->ec)
sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
@@ -1292,3 +1423,19 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
return 0;
}
+
+
+const char * sae_state_txt(enum sae_state state)
+{
+ switch (state) {
+ case SAE_NOTHING:
+ return "Nothing";
+ case SAE_COMMITTED:
+ return "Committed";
+ case SAE_CONFIRMED:
+ return "Confirmed";
+ case SAE_ACCEPTED:
+ return "Accepted";
+ }
+ return "?";
+}
diff --git a/src/common/sae.h b/src/common/sae.h
index a4270bc22d14..3fbcb58d155a 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -39,16 +39,22 @@ struct sae_temporary_data {
struct crypto_bignum *prime_buf;
struct crypto_bignum *order_buf;
struct wpabuf *anti_clogging_token;
+ char *pw_id;
+};
+
+enum sae_state {
+ SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED
};
struct sae_data {
- enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
+ enum sae_state state;
u16 send_confirm;
u8 pmk[SAE_PMK_LEN];
u8 pmkid[SAE_PMKID_LEN];
struct crypto_bignum *peer_commit_scalar;
int group;
- int sync;
+ unsigned int sync; /* protocol instance variable: Sync */
+ u16 rc; /* protocol instance variable: Rc (received send-confirm) */
struct sae_temporary_data *tmp;
};
@@ -58,14 +64,15 @@ void sae_clear_data(struct sae_data *sae);
int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len,
- struct sae_data *sae);
+ const char *identifier, struct sae_data *sae);
int sae_process_commit(struct sae_data *sae);
void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
- const struct wpabuf *token);
+ const struct wpabuf *token, const char *identifier);
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups);
void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
+const char * sae_state_txt(enum sae_state state);
#endif /* SAE_H */
diff --git a/src/common/version.h b/src/common/version.h
index 75e5c6e006cc..2f47903d407c 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -9,6 +9,6 @@
#define GIT_VERSION_STR_POSTFIX ""
#endif /* GIT_VERSION_STR_POSTFIX */
-#define VERSION_STR "2.6" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.7" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 299b8bbee031..2d989048ac94 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1,6 +1,6 @@
/*
* WPA/RSN - Shared functions for supplicant and authenticator
- * Copyright (c) 2002-2015, 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.
@@ -13,6 +13,7 @@
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
+#include "crypto/sha512.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "ieee802_11_defs.h"
@@ -20,27 +21,151 @@
#include "wpa_common.h"
-static unsigned int wpa_kck_len(int akmp)
+static unsigned int wpa_kck_len(int akmp, size_t pmk_len)
{
- if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ switch (akmp) {
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ return 24;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ return 0;
+ case WPA_KEY_MGMT_DPP:
+ return pmk_len / 2;
+ case WPA_KEY_MGMT_OWE:
+ return pmk_len / 2;
+ default:
+ return 16;
+ }
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static unsigned int wpa_kck2_len(int akmp)
+{
+ switch (akmp) {
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ return 16;
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
return 24;
- return 16;
+ default:
+ return 0;
+ }
}
+#endif /* CONFIG_IEEE80211R */
-static unsigned int wpa_kek_len(int akmp)
+static unsigned int wpa_kek_len(int akmp, size_t pmk_len)
{
- if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ switch (akmp) {
+ case WPA_KEY_MGMT_FILS_SHA384:
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ return 64;
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 32;
- return 16;
+ case WPA_KEY_MGMT_DPP:
+ return pmk_len <= 32 ? 16 : 32;
+ case WPA_KEY_MGMT_OWE:
+ return pmk_len <= 32 ? 16 : 32;
+ default:
+ return 16;
+ }
}
-unsigned int wpa_mic_len(int akmp)
+#ifdef CONFIG_IEEE80211R
+static unsigned int wpa_kek2_len(int akmp)
{
- if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ switch (akmp) {
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ return 16;
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ return 32;
+ default:
+ return 0;
+ }
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+unsigned int wpa_mic_len(int akmp, size_t pmk_len)
+{
+ switch (akmp) {
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 24;
- return 16;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ return 0;
+ case WPA_KEY_MGMT_DPP:
+ return pmk_len / 2;
+ case WPA_KEY_MGMT_OWE:
+ return pmk_len / 2;
+ default:
+ return 16;
+ }
+}
+
+
+/**
+ * wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: 1 if AKM-defined Key Descriptor Version is used; 0 otherwise
+ */
+int wpa_use_akm_defined(int akmp)
+{
+ return akmp == WPA_KEY_MGMT_OSEN ||
+ akmp == WPA_KEY_MGMT_OWE ||
+ akmp == WPA_KEY_MGMT_DPP ||
+ akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
+ wpa_key_mgmt_sae(akmp) ||
+ wpa_key_mgmt_suite_b(akmp) ||
+ wpa_key_mgmt_fils(akmp);
+}
+
+
+/**
+ * wpa_use_cmac - Is CMAC integrity algorithm used for EAPOL-Key MIC
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: 1 if CMAC is used; 0 otherwise
+ */
+int wpa_use_cmac(int akmp)
+{
+ return akmp == WPA_KEY_MGMT_OSEN ||
+ akmp == WPA_KEY_MGMT_OWE ||
+ akmp == WPA_KEY_MGMT_DPP ||
+ wpa_key_mgmt_ft(akmp) ||
+ wpa_key_mgmt_sha256(akmp) ||
+ wpa_key_mgmt_sae(akmp) ||
+ wpa_key_mgmt_suite_b(akmp);
+}
+
+
+/**
+ * wpa_use_aes_key_wrap - Is AES Keywrap algorithm used for EAPOL-Key Key Data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: 1 if AES Keywrap is used; 0 otherwise
+ *
+ * Note: AKM 00-0F-AC:1 and 00-0F-AC:2 have special rules for selecting whether
+ * to use AES Keywrap based on the negotiated pairwise cipher. This function
+ * does not cover those special cases.
+ */
+int wpa_use_aes_key_wrap(int akmp)
+{
+ return akmp == WPA_KEY_MGMT_OSEN ||
+ akmp == WPA_KEY_MGMT_OWE ||
+ akmp == WPA_KEY_MGMT_DPP ||
+ wpa_key_mgmt_ft(akmp) ||
+ wpa_key_mgmt_sha256(akmp) ||
+ wpa_key_mgmt_sae(akmp) ||
+ wpa_key_mgmt_suite_b(akmp);
}
@@ -67,30 +192,50 @@ unsigned int wpa_mic_len(int akmp)
int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
const u8 *buf, size_t len, u8 *mic)
{
- u8 hash[SHA384_MAC_LEN];
+ u8 hash[SHA512_MAC_LEN];
+
+ if (key_len == 0) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: KCK not set - cannot calculate MIC");
+ return -1;
+ }
switch (ver) {
#ifndef CONFIG_FIPS
case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-MD5");
return hmac_md5(key, key_len, buf, len, mic);
#endif /* CONFIG_FIPS */
case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-SHA1");
if (hmac_sha1(key, key_len, buf, len, hash))
return -1;
os_memcpy(mic, hash, MD5_MAC_LEN);
break;
#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC");
return omac1_aes_128(key, buf, len, mic);
#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
case WPA_KEY_INFO_TYPE_AKM_DEFINED:
switch (akmp) {
+#ifdef CONFIG_SAE
+ case WPA_KEY_MGMT_SAE:
+ case WPA_KEY_MGMT_FT_SAE:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)");
+ return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_SAE */
#ifdef CONFIG_HS20
case WPA_KEY_MGMT_OSEN:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)");
return omac1_aes_128(key, buf, len, mic);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_SUITEB
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using HMAC-SHA256 (AKM-defined - Suite B)");
if (hmac_sha256(key, key_len, buf, len, hash))
return -1;
os_memcpy(mic, hash, MD5_MAC_LEN);
@@ -98,16 +243,79 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - Suite B 192-bit)");
if (hmac_sha384(key, key_len, buf, len, hash))
return -1;
os_memcpy(mic, hash, 24);
break;
#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_OWE
+ case WPA_KEY_MGMT_OWE:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)",
+ (unsigned int) key_len * 8 * 2);
+ if (key_len == 128 / 8) {
+ if (hmac_sha256(key, key_len, buf, len, hash))
+ return -1;
+ } else if (key_len == 192 / 8) {
+ if (hmac_sha384(key, key_len, buf, len, hash))
+ return -1;
+ } else if (key_len == 256 / 8) {
+ if (hmac_sha512(key, key_len, buf, len, hash))
+ return -1;
+ } else {
+ wpa_printf(MSG_INFO,
+ "OWE: Unsupported KCK length: %u",
+ (unsigned int) key_len);
+ return -1;
+ }
+ os_memcpy(mic, hash, key_len);
+ break;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ case WPA_KEY_MGMT_DPP:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)",
+ (unsigned int) key_len * 8 * 2);
+ if (key_len == 128 / 8) {
+ if (hmac_sha256(key, key_len, buf, len, hash))
+ return -1;
+ } else if (key_len == 192 / 8) {
+ if (hmac_sha384(key, key_len, buf, len, hash))
+ return -1;
+ } else if (key_len == 256 / 8) {
+ if (hmac_sha512(key, key_len, buf, len, hash))
+ return -1;
+ } else {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported KCK length: %u",
+ (unsigned int) key_len);
+ return -1;
+ }
+ os_memcpy(mic, hash, key_len);
+ break;
+#endif /* CONFIG_DPP */
+#if defined(CONFIG_IEEE80211R) && defined(CONFIG_SHA384)
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - FT 802.1X SHA384)");
+ if (hmac_sha384(key, key_len, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 24);
+ break;
+#endif /* CONFIG_IEEE80211R && CONFIG_SHA384 */
default:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)",
+ akmp);
return -1;
}
break;
default:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC algorithm not known (ver=%d)",
+ ver);
return -1;
}
@@ -133,10 +341,6 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
* PTK = PRF-X(PMK, "Pairwise key expansion",
* Min(AA, SA) || Max(AA, SA) ||
* Min(ANonce, SNonce) || Max(ANonce, SNonce))
- *
- * STK = PRF-X(SMK, "Peer key expansion",
- * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
- * Min(INonce, PNonce) || Max(INonce, PNonce))
*/
int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
@@ -147,6 +351,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
size_t ptk_len;
+ if (pmk_len == 0) {
+ wpa_printf(MSG_ERROR, "WPA: No PMK set for PTK derivation");
+ return -1;
+ }
+
if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
os_memcpy(data, addr1, ETH_ALEN);
os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
@@ -165,24 +374,62 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
WPA_NONCE_LEN);
}
- ptk->kck_len = wpa_kck_len(akmp);
- ptk->kek_len = wpa_kek_len(akmp);
+ ptk->kck_len = wpa_kck_len(akmp, pmk_len);
+ ptk->kek_len = wpa_kek_len(akmp, pmk_len);
ptk->tk_len = wpa_cipher_key_len(cipher);
+ if (ptk->tk_len == 0) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Unsupported cipher (0x%x) used in PTK derivation",
+ cipher);
+ return -1;
+ }
ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
-#ifdef CONFIG_SUITEB192
- if (wpa_key_mgmt_sha384(akmp))
- sha384_prf(pmk, pmk_len, label, data, sizeof(data),
- tmp, ptk_len);
- else
-#endif /* CONFIG_SUITEB192 */
-#ifdef CONFIG_IEEE80211W
- if (wpa_key_mgmt_sha256(akmp))
- sha256_prf(pmk, pmk_len, label, data, sizeof(data),
- tmp, ptk_len);
- else
-#endif /* CONFIG_IEEE80211W */
- sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
+ if (wpa_key_mgmt_sha384(akmp)) {
+#if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
+ if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+ tmp, ptk_len) < 0)
+ return -1;
+#else /* CONFIG_SUITEB192 || CONFIG_FILS */
+ return -1;
+#endif /* CONFIG_SUITEB192 || CONFIG_FILS */
+ } else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS)
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
+ if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+ tmp, ptk_len) < 0)
+ return -1;
+#else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
+ return -1;
+#endif /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
+#ifdef CONFIG_DPP
+ } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
+ if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+ tmp, ptk_len) < 0)
+ return -1;
+ } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
+ if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+ tmp, ptk_len) < 0)
+ return -1;
+ } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
+ if (sha512_prf(pmk, pmk_len, label, data, sizeof(data),
+ tmp, ptk_len) < 0)
+ return -1;
+ } else if (akmp == WPA_KEY_MGMT_DPP) {
+ wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u",
+ (unsigned int) pmk_len);
+ return -1;
+#endif /* CONFIG_DPP */
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
+ if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp,
+ ptk_len) < 0)
+ return -1;
+ }
wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
MAC2STR(addr1), MAC2STR(addr2));
@@ -200,10 +447,290 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
+ ptk->kek2_len = 0;
+ ptk->kck2_len = 0;
+
os_memset(tmp, 0, sizeof(tmp));
return 0;
}
+#ifdef CONFIG_FILS
+
+int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
+ const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
+ size_t dh_ss_len, u8 *pmk, size_t *pmk_len)
+{
+ u8 nonces[2 * FILS_NONCE_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+ size_t num_elem;
+ int res;
+
+ /* PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) */
+ wpa_printf(MSG_DEBUG, "FILS: rMSK to PMK derivation");
+
+ if (wpa_key_mgmt_sha384(akmp))
+ *pmk_len = SHA384_MAC_LEN;
+ else if (wpa_key_mgmt_sha256(akmp))
+ *pmk_len = SHA256_MAC_LEN;
+ else
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len);
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len);
+
+ os_memcpy(nonces, snonce, FILS_NONCE_LEN);
+ os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN);
+ addr[0] = rmsk;
+ len[0] = rmsk_len;
+ num_elem = 1;
+ if (dh_ss) {
+ addr[1] = dh_ss;
+ len[1] = dh_ss_len;
+ num_elem++;
+ }
+ if (wpa_key_mgmt_sha384(akmp))
+ res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
+ addr, len, pmk);
+ else
+ res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
+ addr, len, pmk);
+ if (res == 0)
+ wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len);
+ else
+ *pmk_len = 0;
+ return res;
+}
+
+
+int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
+ u8 *pmkid)
+{
+ const u8 *addr[1];
+ size_t len[1];
+ u8 hash[SHA384_MAC_LEN];
+ int res;
+
+ /* PMKID = Truncate-128(Hash(EAP-Initiate/Reauth)) */
+ addr[0] = reauth;
+ len[0] = reauth_len;
+ if (wpa_key_mgmt_sha384(akmp))
+ res = sha384_vector(1, addr, len, hash);
+ else if (wpa_key_mgmt_sha256(akmp))
+ res = sha256_vector(1, addr, len, hash);
+ else
+ return -1;
+ if (res)
+ return res;
+ os_memcpy(pmkid, hash, PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
+ return 0;
+}
+
+
+int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
+ const u8 *snonce, const u8 *anonce, const u8 *dhss,
+ size_t dhss_len, struct wpa_ptk *ptk,
+ u8 *ick, size_t *ick_len, int akmp, int cipher,
+ u8 *fils_ft, size_t *fils_ft_len)
+{
+ u8 *data, *pos;
+ size_t data_len;
+ u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
+ FILS_FT_MAX_LEN];
+ size_t key_data_len;
+ const char *label = "FILS PTK Derivation";
+ int ret = -1;
+
+ /*
+ * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
+ * SPA || AA || SNonce || ANonce [ || DHss ])
+ * ICK = L(FILS-Key-Data, 0, ICK_bits)
+ * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
+ * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
+ * If doing FT initial mobility domain association:
+ * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
+ * FILS-FT_bits)
+ */
+ data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len;
+ data = os_malloc(data_len);
+ if (!data)
+ goto err;
+ pos = data;
+ os_memcpy(pos, spa, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, aa, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, snonce, FILS_NONCE_LEN);
+ pos += FILS_NONCE_LEN;
+ os_memcpy(pos, anonce, FILS_NONCE_LEN);
+ pos += FILS_NONCE_LEN;
+ if (dhss)
+ os_memcpy(pos, dhss, dhss_len);
+
+ ptk->kck_len = 0;
+ ptk->kek_len = wpa_kek_len(akmp, pmk_len);
+ ptk->tk_len = wpa_cipher_key_len(cipher);
+ if (wpa_key_mgmt_sha384(akmp))
+ *ick_len = 48;
+ else if (wpa_key_mgmt_sha256(akmp))
+ *ick_len = 32;
+ else
+ goto err;
+ key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
+
+ if (fils_ft && fils_ft_len) {
+ if (akmp == WPA_KEY_MGMT_FT_FILS_SHA256) {
+ *fils_ft_len = 32;
+ } else if (akmp == WPA_KEY_MGMT_FT_FILS_SHA384) {
+ *fils_ft_len = 48;
+ } else {
+ *fils_ft_len = 0;
+ fils_ft = NULL;
+ }
+ key_data_len += *fils_ft_len;
+ }
+
+ if (wpa_key_mgmt_sha384(akmp)) {
+ wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)");
+ if (sha384_prf(pmk, pmk_len, label, data, data_len,
+ tmp, key_data_len) < 0)
+ goto err;
+ } else {
+ wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)");
+ if (sha256_prf(pmk, pmk_len, label, data, data_len,
+ tmp, key_data_len) < 0)
+ goto err;
+ }
+
+ wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
+ " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+ if (dhss)
+ wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
+
+ os_memcpy(ick, tmp, *ick_len);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, *ick_len);
+
+ os_memcpy(ptk->kek, tmp + *ick_len, ptk->kek_len);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", ptk->kek, ptk->kek_len);
+
+ os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len);
+
+ if (fils_ft && fils_ft_len) {
+ os_memcpy(fils_ft, tmp + *ick_len + ptk->kek_len + ptk->tk_len,
+ *fils_ft_len);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-FT",
+ fils_ft, *fils_ft_len);
+ }
+
+ ptk->kek2_len = 0;
+ ptk->kck2_len = 0;
+
+ os_memset(tmp, 0, sizeof(tmp));
+ ret = 0;
+err:
+ bin_clear_free(data, data_len);
+ return ret;
+}
+
+
+int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
+ const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
+ const u8 *g_sta, size_t g_sta_len,
+ const u8 *g_ap, size_t g_ap_len,
+ int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
+ size_t *key_auth_len)
+{
+ const u8 *addr[6];
+ size_t len[6];
+ size_t num_elem = 4;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR
+ " AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid));
+ wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len);
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len);
+ wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
+
+ /*
+ * For (Re)Association Request frame (STA->AP):
+ * Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID
+ * [ || gSTA || gAP ])
+ */
+ addr[0] = snonce;
+ len[0] = FILS_NONCE_LEN;
+ addr[1] = anonce;
+ len[1] = FILS_NONCE_LEN;
+ addr[2] = sta_addr;
+ len[2] = ETH_ALEN;
+ addr[3] = bssid;
+ len[3] = ETH_ALEN;
+ if (g_sta && g_ap_len && g_ap && g_ap_len) {
+ addr[4] = g_sta;
+ len[4] = g_sta_len;
+ addr[5] = g_ap;
+ len[5] = g_ap_len;
+ num_elem = 6;
+ }
+
+ if (wpa_key_mgmt_sha384(akmp)) {
+ *key_auth_len = 48;
+ res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
+ key_auth_sta);
+ } else if (wpa_key_mgmt_sha256(akmp)) {
+ *key_auth_len = 32;
+ res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
+ key_auth_sta);
+ } else {
+ return -1;
+ }
+ if (res < 0)
+ return res;
+
+ /*
+ * For (Re)Association Response frame (AP->STA):
+ * Key-Auth = HMAC-Hash(ICK, ANonce || SNonce || AP-BSSID || STA-MAC
+ * [ || gAP || gSTA ])
+ */
+ addr[0] = anonce;
+ addr[1] = snonce;
+ addr[2] = bssid;
+ addr[3] = sta_addr;
+ if (g_sta && g_ap_len && g_ap && g_ap_len) {
+ addr[4] = g_ap;
+ len[4] = g_ap_len;
+ addr[5] = g_sta;
+ len[5] = g_sta_len;
+ }
+
+ if (wpa_key_mgmt_sha384(akmp))
+ res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
+ key_auth_ap);
+ else if (wpa_key_mgmt_sha256(akmp))
+ res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
+ key_auth_ap);
+ if (res < 0)
+ return res;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (STA)",
+ key_auth_sta, *key_auth_len);
+ wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (AP)",
+ key_auth_ap, *key_auth_len);
+
+ return 0;
+}
+
+#endif /* CONFIG_FILS */
+
#ifdef CONFIG_IEEE80211R
int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
@@ -216,14 +743,23 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
const u8 *addr[9];
size_t len[9];
size_t i, num_elem = 0;
- u8 zero_mic[16];
-
- if (kck_len != 16) {
+ u8 zero_mic[24];
+ size_t mic_len, fte_fixed_len;
+
+ if (kck_len == 16) {
+ mic_len = 16;
+#ifdef CONFIG_SHA384
+ } else if (kck_len == 24) {
+ mic_len = 24;
+#endif /* CONFIG_SHA384 */
+ } else {
wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
(unsigned int) kck_len);
return -1;
}
+ fte_fixed_len = sizeof(struct rsn_ftie) - 16 + mic_len;
+
addr[num_elem] = sta_addr;
len[num_elem] = ETH_ALEN;
num_elem++;
@@ -247,7 +783,7 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
num_elem++;
}
if (ftie) {
- if (ftie_len < 2 + sizeof(struct rsn_ftie))
+ if (ftie_len < 2 + fte_fixed_len)
return -1;
/* IE hdr and mic_control */
@@ -256,14 +792,14 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
num_elem++;
/* MIC field with all zeros */
- os_memset(zero_mic, 0, sizeof(zero_mic));
+ os_memset(zero_mic, 0, mic_len);
addr[num_elem] = zero_mic;
- len[num_elem] = sizeof(zero_mic);
+ len[num_elem] = mic_len;
num_elem++;
/* Rest of FTIE */
- addr[num_elem] = ftie + 2 + 2 + 16;
- len[num_elem] = ftie_len - (2 + 2 + 16);
+ addr[num_elem] = ftie + 2 + 2 + mic_len;
+ len[num_elem] = ftie_len - (2 + 2 + mic_len);
num_elem++;
}
if (ric) {
@@ -274,7 +810,17 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
for (i = 0; i < num_elem; i++)
wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
- if (omac1_aes_128_vector(kck, num_elem, addr, len, mic))
+#ifdef CONFIG_SHA384
+ if (kck_len == 24) {
+ u8 hash[SHA384_MAC_LEN];
+
+ if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 24);
+ }
+#endif /* CONFIG_SHA384 */
+ if (kck_len == 16 &&
+ omac1_aes_128_vector(kck, num_elem, addr, len, mic))
return -1;
return 0;
@@ -282,23 +828,27 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
- struct wpa_ft_ies *parse)
+ struct wpa_ft_ies *parse, int use_sha384)
{
const u8 *end, *pos;
parse->ftie = ie;
parse->ftie_len = ie_len;
- pos = ie + sizeof(struct rsn_ftie);
+ pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) :
+ sizeof(struct rsn_ftie));
end = ie + ie_len;
+ wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
while (end - pos >= 2) {
u8 id, len;
id = *pos++;
len = *pos++;
- if (len > end - pos)
+ if (len > end - pos) {
+ wpa_printf(MSG_DEBUG, "FT: Truncated subelement");
break;
+ }
switch (id) {
case FTIE_SUBELEM_R1KH_ID:
@@ -330,6 +880,9 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
parse->igtk_len = len;
break;
#endif /* CONFIG_IEEE80211W */
+ default:
+ wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id);
+ break;
}
pos += len;
@@ -340,13 +893,19 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse)
+ struct wpa_ft_ies *parse, int use_sha384)
{
const u8 *end, *pos;
struct wpa_ie_data data;
int ret;
const struct rsn_ftie *ftie;
int prot_ie_count = 0;
+ int update_use_sha384 = 0;
+
+ if (use_sha384 < 0) {
+ use_sha384 = 0;
+ update_use_sha384 = 1;
+ }
os_memset(parse, 0, sizeof(*parse));
if (ies == NULL)
@@ -364,6 +923,7 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
switch (id) {
case WLAN_EID_RSN:
+ wpa_hexdump(MSG_DEBUG, "FT: RSNE", pos, len);
parse->rsn = pos;
parse->rsn_len = len;
ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
@@ -376,22 +936,65 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
}
if (data.num_pmkid == 1 && data.pmkid)
parse->rsn_pmkid = data.pmkid;
+ parse->key_mgmt = data.key_mgmt;
+ parse->pairwise_cipher = data.pairwise_cipher;
+ if (update_use_sha384) {
+ use_sha384 =
+ wpa_key_mgmt_sha384(parse->key_mgmt);
+ update_use_sha384 = 0;
+ }
break;
case WLAN_EID_MOBILITY_DOMAIN:
+ wpa_hexdump(MSG_DEBUG, "FT: MDE", pos, len);
if (len < sizeof(struct rsn_mdie))
return -1;
parse->mdie = pos;
parse->mdie_len = len;
break;
case WLAN_EID_FAST_BSS_TRANSITION:
+ wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len);
+ if (use_sha384) {
+ const struct rsn_ftie_sha384 *ftie_sha384;
+
+ if (len < sizeof(*ftie_sha384))
+ return -1;
+ ftie_sha384 =
+ (const struct rsn_ftie_sha384 *) pos;
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
+ ftie_sha384->mic_control, 2);
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
+ ftie_sha384->mic,
+ sizeof(ftie_sha384->mic));
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
+ ftie_sha384->anonce,
+ WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
+ ftie_sha384->snonce,
+ WPA_NONCE_LEN);
+ prot_ie_count = ftie_sha384->mic_control[1];
+ if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0)
+ return -1;
+ break;
+ }
+
if (len < sizeof(*ftie))
return -1;
ftie = (const struct rsn_ftie *) pos;
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
+ ftie->mic_control, 2);
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
+ ftie->mic, sizeof(ftie->mic));
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
+ ftie->anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
+ ftie->snonce, WPA_NONCE_LEN);
prot_ie_count = ftie->mic_control[1];
- if (wpa_ft_parse_ftie(pos, len, parse) < 0)
+ if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0)
return -1;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
+ wpa_hexdump(MSG_DEBUG, "FT: Timeout Interval",
+ pos, len);
if (len != 5)
break;
parse->tie = pos;
@@ -493,6 +1096,10 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s)
return WPA_KEY_MGMT_FT_IEEE8021X;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
return WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_SHA384
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384)
+ return WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
@@ -510,6 +1117,22 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s)
return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256)
+ return WPA_KEY_MGMT_FILS_SHA256;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384)
+ return WPA_KEY_MGMT_FILS_SHA384;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256)
+ return WPA_KEY_MGMT_FT_FILS_SHA256;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384)
+ return WPA_KEY_MGMT_FT_FILS_SHA384;
+#ifdef CONFIG_OWE
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE)
+ return WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP)
+ return WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
return WPA_KEY_MGMT_OSEN;
return 0;
@@ -579,6 +1202,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
pos = rsn_ie + 6;
left = rsn_ie_len - 6;
+ data->group_cipher = WPA_CIPHER_GTK_NOT_USED;
+ data->key_mgmt = WPA_KEY_MGMT_OSEN;
data->proto = WPA_PROTO_OSEN;
} else {
const struct rsn_ie_hdr *hdr;
@@ -849,27 +1474,39 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
*
* IEEE Std 802.11r-2008 - 8.5.1.5.3
*/
-void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
- const u8 *ssid, size_t ssid_len,
- const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
- const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
+int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
+ int use_sha384)
{
u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
FT_R0KH_ID_MAX_LEN + ETH_ALEN];
- u8 *pos, r0_key_data[48], hash[32];
+ u8 *pos, r0_key_data[64], hash[48];
const u8 *addr[2];
size_t len[2];
+ size_t q = use_sha384 ? 48 : 32;
+ size_t r0_key_data_len = q + 16;
/*
* R0-Key-Data = KDF-384(XXKey, "FT-R0",
* SSIDlength || SSID || MDID || R0KHlength ||
* R0KH-ID || S0KH-ID)
- * XXKey is either the second 256 bits of MSK or PSK.
- * PMK-R0 = L(R0-Key-Data, 0, 256)
- * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
+ * XXKey is either the second 256 bits of MSK or PSK; or the first
+ * 384 bits of MSK for FT-EAP-SHA384.
+ * PMK-R0 = L(R0-Key-Data, 0, Q)
+ * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
+ * Q = 384 for FT-EAP-SHA384; otherwise, 256
*/
if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
- return;
+ return -1;
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s",
+ use_sha384 ? "SHA384" : "SHA256");
+ wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len);
+ wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len);
+ wpa_printf(MSG_DEBUG, "FT: S0KH-ID: " MACSTR, MAC2STR(s0kh_id));
pos = buf;
*pos++ = ssid_len;
os_memcpy(pos, ssid, ssid_len);
@@ -882,20 +1519,51 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
os_memcpy(pos, s0kh_id, ETH_ALEN);
pos += ETH_ALEN;
- sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
- r0_key_data, sizeof(r0_key_data));
- os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
+#ifdef CONFIG_SHA384
+ if (use_sha384) {
+ if (xxkey_len != SHA384_MAC_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected XXKey length %d (expected %d)",
+ (int) xxkey_len, SHA384_MAC_LEN);
+ return -1;
+ }
+ if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len) < 0)
+ return -1;
+ }
+#endif /* CONFIG_SHA384 */
+ if (!use_sha384) {
+ if (xxkey_len != PMK_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected XXKey length %d (expected %d)",
+ (int) xxkey_len, PMK_LEN);
+ return -1;
+ }
+ if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len) < 0)
+ return -1;
+ }
+ os_memcpy(pmk_r0, r0_key_data, q);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16);
/*
- * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)
+ * PMKR0Name = Truncate-128(Hash("FT-R0N" || PMK-R0Name-Salt)
*/
addr[0] = (const u8 *) "FT-R0N";
len[0] = 6;
- addr[1] = r0_key_data + PMK_LEN;
+ addr[1] = &r0_key_data[q];
len[1] = 16;
- sha256_vector(2, addr, len, hash);
+#ifdef CONFIG_SHA384
+ if (use_sha384 && sha384_vector(2, addr, len, hash) < 0)
+ return -1;
+#endif /* CONFIG_SHA384 */
+ if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
+ return -1;
os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
+ os_memset(r0_key_data, 0, sizeof(r0_key_data));
+ return 0;
}
@@ -904,16 +1572,16 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
*
* IEEE Std 802.11r-2008 - 8.5.1.5.4
*/
-void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
- const u8 *s1kh_id, u8 *pmk_r1_name)
+int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+ const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384)
{
- u8 hash[32];
+ u8 hash[48];
const u8 *addr[4];
size_t len[4];
/*
- * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name ||
- * R1KH-ID || S1KH-ID))
+ * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name ||
+ * R1KH-ID || S1KH-ID))
*/
addr[0] = (const u8 *) "FT-R1N";
len[0] = 6;
@@ -924,8 +1592,14 @@ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
addr[3] = s1kh_id;
len[3] = ETH_ALEN;
- sha256_vector(4, addr, len, hash);
+#ifdef CONFIG_SHA384
+ if (use_sha384 && sha384_vector(4, addr, len, hash) < 0)
+ return -1;
+#endif /* CONFIG_SHA384 */
+ if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
+ return -1;
os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
+ return 0;
}
@@ -934,23 +1608,46 @@ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
*
* IEEE Std 802.11r-2008 - 8.5.1.5.4
*/
-void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
- const u8 *r1kh_id, const u8 *s1kh_id,
- u8 *pmk_r1, u8 *pmk_r1_name)
+int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
+ const u8 *pmk_r0_name,
+ const u8 *r1kh_id, const u8 *s1kh_id,
+ u8 *pmk_r1, u8 *pmk_r1_name)
{
u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
u8 *pos;
/* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s",
+ pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256");
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN);
+ wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id));
pos = buf;
os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
pos += FT_R1KH_ID_LEN;
os_memcpy(pos, s1kh_id, ETH_ALEN);
pos += ETH_ALEN;
- sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN);
+#ifdef CONFIG_SHA384
+ if (pmk_r0_len == SHA384_MAC_LEN &&
+ sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
+ return -1;
+#endif /* CONFIG_SHA384 */
+ if (pmk_r0_len == PMK_LEN &&
+ sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
+ return -1;
+ if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) {
+ wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d",
+ (int) pmk_r0_len);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len);
- wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name);
+ return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id,
+ pmk_r1_name,
+ pmk_r0_len == SHA384_MAC_LEN);
}
@@ -959,7 +1656,8 @@ void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
*
* IEEE Std 802.11r-2008 - 8.5.1.5.5
*/
-int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len,
+ const u8 *snonce, const u8 *anonce,
const u8 *sta_addr, const u8 *bssid,
const u8 *pmk_r1_name,
struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher)
@@ -968,13 +1666,21 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
u8 *pos, hash[32];
const u8 *addr[6];
size_t len[6];
- u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
- size_t ptk_len;
+ u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+ size_t ptk_len, offset;
+ int use_sha384 = wpa_key_mgmt_sha384(akmp);
/*
* PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
* BSSID || STA-ADDR)
*/
+ wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s",
+ use_sha384 ? "SHA384" : "SHA256");
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
+ wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
+ wpa_printf(MSG_DEBUG, "FT: BSSID=" MACSTR " STA-ADDR=" MACSTR,
+ MAC2STR(bssid), MAC2STR(sta_addr));
pos = buf;
os_memcpy(pos, snonce, WPA_NONCE_LEN);
pos += WPA_NONCE_LEN;
@@ -985,17 +1691,45 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
os_memcpy(pos, sta_addr, ETH_ALEN);
pos += ETH_ALEN;
- ptk->kck_len = wpa_kck_len(akmp);
- ptk->kek_len = wpa_kek_len(akmp);
+ ptk->kck_len = wpa_kck_len(akmp, PMK_LEN);
+ ptk->kck2_len = wpa_kck2_len(akmp);
+ ptk->kek_len = wpa_kek_len(akmp, PMK_LEN);
+ ptk->kek2_len = wpa_kek2_len(akmp);
ptk->tk_len = wpa_cipher_key_len(cipher);
- ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
-
- sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len);
+ ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len +
+ ptk->kck2_len + ptk->kek2_len;
+
+#ifdef CONFIG_SHA384
+ if (use_sha384) {
+ if (pmk_r1_len != SHA384_MAC_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected PMK-R1 length %d (expected %d)",
+ (int) pmk_r1_len, SHA384_MAC_LEN);
+ return -1;
+ }
+ if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len) < 0)
+ return -1;
+ }
+#endif /* CONFIG_SHA384 */
+ if (!use_sha384) {
+ if (pmk_r1_len != PMK_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected PMK-R1 length %d (expected %d)",
+ (int) pmk_r1_len, PMK_LEN);
+ return -1;
+ }
+ if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len) < 0)
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len);
/*
* PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
* ANonce || BSSID || STA-ADDR))
*/
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
addr[0] = pmk_r1_name;
len[0] = WPA_PMK_NAME_LEN;
addr[1] = (const u8 *) "FT-PTKN";
@@ -1009,15 +1743,28 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
addr[5] = sta_addr;
len[5] = ETH_ALEN;
- sha256_vector(6, addr, len, hash);
+ if (sha256_vector(6, addr, len, hash) < 0)
+ return -1;
os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
os_memcpy(ptk->kck, tmp, ptk->kck_len);
- os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
- os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+ offset = ptk->kck_len;
+ os_memcpy(ptk->kek, tmp + offset, ptk->kek_len);
+ offset += ptk->kek_len;
+ os_memcpy(ptk->tk, tmp + offset, ptk->tk_len);
+ offset += ptk->tk_len;
+ os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len);
+ offset = ptk->kck2_len;
+ os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len);
wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
+ if (ptk->kck2_len)
+ wpa_hexdump_key(MSG_DEBUG, "FT: KCK2",
+ ptk->kck2, ptk->kck2_len);
+ if (ptk->kek2_len)
+ wpa_hexdump_key(MSG_DEBUG, "FT: KEK2",
+ ptk->kek2, ptk->kek2_len);
wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
@@ -1036,29 +1783,48 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
* @aa: Authenticator address
* @spa: Supplicant address
* @pmkid: Buffer for PMKID
- * @use_sha256: Whether to use SHA256-based KDF
+ * @akmp: Negotiated key management protocol
*
- * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
- * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
+ * IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy
+ * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
+ * PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA))
+ * AKM: 00-0F-AC:11
+ * See rsn_pmkid_suite_b()
+ * AKM: 00-0F-AC:12
+ * See rsn_pmkid_suite_b_192()
+ * AKM: 00-0F-AC:13, 00-0F-AC:15, 00-0F-AC:17
+ * PMKID = Truncate-128(HMAC-SHA-384(PMK, "PMK Name" || AA || SPA))
+ * Otherwise:
+ * PMKID = Truncate-128(HMAC-SHA-1(PMK, "PMK Name" || AA || SPA))
*/
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
- u8 *pmkid, int use_sha256)
+ u8 *pmkid, int akmp)
{
char *title = "PMK Name";
const u8 *addr[3];
const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
- unsigned char hash[SHA256_MAC_LEN];
+ unsigned char hash[SHA384_MAC_LEN];
addr[0] = (u8 *) title;
addr[1] = aa;
addr[2] = spa;
-#ifdef CONFIG_IEEE80211W
- if (use_sha256)
+ if (0) {
+#if defined(CONFIG_FILS) || defined(CONFIG_SHA384)
+ } else if (wpa_key_mgmt_sha384(akmp)) {
+ wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384");
+ hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash);
+#endif /* CONFIG_FILS || CONFIG_SHA384 */
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
+ } else if (wpa_key_mgmt_sha256(akmp)) {
+ wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256");
hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
- else
-#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211W || CONFIG_FILS */
+ } else {
+ wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-1");
hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: Derived PMKID", hash, PMKID_LEN);
os_memcpy(pmkid, hash, PMKID_LEN);
}
@@ -1155,6 +1921,14 @@ const char * wpa_cipher_txt(int cipher)
return "GCMP-256";
case WPA_CIPHER_CCMP_256:
return "CCMP-256";
+ case WPA_CIPHER_AES_128_CMAC:
+ return "BIP";
+ case WPA_CIPHER_BIP_GMAC_128:
+ return "BIP-GMAC-128";
+ case WPA_CIPHER_BIP_GMAC_256:
+ return "BIP-GMAC-256";
+ case WPA_CIPHER_BIP_CMAC_256:
+ return "BIP-CMAC-256";
case WPA_CIPHER_GTK_NOT_USED:
return "GTK_NOT_USED";
default:
@@ -1191,6 +1965,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
#ifdef CONFIG_IEEE80211R
case WPA_KEY_MGMT_FT_IEEE8021X:
return "FT-EAP";
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ return "FT-EAP-SHA384";
case WPA_KEY_MGMT_FT_PSK:
return "FT-PSK";
#endif /* CONFIG_IEEE80211R */
@@ -1212,6 +1988,18 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
return "WPA2-EAP-SUITE-B";
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
return "WPA2-EAP-SUITE-B-192";
+ case WPA_KEY_MGMT_FILS_SHA256:
+ return "FILS-SHA256";
+ case WPA_KEY_MGMT_FILS_SHA384:
+ return "FILS-SHA384";
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ return "FT-FILS-SHA256";
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ return "FT-FILS-SHA384";
+ case WPA_KEY_MGMT_OWE:
+ return "OWE";
+ case WPA_KEY_MGMT_DPP:
+ return "DPP";
default:
return "UNKNOWN";
}
@@ -1220,28 +2008,36 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
u32 wpa_akm_to_suite(int akm)
{
+ if (akm & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ return RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
- return WLAN_AKM_SUITE_FT_8021X;
+ return RSN_AUTH_KEY_MGMT_FT_802_1X;
if (akm & WPA_KEY_MGMT_FT_PSK)
- return WLAN_AKM_SUITE_FT_PSK;
- if (akm & WPA_KEY_MGMT_IEEE8021X)
- return WLAN_AKM_SUITE_8021X;
+ return RSN_AUTH_KEY_MGMT_FT_PSK;
if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
- return WLAN_AKM_SUITE_8021X_SHA256;
+ return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
if (akm & WPA_KEY_MGMT_IEEE8021X)
- return WLAN_AKM_SUITE_8021X;
+ return RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
if (akm & WPA_KEY_MGMT_PSK_SHA256)
- return WLAN_AKM_SUITE_PSK_SHA256;
+ return RSN_AUTH_KEY_MGMT_PSK_SHA256;
if (akm & WPA_KEY_MGMT_PSK)
- return WLAN_AKM_SUITE_PSK;
+ return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
if (akm & WPA_KEY_MGMT_CCKM)
- return WLAN_AKM_SUITE_CCKM;
+ return RSN_AUTH_KEY_MGMT_CCKM;
if (akm & WPA_KEY_MGMT_OSEN)
- return WLAN_AKM_SUITE_OSEN;
+ return RSN_AUTH_KEY_MGMT_OSEN;
if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
- return WLAN_AKM_SUITE_8021X_SUITE_B;
+ return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
- return WLAN_AKM_SUITE_8021X_SUITE_B_192;
+ return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ if (akm & WPA_KEY_MGMT_FILS_SHA256)
+ return RSN_AUTH_KEY_MGMT_FILS_SHA256;
+ if (akm & WPA_KEY_MGMT_FILS_SHA384)
+ return RSN_AUTH_KEY_MGMT_FILS_SHA384;
+ if (akm & WPA_KEY_MGMT_FT_FILS_SHA256)
+ return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+ if (akm & WPA_KEY_MGMT_FT_FILS_SHA384)
+ return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
return 0;
}
@@ -1283,7 +2079,7 @@ int wpa_compare_rsn_ie(int ft_initial_assoc,
}
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
{
u8 *start, *end, *rpos, *rend;
@@ -1382,7 +2178,7 @@ int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
return 0;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
int wpa_cipher_key_len(int cipher)
@@ -1421,7 +2217,7 @@ int wpa_cipher_rsc_len(int cipher)
}
-int wpa_cipher_to_alg(int cipher)
+enum wpa_alg wpa_cipher_to_alg(int cipher)
{
switch (cipher) {
case WPA_CIPHER_CCMP_256:
@@ -1616,6 +2412,14 @@ int wpa_parse_cipher(const char *value)
val |= WPA_CIPHER_NONE;
else if (os_strcmp(start, "GTK_NOT_USED") == 0)
val |= WPA_CIPHER_GTK_NOT_USED;
+ else if (os_strcmp(start, "AES-128-CMAC") == 0)
+ val |= WPA_CIPHER_AES_128_CMAC;
+ else if (os_strcmp(start, "BIP-GMAC-128") == 0)
+ val |= WPA_CIPHER_BIP_GMAC_128;
+ else if (os_strcmp(start, "BIP-GMAC-256") == 0)
+ val |= WPA_CIPHER_BIP_GMAC_256;
+ else if (os_strcmp(start, "BIP-CMAC-256") == 0)
+ val |= WPA_CIPHER_BIP_CMAC_256;
else {
os_free(buf);
return -1;
@@ -1671,6 +2475,34 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim)
return -1;
pos += ret;
}
+ if (ciphers & WPA_CIPHER_AES_128_CMAC) {
+ ret = os_snprintf(pos, end - pos, "%sAES-128-CMAC",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (ciphers & WPA_CIPHER_BIP_GMAC_128) {
+ ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (ciphers & WPA_CIPHER_BIP_GMAC_256) {
+ ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (ciphers & WPA_CIPHER_BIP_CMAC_256) {
+ ret = os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
+ pos == start ? "" : delim);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
if (ciphers & WPA_CIPHER_NONE) {
ret = os_snprintf(pos, end - pos, "%sNONE",
pos == start ? "" : delim);
@@ -1705,3 +2537,29 @@ int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
return WPA_CIPHER_CCMP_256;
return WPA_CIPHER_CCMP;
}
+
+
+#ifdef CONFIG_FILS
+int fils_domain_name_hash(const char *domain, u8 *hash)
+{
+ char buf[255], *wpos = buf;
+ const char *pos = domain;
+ size_t len;
+ const u8 *addr[1];
+ u8 mac[SHA256_MAC_LEN];
+
+ for (len = 0; len < sizeof(buf) && *pos; len++) {
+ if (isalpha(*pos) && isupper(*pos))
+ *wpos++ = tolower(*pos);
+ else
+ *wpos++ = *pos;
+ pos++;
+ }
+
+ addr[0] = (const u8 *) buf;
+ if (sha256_vector(1, addr, &len, mac) < 0)
+ return -1;
+ os_memcpy(hash, mac, 2);
+ return 0;
+}
+#endif /* CONFIG_FILS */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 1021ccb05a71..62617444058b 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -1,6 +1,6 @@
/*
* WPA definitions shared between hostapd and wpa_supplicant
- * Copyright (c) 2002-2015, 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.
@@ -13,13 +13,15 @@
#define PMKID_LEN 16
#define PMK_LEN 32
#define PMK_LEN_SUITE_B_192 48
-#define PMK_LEN_MAX 48
+#define PMK_LEN_MAX 64
#define WPA_REPLAY_COUNTER_LEN 8
#define WPA_NONCE_LEN 32
#define WPA_KEY_RSC_LEN 8
#define WPA_GMK_LEN 32
#define WPA_GTK_MAX_LEN 32
+#define OWE_DH_GROUP 19
+
#define WPA_ALLOWED_PAIRWISE_CIPHERS \
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
@@ -27,6 +29,9 @@ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
WPA_CIPHER_GTK_NOT_USED)
+#define WPA_ALLOWED_GROUP_MGMT_CIPHERS \
+(WPA_CIPHER_AES_128_CMAC | WPA_CIPHER_BIP_GMAC_128 | WPA_CIPHER_BIP_GMAC_256 | \
+WPA_CIPHER_BIP_CMAC_256)
#define WPA_SELECTOR_LEN 4
#define WPA_VERSION 1
@@ -48,10 +53,8 @@ WPA_CIPHER_GTK_NOT_USED)
#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
-#ifdef CONFIG_IEEE80211R
#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
-#endif /* CONFIG_IEEE80211R */
#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
@@ -59,17 +62,24 @@ WPA_CIPHER_GTK_NOT_USED)
#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
-#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
-RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+#define RSN_AUTH_KEY_MGMT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
+#define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15)
+#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
+#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
+#define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
+#define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02)
#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
+#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
#define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
#if 0
#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
#endif
#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
@@ -78,6 +88,12 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
#define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
#define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+#define RSN_CIPHER_SUITE_SMS4 RSN_SELECTOR(0x00, 0x14, 0x72, 1)
+#define RSN_CIPHER_SUITE_CKIP RSN_SELECTOR(0x00, 0x40, 0x96, 0)
+#define RSN_CIPHER_SUITE_CKIP_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 1)
+#define RSN_CIPHER_SUITE_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 2)
+/* KRK is defined for nl80211 use only */
+#define RSN_CIPHER_SUITE_KRK RSN_SELECTOR(0x00, 0x40, 0x96, 255)
/* EAPOL-Key Key Data Encapsulation
* GroupKey and PeerKey require encryption, otherwise, encryption is optional.
@@ -88,12 +104,6 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
#endif
#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
-#ifdef CONFIG_PEERKEY
-#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
-#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
-#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
-#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
-#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_IEEE80211W
#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
#endif /* CONFIG_IEEE80211W */
@@ -179,30 +189,17 @@ struct wpa_eapol_key {
u8 key_iv[16];
u8 key_rsc[WPA_KEY_RSC_LEN];
u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
- u8 key_mic[16];
- u8 key_data_length[2]; /* big endian */
- /* followed by key_data_length bytes of key_data */
-} STRUCT_PACKED;
-
-struct wpa_eapol_key_192 {
- u8 type;
- /* Note: key_info, key_length, and key_data_length are unaligned */
- u8 key_info[2]; /* big endian */
- u8 key_length[2]; /* big endian */
- u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
- u8 key_nonce[WPA_NONCE_LEN];
- u8 key_iv[16];
- u8 key_rsc[WPA_KEY_RSC_LEN];
- u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
- u8 key_mic[24];
- u8 key_data_length[2]; /* big endian */
- /* followed by key_data_length bytes of key_data */
+ /* variable length Key MIC field */
+ /* big endian 2-octet Key Data Length field */
+ /* followed by Key Data Length bytes of Key Data */
} STRUCT_PACKED;
-#define WPA_EAPOL_KEY_MIC_MAX_LEN 24
-#define WPA_KCK_MAX_LEN 24
-#define WPA_KEK_MAX_LEN 32
+#define WPA_EAPOL_KEY_MIC_MAX_LEN 32
+#define WPA_KCK_MAX_LEN 32
+#define WPA_KEK_MAX_LEN 64
#define WPA_TK_MAX_LEN 32
+#define FILS_ICK_MAX_LEN 48
+#define FILS_FT_MAX_LEN 48
/**
* struct wpa_ptk - WPA Pairwise Transient Key
@@ -212,9 +209,13 @@ struct wpa_ptk {
u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */
u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */
u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */
+ u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */
+ u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */
size_t kck_len;
size_t kek_len;
size_t tk_len;
+ size_t kck2_len;
+ size_t kek2_len;
int installed; /* 1 if key has already been installed to driver */
};
@@ -283,22 +284,6 @@ struct rsn_ie_hdr {
} STRUCT_PACKED;
-#ifdef CONFIG_PEERKEY
-enum {
- STK_MUI_4WAY_STA_AP = 1,
- STK_MUI_4WAY_STAT_STA = 2,
- STK_MUI_GTK = 3,
- STK_MUI_SMK = 4
-};
-
-enum {
- STK_ERR_STA_NR = 1,
- STK_ERR_STA_NRSN = 2,
- STK_ERR_CPHR_NS = 3,
- STK_ERR_NO_STSL = 4
-};
-#endif /* CONFIG_PEERKEY */
-
struct rsn_error_kde {
be16 mui;
be16 error_type;
@@ -329,6 +314,14 @@ struct rsn_ftie {
/* followed by optional parameters */
} STRUCT_PACKED;
+struct rsn_ftie_sha384 {
+ u8 mic_control[2];
+ u8 mic[24];
+ u8 anonce[WPA_NONCE_LEN];
+ u8 snonce[WPA_NONCE_LEN];
+ /* followed by optional parameters */
+} STRUCT_PACKED;
+
#define FTIE_SUBELEM_R1KH_ID 1
#define FTIE_SUBELEM_GTK 2
#define FTIE_SUBELEM_R0KH_ID 3
@@ -352,6 +345,22 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
const u8 *nonce1, const u8 *nonce2,
struct wpa_ptk *ptk, int akmp, int cipher);
+int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
+ const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
+ size_t dh_ss_len, u8 *pmk, size_t *pmk_len);
+int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
+ u8 *pmkid);
+int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
+ const u8 *snonce, const u8 *anonce, const u8 *dhss,
+ size_t dhss_len, struct wpa_ptk *ptk,
+ u8 *ick, size_t *ick_len, int akmp, int cipher,
+ u8 *fils_ft, size_t *fils_ft_len);
+int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
+ const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
+ const u8 *g_sta, size_t g_sta_len,
+ const u8 *g_ap, size_t g_ap_len,
+ int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
+ size_t *key_auth_len);
#ifdef CONFIG_IEEE80211R
int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
@@ -360,17 +369,19 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
const u8 *ftie, size_t ftie_len,
const u8 *rsnie, size_t rsnie_len,
const u8 *ric, size_t ric_len, u8 *mic);
-void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
- const u8 *ssid, size_t ssid_len,
- const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
- const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
-void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
- const u8 *s1kh_id, u8 *pmk_r1_name);
-void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
- const u8 *r1kh_id, const u8 *s1kh_id,
- u8 *pmk_r1, u8 *pmk_r1_name);
-int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
- const u8 *sta_addr, const u8 *bssid,
+int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
+ int use_sha384);
+int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+ const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384);
+int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
+ const u8 *pmk_r0_name,
+ const u8 *r1kh_id, const u8 *s1kh_id,
+ u8 *pmk_r1, u8 *pmk_r1_name);
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce,
+ const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
const u8 *pmk_r1_name,
struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher);
#endif /* CONFIG_IEEE80211R */
@@ -393,7 +404,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ie_data *data);
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
- u8 *pmkid, int use_sha256);
+ u8 *pmkid, int akmp);
#ifdef CONFIG_SUITEB
int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, u8 *pmkid);
@@ -442,13 +453,16 @@ struct wpa_ft_ies {
size_t igtk_len;
const u8 *ric;
size_t ric_len;
+ int key_mgmt;
+ int pairwise_cipher;
};
-int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
+ int use_sha384);
int wpa_cipher_key_len(int cipher);
int wpa_cipher_rsc_len(int cipher);
-int wpa_cipher_to_alg(int cipher);
+enum wpa_alg wpa_cipher_to_alg(int cipher);
int wpa_cipher_valid_group(int cipher);
int wpa_cipher_valid_pairwise(int cipher);
int wpa_cipher_valid_mgmt_group(int cipher);
@@ -460,6 +474,10 @@ int wpa_pick_group_cipher(int ciphers);
int wpa_parse_cipher(const char *value);
int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
-unsigned int wpa_mic_len(int akmp);
+unsigned int wpa_mic_len(int akmp, size_t pmk_len);
+int wpa_use_akm_defined(int akmp);
+int wpa_use_cmac(int akmp);
+int wpa_use_aes_key_wrap(int akmp);
+int fils_domain_name_hash(const char *domain, u8 *hash);
#endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 4dcba81dc1a4..f65077e04282 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -1,6 +1,6 @@
/*
* wpa_supplicant/hostapd control interface library
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -50,10 +50,19 @@ extern "C" {
#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
/** EAP status */
#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS "
+/** Retransmit the previous request packet */
+#define WPA_EVENT_EAP_RETRANSMIT "CTRL-EVENT-EAP-RETRANSMIT "
+#define WPA_EVENT_EAP_RETRANSMIT2 "CTRL-EVENT-EAP-RETRANSMIT2 "
/** EAP authentication completed successfully */
#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
+#define WPA_EVENT_EAP_SUCCESS2 "CTRL-EVENT-EAP-SUCCESS2 "
/** EAP authentication failed (EAP-Failure received) */
#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+#define WPA_EVENT_EAP_FAILURE2 "CTRL-EVENT-EAP-FAILURE2 "
+/** EAP authentication failed due to no response received */
+#define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE "
+#define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 "
+#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE "
/** Network block temporarily disabled (e.g., due to authentication failure) */
#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
/** Temporarily disabled network block re-enabled */
@@ -74,10 +83,15 @@ extern "C" {
#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
/** Change in the signal level was reported by the driver */
#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
+/** Beacon loss reported by the driver */
+#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS "
/** Regulatory domain channel */
#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
/** Channel switch (followed by freq=<MHz> and other channel parameters) */
#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
+/** SAE authentication failed due to unknown password identifier */
+#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
+ "CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
/** IP subnet status change notification
*
@@ -141,6 +155,33 @@ extern "C" {
#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
+/* DPP events */
+#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
+#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED "
+#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
+#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
+#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
+#define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION "
+#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
+#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
+#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
+#define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM "
+#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
+#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
+#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
+#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
+#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
+#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
+#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
+#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
+#define DPP_EVENT_RX "DPP-RX "
+#define DPP_EVENT_TX "DPP-TX "
+#define DPP_EVENT_TX_STATUS "DPP-TX-STATUS "
+#define DPP_EVENT_FAIL "DPP-FAIL "
+#define DPP_EVENT_PKEX_T_LIMIT "DPP-PKEX-T-LIMIT "
+#define DPP_EVENT_INTRO "DPP-INTRO "
+#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
+
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
@@ -232,9 +273,14 @@ extern "C" {
#define RX_HS20_ANQP "RX-HS20-ANQP "
#define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON "
#define RX_HS20_ICON "RX-HS20-ICON "
+#define RX_MBO_ANQP "RX-MBO-ANQP "
+
+/* parameters: <Venue Number> <Venue URL> */
+#define RX_VENUE_URL "RX-VENUE-URL "
#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
+#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE "
#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
@@ -258,6 +304,9 @@ extern "C" {
#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
+#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD "
+#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE "
+
#define AP_EVENT_ENABLED "AP-ENABLED "
#define AP_EVENT_DISABLED "AP-DISABLED "
@@ -273,6 +322,7 @@ extern "C" {
#define DFS_EVENT_CAC_START "DFS-CAC-START "
#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
#define AP_CSA_FINISHED "AP-CSA-FINISHED "
@@ -282,12 +332,46 @@ extern "C" {
/* BSS Transition Management Response frame received */
#define BSS_TM_RESP "BSS-TM-RESP "
+/* Collocated Interference Request frame received;
+ * parameters: <dialog token> <automatic report enabled> <report timeout> */
+#define COLOC_INTF_REQ "COLOC-INTF-REQ "
+/* Collocated Interference Report frame received;
+ * parameters: <STA address> <dialog token> <hexdump of report elements> */
+#define COLOC_INTF_REPORT "COLOC-INTF-REPORT "
+
/* MBO IE with cellular data connection preference received */
#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE "
/* BSS Transition Management Request received with MBO transition reason */
#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON "
+/* parameters: <STA address> <dialog token> <ack=0/1> */
+#define BEACON_REQ_TX_STATUS "BEACON-REQ-TX-STATUS "
+/* parameters: <STA address> <dialog token> <report mode> <beacon report> */
+#define BEACON_RESP_RX "BEACON-RESP-RX "
+
+/* PMKSA cache entry added; parameters: <BSSID> <network_id> */
+#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED "
+/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */
+#define PMKSA_CACHE_REMOVED "PMKSA-CACHE-REMOVED "
+
+/* FILS HLP Container receive; parameters: dst=<addr> src=<addr> frame=<hexdump>
+ */
+#define FILS_HLP_RX "FILS-HLP-RX "
+
+/* Event to indicate Probe Request frame;
+ * parameters: sa=<STA MAC address> signal=<signal> */
+#define RX_PROBE_REQUEST "RX-PROBE-REQUEST "
+
+/* Event to indicate station's HT/VHT operation mode change information */
+#define STA_OPMODE_MAX_BW_CHANGED "STA-OPMODE-MAX-BW-CHANGED "
+#define STA_OPMODE_SMPS_MODE_CHANGED "STA-OPMODE-SMPS-MODE-CHANGED "
+#define STA_OPMODE_N_SS_CHANGED "STA-OPMODE-N_SS-CHANGED "
+
+/* New interface addition or removal for 4addr WDS SDA */
+#define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED "
+#define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED "
+
/* BSS command information masks */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF
@@ -313,6 +397,9 @@ extern "C" {
#define WPA_BSS_MASK_SNR BIT(19)
#define WPA_BSS_MASK_EST_THROUGHPUT BIT(20)
#define WPA_BSS_MASK_FST BIT(21)
+#define WPA_BSS_MASK_UPDATE_IDX BIT(22)
+#define WPA_BSS_MASK_BEACON_IE BIT(23)
+#define WPA_BSS_MASK_FILS_INDICATION BIT(24)
/* VENDOR_ELEM_* frame id values */
@@ -386,7 +473,7 @@ void wpa_ctrl_close(struct wpa_ctrl *ctrl);
*
* This function is used to send commands to wpa_supplicant/hostapd. Received
* response will be written to reply and reply_len is set to the actual length
- * of the reply. This function will block for up to two seconds while waiting
+ * of the reply. This function will block for up to 10 seconds while waiting
* for the reply. If unsolicited messages are received, the blocking time may
* be longer.
*
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
index f1594213f97f..8e1c09ec5929 100644
--- a/src/common/wpa_helpers.c
+++ b/src/common/wpa_helpers.c
@@ -222,7 +222,8 @@ int wait_ip_addr(const char *ifname, int timeout)
if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0
&& strlen(ip) > 0) {
printf("IP address found: '%s'\n", ip);
- return 0;
+ if (strncmp(ip, "169.254.", 8) != 0)
+ return 0;
}
ctrl = wpa_open_ctrl(ifname);
if (ctrl == NULL)
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index d181e723134e..ee93e41fbbb1 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -14,6 +14,9 @@ CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
#CFLAGS += -DALL_DH_GROUPS
CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+CFLAGS += -DCONFIG_INTERNAL_SHA384
LIB_OBJS= \
aes-cbc.o \
@@ -48,6 +51,8 @@ LIB_OBJS= \
sha256-prf.o \
sha256-tlsprf.o \
sha256-internal.o \
+ sha384.o \
+ sha384-prf.o \
sha384-internal.o \
sha512-internal.o
diff --git a/src/crypto/aes-ctr.c b/src/crypto/aes-ctr.c
index d4d874daacd0..e27f3bbd0706 100644
--- a/src/crypto/aes-ctr.c
+++ b/src/crypto/aes-ctr.c
@@ -1,5 +1,5 @@
/*
- * AES-128 CTR
+ * AES-128/192/256 CTR
*
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
@@ -14,15 +14,16 @@
#include "aes_wrap.h"
/**
- * aes_128_ctr_encrypt - AES-128 CTR mode encryption
- * @key: Key for encryption (16 bytes)
+ * aes_ctr_encrypt - AES-128/192/256 CTR mode encryption
+ * @key: Key for encryption (key_len bytes)
+ * @key_len: Length of the key (16, 24, or 32 bytes)
* @nonce: Nonce for counter mode (16 bytes)
* @data: Data to encrypt in-place
* @data_len: Length of data in bytes
* Returns: 0 on success, -1 on failure
*/
-int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
- u8 *data, size_t data_len)
+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+ u8 *data, size_t data_len)
{
void *ctx;
size_t j, len, left = data_len;
@@ -30,7 +31,7 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
u8 *pos = data;
u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
- ctx = aes_encrypt_init(key, 16);
+ ctx = aes_encrypt_init(key, key_len);
if (ctx == NULL)
return -1;
os_memcpy(counter, nonce, AES_BLOCK_SIZE);
@@ -53,3 +54,18 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
aes_encrypt_deinit(ctx);
return 0;
}
+
+
+/**
+ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+ * @key: Key for encryption (key_len bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len)
+{
+ return aes_ctr_encrypt(key, 16, nonce, data, data_len);
+}
diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c
index 720c7036e4e7..748229594904 100644
--- a/src/crypto/aes-internal-dec.c
+++ b/src/crypto/aes-internal-dec.c
@@ -147,10 +147,12 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3]
PUTU32(pt + 12, s3);
}
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
u32 *rk = ctx;
rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain);
+ return 0;
}
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
index f3c61b8508f3..9fdb4f35cb9b 100644
--- a/src/crypto/aes-internal-enc.c
+++ b/src/crypto/aes-internal-enc.c
@@ -112,10 +112,11 @@ void * aes_encrypt_init(const u8 *key, size_t len)
}
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
u32 *rk = ctx;
rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt);
+ return 0;
}
diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c
index 5ac82c2e4b5c..b682f3ad565d 100644
--- a/src/crypto/aes-siv.c
+++ b/src/crypto/aes-siv.c
@@ -61,26 +61,33 @@ static void pad_block(u8 *pad, const u8 *addr, size_t len)
}
-static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
- size_t *len, u8 *mac)
+static int aes_s2v(const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], size_t *len, u8 *mac)
{
u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
u8 *buf = NULL;
int ret;
size_t i;
+ const u8 *data[1];
+ size_t data_len[1];
if (!num_elem) {
os_memcpy(tmp, zero, sizeof(zero));
tmp[AES_BLOCK_SIZE - 1] = 1;
- return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+ data[0] = tmp;
+ data_len[0] = sizeof(tmp);
+ return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
}
- ret = omac1_aes_128(key, zero, sizeof(zero), tmp);
+ data[0] = zero;
+ data_len[0] = sizeof(zero);
+ ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp);
if (ret)
return ret;
for (i = 0; i < num_elem - 1; i++) {
- ret = omac1_aes_128(key, addr[i], len[i], tmp2);
+ ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i],
+ tmp2);
if (ret)
return ret;
@@ -88,13 +95,13 @@ static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
xor(tmp, tmp2);
}
if (len[i] >= AES_BLOCK_SIZE) {
- buf = os_malloc(len[i]);
+ buf = os_memdup(addr[i], len[i]);
if (!buf)
return -ENOMEM;
- os_memcpy(buf, addr[i], len[i]);
xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
- ret = omac1_aes_128(key, buf, len[i], mac);
+ data[0] = buf;
+ ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac);
bin_clear_free(buf, len[i]);
return ret;
}
@@ -103,24 +110,32 @@ static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
pad_block(tmp2, addr[i], len[i]);
xor(tmp, tmp2);
- return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+ data[0] = tmp;
+ data_len[0] = sizeof(tmp);
+ return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
}
-int aes_siv_encrypt(const u8 *key, const u8 *pw,
- size_t pwlen, size_t num_elem,
- const u8 *addr[], const size_t *len, u8 *out)
+int aes_siv_encrypt(const u8 *key, size_t key_len,
+ const u8 *pw, size_t pwlen,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *out)
{
const u8 *_addr[6];
size_t _len[6];
- const u8 *k1 = key, *k2 = key + 16;
+ const u8 *k1, *k2;
u8 v[AES_BLOCK_SIZE];
size_t i;
u8 *iv, *crypt_pw;
- if (num_elem > ARRAY_SIZE(_addr) - 1)
+ if (num_elem > ARRAY_SIZE(_addr) - 1 ||
+ (key_len != 32 && key_len != 48 && key_len != 64))
return -1;
+ key_len /= 2;
+ k1 = key;
+ k2 = key + key_len;
+
for (i = 0; i < num_elem; i++) {
_addr[i] = addr[i];
_len[i] = len[i];
@@ -128,7 +143,7 @@ int aes_siv_encrypt(const u8 *key, const u8 *pw,
_addr[num_elem] = pw;
_len[num_elem] = pwlen;
- if (aes_s2v(k1, num_elem + 1, _addr, _len, v))
+ if (aes_s2v(k1, key_len, num_elem + 1, _addr, _len, v))
return -1;
iv = out;
@@ -140,26 +155,31 @@ int aes_siv_encrypt(const u8 *key, const u8 *pw,
/* zero out 63rd and 31st bits of ctr (from right) */
v[8] &= 0x7f;
v[12] &= 0x7f;
- return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen);
+ return aes_ctr_encrypt(k2, key_len, v, crypt_pw, pwlen);
}
-int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+int aes_siv_decrypt(const u8 *key, size_t key_len,
+ const u8 *iv_crypt, size_t iv_c_len,
size_t num_elem, const u8 *addr[], const size_t *len,
u8 *out)
{
const u8 *_addr[6];
size_t _len[6];
- const u8 *k1 = key, *k2 = key + 16;
+ const u8 *k1, *k2;
size_t crypt_len;
size_t i;
int ret;
u8 iv[AES_BLOCK_SIZE];
u8 check[AES_BLOCK_SIZE];
- if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1)
+ if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1 ||
+ (key_len != 32 && key_len != 48 && key_len != 64))
return -1;
crypt_len = iv_c_len - AES_BLOCK_SIZE;
+ key_len /= 2;
+ k1 = key;
+ k2 = key + key_len;
for (i = 0; i < num_elem; i++) {
_addr[i] = addr[i];
@@ -174,11 +194,11 @@ int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
iv[8] &= 0x7f;
iv[12] &= 0x7f;
- ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len);
+ ret = aes_ctr_encrypt(k2, key_len, iv, out, crypt_len);
if (ret)
return ret;
- ret = aes_s2v(k1, num_elem + 1, _addr, _len, check);
+ ret = aes_s2v(k1, key_len, num_elem + 1, _addr, _len, check);
if (ret)
return ret;
if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0)
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
index 2de59e04efa5..8ab3de2ee83f 100644
--- a/src/crypto/aes.h
+++ b/src/crypto/aes.h
@@ -12,10 +12,10 @@
#define AES_BLOCK_SIZE 16
void * aes_encrypt_init(const u8 *key, size_t len);
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
void aes_encrypt_deinit(void *ctx);
void * aes_decrypt_init(const u8 *key, size_t len);
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
void aes_decrypt_deinit(void *ctx);
#endif /* AES_H */
diff --git a/src/crypto/aes_siv.h b/src/crypto/aes_siv.h
index 463cf6536107..fb05d80c1f12 100644
--- a/src/crypto/aes_siv.h
+++ b/src/crypto/aes_siv.h
@@ -9,10 +9,12 @@
#ifndef AES_SIV_H
#define AES_SIV_H
-int aes_siv_encrypt(const u8 *key, const u8 *pw,
- size_t pwlen, size_t num_elem,
- const u8 *addr[], const size_t *len, u8 *out);
-int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+int aes_siv_encrypt(const u8 *key, size_t key_len,
+ const u8 *pw, size_t pwlen,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *out);
+int aes_siv_decrypt(const u8 *key, size_t key_len,
+ const u8 *iv_crypt, size_t iv_c_len,
size_t num_elem, const u8 *addr[], const size_t *len,
u8 *out);
diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h
index 4a142093b0d6..b70b1d26e550 100644
--- a/src/crypto/aes_wrap.h
+++ b/src/crypto/aes_wrap.h
@@ -3,7 +3,7 @@
*
* - AES Key Wrap Algorithm (RFC3394)
* - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
- * - AES-128 CTR mode encryption
+ * - AES-128/192/256 CTR mode encryption
* - AES-128 EAX mode encryption/decryption
* - AES-128 CBC
* - AES-GCM
@@ -33,6 +33,8 @@ int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
u8 *mac);
int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+ u8 *data, size_t data_len);
int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
u8 *data, size_t data_len);
int __must_check aes_128_eax_encrypt(const u8 *key,
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index bdc3ba6f37e0..507b7cab86fc 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1,6 +1,6 @@
/*
* Wrapper functions for crypto libraries
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -106,8 +106,9 @@ int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
* @clear: 8 octets (in)
* @key: 7 octets (in) (no parity bits included)
* @cypher: 8 octets (out)
+ * Returns: 0 on success, -1 on failure
*/
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
/**
* aes_encrypt_init - Initialize AES for encryption
@@ -122,8 +123,9 @@ void * aes_encrypt_init(const u8 *key, size_t len);
* @ctx: Context pointer from aes_encrypt_init()
* @plain: Plaintext data to be encrypted (16 bytes)
* @crypt: Buffer for the encrypted data (16 bytes)
+ * Returns: 0 on success, -1 on failure
*/
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
/**
* aes_encrypt_deinit - Deinitialize AES encryption
@@ -144,8 +146,9 @@ void * aes_decrypt_init(const u8 *key, size_t len);
* @ctx: Context pointer from aes_encrypt_init()
* @crypt: Encrypted data (16 bytes)
* @plain: Buffer for the decrypted data (16 bytes)
+ * Returns: 0 on success, -1 on failure
*/
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
/**
* aes_decrypt_deinit - Deinitialize AES decryption
@@ -414,6 +417,13 @@ int __must_check crypto_public_key_decrypt_pkcs1(
struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
u8 *plain, size_t *plain_len);
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey);
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len);
+
/**
* crypto_global_init - Initialize crypto wrapper
*
@@ -526,6 +536,14 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a,
u8 *buf, size_t buflen, size_t padlen);
/**
+ * crypto_bignum_rand - Create a random number in range of modulus
+ * @r: Bignum; set to a random value
+ * @m: Bignum; modulus
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m);
+
+/**
* crypto_bignum_add - c = a + b
* @a: Bignum
* @b: Bignum
@@ -607,6 +625,16 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a,
struct crypto_bignum *d);
/**
+ * crypto_bignum_rshift - r = a >> n
+ * @a: Bignum
+ * @n: Number of bits
+ * @r: Bignum; used to store the result of a >> n
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+ struct crypto_bignum *r);
+
+/**
* crypto_bignum_cmp - Compare two bignums
* @a: Bignum
* @b: Bignum
@@ -637,6 +665,13 @@ int crypto_bignum_is_zero(const struct crypto_bignum *a);
int crypto_bignum_is_one(const struct crypto_bignum *a);
/**
+ * crypto_bignum_is_odd - Is the given bignum odd
+ * @a: Bignum
+ * Returns: 1 if @a is odd or 0 if not
+ */
+int crypto_bignum_is_odd(const struct crypto_bignum *a);
+
+/**
* crypto_bignum_legendre - Compute the Legendre symbol (a/p)
* @a: Bignum
* @p: Bignum
@@ -668,6 +703,14 @@ struct crypto_ec * crypto_ec_init(int group);
void crypto_ec_deinit(struct crypto_ec *e);
/**
+ * crypto_ec_cofactor - Set the cofactor into the big number
+ * @e: EC context from crypto_ec_init()
+ * @cofactor: Cofactor of curve.
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor);
+
+/**
* crypto_ec_prime_len - Get length of the prime in octets
* @e: EC context from crypto_ec_init()
* Returns: Length of the prime defining the group
@@ -682,6 +725,13 @@ size_t crypto_ec_prime_len(struct crypto_ec *e);
size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
/**
+ * crypto_ec_order_len - Get length of the order in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the order defining the group
+ */
+size_t crypto_ec_order_len(struct crypto_ec *e);
+
+/**
* crypto_ec_get_prime - Get prime defining an EC group
* @e: EC context from crypto_ec_init()
* Returns: Prime (bignum) defining the group
@@ -718,6 +768,16 @@ struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
/**
+ * crypto_ec_point_x - Copies the x-ordinate point into big number
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data
+ * @x: Big number to set to the copy of x-ordinate
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+ struct crypto_bignum *x);
+
+/**
* crypto_ec_point_to_bin - Write EC point value as binary data
* @e: EC context from crypto_ec_init()
* @p: EC point data from crypto_ec_point_init()
@@ -746,7 +806,7 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
const u8 *val);
/**
- * crypto_bignum_add - c = a + b
+ * crypto_ec_point_add - c = a + b
* @e: EC context from crypto_ec_init()
* @a: Bignum
* @b: Bignum
@@ -758,7 +818,7 @@ int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
struct crypto_ec_point *c);
/**
- * crypto_bignum_mul - res = b * p
+ * crypto_ec_point_mul - res = b * p
* @e: EC context from crypto_ec_init()
* @p: EC point
* @b: Bignum
@@ -829,4 +889,12 @@ int crypto_ec_point_cmp(const struct crypto_ec *e,
const struct crypto_ec_point *a,
const struct crypto_ec_point *b);
+struct crypto_ecdh;
+
+struct crypto_ecdh * crypto_ecdh_init(int group);
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y);
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+ const u8 *key, size_t len);
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh);
+
#endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 0dfd54d22d47..7a797b5c359d 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / wrapper functions for libgcrypt
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,27 +10,42 @@
#include <gcrypt.h>
#include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
#include "crypto.h"
-int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+static int gnutls_digest_vector(int algo, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
{
gcry_md_hd_t hd;
unsigned char *p;
size_t i;
- if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR)
+ if (TEST_FAIL())
+ return -1;
+
+ if (gcry_md_open(&hd, algo, 0) != GPG_ERR_NO_ERROR)
return -1;
for (i = 0; i < num_elem; i++)
gcry_md_write(hd, addr[i], len[i]);
- p = gcry_md_read(hd, GCRY_MD_MD4);
+ p = gcry_md_read(hd, algo);
if (p)
- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4));
+ memcpy(mac, p, gcry_md_get_algo_dlen(algo));
gcry_md_close(hd);
return 0;
}
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_digest_vector(GCRY_MD_MD4, num_elem, addr, len, mac);
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
{
gcry_cipher_hd_t hd;
u8 pkey[8], next, tmp;
@@ -49,49 +64,161 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
gcry_err_code(gcry_cipher_setkey(hd, pkey, 8));
gcry_cipher_encrypt(hd, cypher, 8, clear, 8);
gcry_cipher_close(hd);
+ return 0;
}
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
- gcry_md_hd_t hd;
- unsigned char *p;
- size_t i;
-
- if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR)
- return -1;
- for (i = 0; i < num_elem; i++)
- gcry_md_write(hd, addr[i], len[i]);
- p = gcry_md_read(hd, GCRY_MD_MD5);
- if (p)
- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5));
- gcry_md_close(hd);
- return 0;
+ return gnutls_digest_vector(GCRY_MD_MD5, num_elem, addr, len, mac);
}
int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
+ return gnutls_digest_vector(GCRY_MD_SHA1, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_digest_vector(GCRY_MD_SHA256, num_elem, addr, len, mac);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_digest_vector(GCRY_MD_SHA384, num_elem, addr, len, mac);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_digest_vector(GCRY_MD_SHA512, num_elem, addr, len, mac);
+}
+
+
+static int gnutls_hmac_vector(int algo, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac)
+{
gcry_md_hd_t hd;
unsigned char *p;
size_t i;
- if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR)
+ if (TEST_FAIL())
+ return -1;
+
+ if (gcry_md_open(&hd, algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+ return -1;
+ if (gcry_md_setkey(hd, key, key_len) != GPG_ERR_NO_ERROR) {
+ gcry_md_close(hd);
return -1;
+ }
for (i = 0; i < num_elem; i++)
gcry_md_write(hd, addr[i], len[i]);
- p = gcry_md_read(hd, GCRY_MD_SHA1);
+ p = gcry_md_read(hd, algo);
if (p)
- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
+ memcpy(mac, p, gcry_md_get_algo_dlen(algo));
gcry_md_close(hd);
return 0;
}
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_MD5, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_SHA1, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_SHA256, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_SHA384, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_SHA512, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
void * aes_encrypt_init(const u8 *key, size_t len)
{
gcry_cipher_hd_t hd;
+ if (TEST_FAIL())
+ return NULL;
+
if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
GPG_ERR_NO_ERROR) {
printf("cipher open failed\n");
@@ -107,10 +234,11 @@ void * aes_encrypt_init(const u8 *key, size_t len)
}
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
gcry_cipher_hd_t hd = ctx;
gcry_cipher_encrypt(hd, crypt, 16, plain, 16);
+ return 0;
}
@@ -125,6 +253,9 @@ void * aes_decrypt_init(const u8 *key, size_t len)
{
gcry_cipher_hd_t hd;
+ if (TEST_FAIL())
+ return NULL;
+
if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
GPG_ERR_NO_ERROR)
return NULL;
@@ -137,10 +268,11 @@ void * aes_decrypt_init(const u8 *key, size_t len)
}
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
gcry_cipher_hd_t hd = ctx;
gcry_cipher_decrypt(hd, plain, 16, crypt, 16);
+ return 0;
}
@@ -151,6 +283,42 @@ void aes_decrypt_deinit(void *ctx)
}
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c
index 9dcabb95bdd2..92581ac676d3 100644
--- a/src/crypto/crypto_internal-modexp.c
+++ b/src/crypto/crypto_internal-modexp.c
@@ -13,6 +13,42 @@
#include "crypto.h"
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index a55edd14e2d3..259f99500bcd 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -35,7 +35,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
}
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
{
u8 pkey[8], next, tmp;
int i;
@@ -53,6 +53,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
des_setup(pkey, 8, 0, &skey);
des_ecb_encrypt(clear, cypher, &skey);
des_done(&skey);
+ return 0;
}
@@ -96,10 +97,10 @@ void * aes_encrypt_init(const u8 *key, size_t len)
}
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
symmetric_key *skey = ctx;
- aes_ecb_encrypt(plain, crypt, skey);
+ return aes_ecb_encrypt(plain, crypt, skey) == CRYPT_OK ? 0 : -1;
}
@@ -125,10 +126,10 @@ void * aes_decrypt_init(const u8 *key, size_t len)
}
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
symmetric_key *skey = ctx;
- aes_ecb_encrypt(plain, (u8 *) crypt, skey);
+ return aes_ecb_encrypt(plain, (u8 *) crypt, skey) == CRYPT_OK ? 0 : -1;
}
@@ -297,7 +298,7 @@ struct crypto_cipher {
struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
const u8 *iv, const u8 *key,
size_t key_len)
-{
+{
struct crypto_cipher *ctx;
int idx, res, rc4 = 0;
@@ -693,6 +694,42 @@ void crypto_global_deinit(void)
#ifdef CONFIG_MODEXP
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c
new file mode 100644
index 000000000000..8099193bf068
--- /dev/null
+++ b/src/crypto/crypto_linux.c
@@ -0,0 +1,1006 @@
+/*
+ * Crypto wrapper for Linux kernel AF_ALG
+ * Copyright (c) 2017, 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 <linux/if_alg.h>
+
+#include "common.h"
+#include "crypto.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "aes.h"
+
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif /* SOL_ALG */
+
+
+static int linux_af_alg_socket(const char *type, const char *name)
+{
+ struct sockaddr_alg sa;
+ int s;
+
+ if (TEST_FAIL())
+ return -1;
+
+ s = socket(AF_ALG, SOCK_SEQPACKET, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&sa, 0, sizeof(sa));
+ sa.salg_family = AF_ALG;
+ os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type));
+ os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type));
+ if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: Failed to bind AF_ALG socket(%s,%s): %s",
+ __func__, type, name, strerror(errno));
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+
+static int linux_af_alg_hash_vector(const char *alg, const u8 *key,
+ size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len,
+ u8 *mac, size_t mac_len)
+{
+ int s, t;
+ size_t i;
+ ssize_t res;
+ int ret = -1;
+
+ s = linux_af_alg_socket("hash", alg);
+ if (s < 0)
+ return -1;
+
+ if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+ wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+ __func__, strerror(errno));
+ close(s);
+ return -1;
+ }
+
+ t = accept(s, NULL, NULL);
+ if (t < 0) {
+ wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+ __func__, strerror(errno));
+ close(s);
+ return -1;
+ }
+
+ for (i = 0; i < num_elem; i++) {
+ res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: send on AF_ALG socket failed: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if ((size_t) res < len[i]) {
+ wpa_printf(MSG_ERROR,
+ "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+ __func__, (int) res, (int) len[i]);
+ goto fail;
+ }
+ }
+
+ res = recv(t, mac, mac_len, 0);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: recv on AF_ALG socket failed: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if ((size_t) res < mac_len) {
+ wpa_printf(MSG_ERROR,
+ "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+ __func__, (int) res, (int) mac_len);
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ close(t);
+ close(s);
+
+ return ret;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len,
+ mac, 16);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len,
+ mac, MD5_MAC_LEN);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len,
+ mac, SHA1_MAC_LEN);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len,
+ mac, SHA256_MAC_LEN);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len,
+ mac, SHA384_MAC_LEN);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len,
+ mac, 64);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem,
+ addr, len, mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem,
+ addr, len, mac, SHA1_MAC_LEN);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem,
+ addr, len, mac, SHA256_MAC_LEN);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem,
+ addr, len, mac, SHA384_MAC_LEN);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_hash {
+ int s;
+ int t;
+ size_t mac_len;
+ int failed;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_hash *ctx;
+ const char *name;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ switch (alg) {
+ case CRYPTO_HASH_ALG_MD5:
+ name = "md5";
+ ctx->mac_len = MD5_MAC_LEN;
+ break;
+ case CRYPTO_HASH_ALG_SHA1:
+ name = "sha1";
+ ctx->mac_len = SHA1_MAC_LEN;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ name = "hmac(md5)";
+ ctx->mac_len = MD5_MAC_LEN;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ name = "hmac(sha1)";
+ ctx->mac_len = SHA1_MAC_LEN;
+ break;
+ case CRYPTO_HASH_ALG_SHA256:
+ name = "sha256";
+ ctx->mac_len = SHA256_MAC_LEN;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_SHA256:
+ name = "hmac(sha256)";
+ ctx->mac_len = SHA256_MAC_LEN;
+ break;
+ case CRYPTO_HASH_ALG_SHA384:
+ name = "sha384";
+ ctx->mac_len = SHA384_MAC_LEN;
+ break;
+ case CRYPTO_HASH_ALG_SHA512:
+ name = "sha512";
+ ctx->mac_len = 64;
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ ctx->s = linux_af_alg_socket("hash", name);
+ if (ctx->s < 0) {
+ os_free(ctx);
+ return NULL;
+ }
+
+ if (key && key_len &&
+ setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+ wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+ __func__, strerror(errno));
+ close(ctx->s);
+ os_free(ctx);
+ return NULL;
+ }
+
+ ctx->t = accept(ctx->s, NULL, NULL);
+ if (ctx->t < 0) {
+ wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+ __func__, strerror(errno));
+ close(ctx->s);
+ os_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+ ssize_t res;
+
+ if (!ctx)
+ return;
+
+ res = send(ctx->t, data, len, MSG_MORE);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: send on AF_ALG socket failed: %s",
+ __func__, strerror(errno));
+ ctx->failed = 1;
+ return;
+ }
+ if ((size_t) res < len) {
+ wpa_printf(MSG_ERROR,
+ "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+ __func__, (int) res, (int) len);
+ ctx->failed = 1;
+ return;
+ }
+}
+
+
+static void crypto_hash_deinit(struct crypto_hash *ctx)
+{
+ close(ctx->s);
+ close(ctx->t);
+ os_free(ctx);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+ ssize_t res;
+
+ if (!ctx)
+ return -2;
+
+ if (!mac || !len) {
+ crypto_hash_deinit(ctx);
+ return 0;
+ }
+
+ if (ctx->failed) {
+ crypto_hash_deinit(ctx);
+ return -2;
+ }
+
+ if (*len < ctx->mac_len) {
+ crypto_hash_deinit(ctx);
+ *len = ctx->mac_len;
+ return -1;
+ }
+ *len = ctx->mac_len;
+
+ res = recv(ctx->t, mac, ctx->mac_len, 0);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: recv on AF_ALG socket failed: %s",
+ __func__, strerror(errno));
+ crypto_hash_deinit(ctx);
+ return -2;
+ }
+ if ((size_t) res < ctx->mac_len) {
+ wpa_printf(MSG_ERROR,
+ "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+ __func__, (int) res, (int) ctx->mac_len);
+ crypto_hash_deinit(ctx);
+ return -2;
+ }
+
+ crypto_hash_deinit(ctx);
+ return 0;
+}
+
+
+struct linux_af_alg_skcipher {
+ int s;
+ int t;
+};
+
+
+static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher)
+{
+ if (!skcipher)
+ return;
+ if (skcipher->s >= 0)
+ close(skcipher->s);
+ if (skcipher->t >= 0)
+ close(skcipher->t);
+ os_free(skcipher);
+}
+
+
+static struct linux_af_alg_skcipher *
+linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len)
+{
+ struct linux_af_alg_skcipher *skcipher;
+
+ skcipher = os_zalloc(sizeof(*skcipher));
+ if (!skcipher)
+ goto fail;
+ skcipher->t = -1;
+
+ skcipher->s = linux_af_alg_socket("skcipher", alg);
+ if (skcipher->s < 0)
+ goto fail;
+
+ if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+ wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ skcipher->t = accept(skcipher->s, NULL, NULL);
+ if (skcipher->t < 0) {
+ wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ return skcipher;
+fail:
+ linux_af_alg_skcipher_deinit(skcipher);
+ return NULL;
+}
+
+
+static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher,
+ int enc, const u8 *in, u8 *out)
+{
+ char buf[CMSG_SPACE(sizeof(u32))];
+ struct iovec io[1];
+ struct msghdr msg;
+ struct cmsghdr *hdr;
+ ssize_t ret;
+ u32 *op;
+
+ io[0].iov_base = (void *) in;
+ io[0].iov_len = AES_BLOCK_SIZE;
+ os_memset(&msg, 0, sizeof(msg));
+ os_memset(buf, 0, sizeof(buf));
+ msg.msg_control = buf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 1;
+ hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_OP;
+ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+ op = (u32 *) CMSG_DATA(hdr);
+ *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+ ret = sendmsg(skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ ret = read(skcipher->t, out, AES_BLOCK_SIZE);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: read failed: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+ if (ret < AES_BLOCK_SIZE) {
+ wpa_printf(MSG_ERROR,
+ "%s: read did not return full data (%d/%d)",
+ __func__, (int) ret, AES_BLOCK_SIZE);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ struct linux_af_alg_skcipher *skcipher = ctx;
+
+ return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ struct linux_af_alg_skcipher *skcipher = ctx;
+
+ return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len)
+{
+ struct linux_af_alg_skcipher *skcipher;
+ u8 *skip_buf;
+ char buf[CMSG_SPACE(sizeof(u32))];
+ struct iovec io[2];
+ struct msghdr msg;
+ struct cmsghdr *hdr;
+ ssize_t ret;
+ u32 *op;
+
+ skip_buf = os_zalloc(skip + 1);
+ if (!skip_buf)
+ return -1;
+ skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen);
+ if (!skcipher) {
+ os_free(skip_buf);
+ return -1;
+ }
+
+ io[0].iov_base = skip_buf;
+ io[0].iov_len = skip;
+ io[1].iov_base = data;
+ io[1].iov_len = data_len;
+ os_memset(&msg, 0, sizeof(msg));
+ os_memset(buf, 0, sizeof(buf));
+ msg.msg_control = buf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 2;
+ hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_OP;
+ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+ op = (u32 *) CMSG_DATA(hdr);
+ *op = ALG_OP_ENCRYPT;
+
+ ret = sendmsg(skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+ __func__, strerror(errno));
+ os_free(skip_buf);
+ linux_af_alg_skcipher_deinit(skcipher);
+ return -1;
+ }
+ os_free(skip_buf);
+
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ ret = recvmsg(skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+ __func__, strerror(errno));
+ linux_af_alg_skcipher_deinit(skcipher);
+ return -1;
+ }
+ linux_af_alg_skcipher_deinit(skcipher);
+
+ if ((size_t) ret < skip + data_len) {
+ wpa_printf(MSG_ERROR,
+ "%s: recvmsg did not return full data (%d/%d)",
+ __func__, (int) ret, (int) (skip + data_len));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ u8 pkey[8], next, tmp;
+ int i;
+ struct linux_af_alg_skcipher *skcipher;
+ char buf[CMSG_SPACE(sizeof(u32))];
+ struct iovec io[1];
+ struct msghdr msg;
+ struct cmsghdr *hdr;
+ ssize_t ret;
+ u32 *op;
+ int res = -1;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey));
+ if (!skcipher)
+ goto fail;
+
+ io[0].iov_base = (void *) clear;
+ io[0].iov_len = 8;
+ os_memset(&msg, 0, sizeof(msg));
+ os_memset(buf, 0, sizeof(buf));
+ msg.msg_control = buf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 1;
+ hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_OP;
+ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+ op = (u32 *) CMSG_DATA(hdr);
+ *op = ALG_OP_ENCRYPT;
+
+ ret = sendmsg(skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ ret = read(skcipher->t, cypher, 8);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: read failed: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (ret < 8) {
+ wpa_printf(MSG_ERROR,
+ "%s: read did not return full data (%d/8)",
+ __func__, (int) ret);
+ goto fail;
+ }
+
+ res = 0;
+fail:
+ linux_af_alg_skcipher_deinit(skcipher);
+ return res;
+}
+
+
+static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv,
+ u8 *data, size_t data_len)
+{
+ struct linux_af_alg_skcipher *skcipher;
+ char buf[100];
+ struct iovec io[1];
+ struct msghdr msg;
+ struct cmsghdr *hdr;
+ ssize_t ret;
+ u32 *op;
+ struct af_alg_iv *alg_iv;
+ size_t iv_len = AES_BLOCK_SIZE;
+
+ skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16);
+ if (!skcipher)
+ return -1;
+
+ io[0].iov_base = (void *) data;
+ io[0].iov_len = data_len;
+ os_memset(&msg, 0, sizeof(msg));
+ os_memset(buf, 0, sizeof(buf));
+ msg.msg_control = buf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+ CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+ msg.msg_iov = io;
+ msg.msg_iovlen = 1;
+
+ hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_OP;
+ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+ op = (u32 *) CMSG_DATA(hdr);
+ *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+ hdr = CMSG_NXTHDR(&msg, hdr);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_IV;
+ hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+ alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+ alg_iv->ivlen = iv_len;
+ os_memcpy(alg_iv->iv, iv, iv_len);
+
+ ret = sendmsg(skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+ __func__, strerror(errno));
+ linux_af_alg_skcipher_deinit(skcipher);
+ return -1;
+ }
+
+ ret = recvmsg(skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+ __func__, strerror(errno));
+ linux_af_alg_skcipher_deinit(skcipher);
+ return -1;
+ }
+ if ((size_t) ret < data_len) {
+ wpa_printf(MSG_ERROR,
+ "%s: recvmsg not return full data (%d/%d)",
+ __func__, (int) ret, (int) data_len);
+ linux_af_alg_skcipher_deinit(skcipher);
+ return -1;
+ }
+
+ linux_af_alg_skcipher_deinit(skcipher);
+ return 0;
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ return aes_128_cbc_oper(key, 1, iv, data, data_len);
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ return aes_128_cbc_oper(key, 0, iv, data, data_len);
+}
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem,
+ addr, len, mac, AES_BLOCK_SIZE);
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+ u8 *plain)
+{
+ struct linux_af_alg_skcipher *skcipher;
+ char buf[100];
+ struct iovec io[1];
+ struct msghdr msg;
+ struct cmsghdr *hdr;
+ ssize_t ret;
+ u32 *op;
+ struct af_alg_iv *alg_iv;
+ size_t iv_len = 8;
+
+ skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len);
+ if (!skcipher)
+ return -1;
+
+ io[0].iov_base = (void *) (cipher + iv_len);
+ io[0].iov_len = n * 8;
+ os_memset(&msg, 0, sizeof(msg));
+ os_memset(buf, 0, sizeof(buf));
+ msg.msg_control = buf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+ CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+ msg.msg_iov = io;
+ msg.msg_iovlen = 1;
+
+ hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_OP;
+ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+ op = (u32 *) CMSG_DATA(hdr);
+ *op = ALG_OP_DECRYPT;
+
+ hdr = CMSG_NXTHDR(&msg, hdr);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_IV;
+ hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+ alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+ alg_iv->ivlen = iv_len;
+ os_memcpy(alg_iv->iv, cipher, iv_len);
+
+ ret = sendmsg(skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ ret = read(skcipher->t, plain, n * 8);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: read failed: %s",
+ __func__, strerror(errno));
+ linux_af_alg_skcipher_deinit(skcipher);
+ return -1;
+ }
+ if (ret < n * 8) {
+ wpa_printf(MSG_ERROR,
+ "%s: read not return full data (%d/%d)",
+ __func__, (int) ret, n * 8);
+ linux_af_alg_skcipher_deinit(skcipher);
+ return -1;
+ }
+
+ linux_af_alg_skcipher_deinit(skcipher);
+ return 0;
+}
+
+
+struct crypto_cipher {
+ struct linux_af_alg_skcipher *skcipher;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+ const char *name;
+ struct af_alg_iv *alg_iv;
+ size_t iv_len = 0;
+ char buf[100];
+ struct msghdr msg;
+ struct cmsghdr *hdr;
+ ssize_t ret;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ switch (alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ name = "ecb(arc4)";
+ break;
+ case CRYPTO_CIPHER_ALG_AES:
+ name = "cbc(aes)";
+ iv_len = AES_BLOCK_SIZE;
+ break;
+ case CRYPTO_CIPHER_ALG_3DES:
+ name = "cbc(des3_ede)";
+ iv_len = 8;
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ name = "cbc(des)";
+ iv_len = 8;
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ ctx->skcipher = linux_af_alg_skcipher(name, key, key_len);
+ if (!ctx->skcipher) {
+ os_free(ctx);
+ return NULL;
+ }
+
+ if (iv && iv_len) {
+ os_memset(&msg, 0, sizeof(msg));
+ os_memset(buf, 0, sizeof(buf));
+ msg.msg_control = buf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+ hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_IV;
+ hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+ alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+ alg_iv->ivlen = iv_len;
+ os_memcpy(alg_iv->iv, iv, iv_len);
+
+ ret = sendmsg(ctx->skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+ __func__, strerror(errno));
+ linux_af_alg_skcipher_deinit(ctx->skcipher);
+ os_free(ctx);
+ return NULL;
+ }
+ }
+
+ return ctx;
+}
+
+
+static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in,
+ u8 *out, size_t len)
+{
+ char buf[CMSG_SPACE(sizeof(u32))];
+ struct iovec io[1];
+ struct msghdr msg;
+ struct cmsghdr *hdr;
+ ssize_t ret;
+ u32 *op;
+
+ io[0].iov_base = (void *) in;
+ io[0].iov_len = len;
+ os_memset(&msg, 0, sizeof(msg));
+ os_memset(buf, 0, sizeof(buf));
+ msg.msg_control = buf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 1;
+ hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_ALG;
+ hdr->cmsg_type = ALG_SET_OP;
+ hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+ op = (u32 *) CMSG_DATA(hdr);
+ *op = type;
+
+ ret = sendmsg(ctx->skcipher->t, &msg, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ ret = read(ctx->skcipher->t, out, len);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: read failed: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+ if (ret < (ssize_t) len) {
+ wpa_printf(MSG_ERROR,
+ "%s: read did not return full data (%d/%d)",
+ __func__, (int) ret, (int) len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len);
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len);
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ if (ctx) {
+ linux_af_alg_skcipher_deinit(ctx->skcipher);
+ os_free(ctx);
+ }
+}
+
+
+int crypto_global_init(void)
+{
+ return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index ffd23942e32d..1cc73d8ec16e 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -17,6 +17,7 @@
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/sha384.h"
static int test_siv(void)
@@ -92,7 +93,7 @@ static int test_siv(void)
addr[0] = ad;
len[0] = sizeof(ad);
- if (aes_siv_encrypt(key, plaintext, sizeof(plaintext),
+ if (aes_siv_encrypt(key, sizeof(key), plaintext, sizeof(plaintext),
1, addr, len, out)) {
wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
return 1;
@@ -103,7 +104,8 @@ static int test_siv(void)
return 1;
}
- if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) {
+ if (aes_siv_decrypt(key, sizeof(key), iv_c, sizeof(iv_c),
+ 1, addr, len, out)) {
wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
return 1;
}
@@ -121,7 +123,8 @@ static int test_siv(void)
addr[2] = nonce_2;
len[2] = sizeof(nonce_2);
- if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2),
+ if (aes_siv_encrypt(key_2, sizeof(key_2),
+ plaintext_2, sizeof(plaintext_2),
3, addr, len, out)) {
wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
return 1;
@@ -132,7 +135,8 @@ static int test_siv(void)
return 1;
}
- if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) {
+ if (aes_siv_decrypt(key_2, sizeof(key_2), iv_c_2, sizeof(iv_c_2),
+ 3, addr, len, out)) {
wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
return 1;
}
@@ -1292,13 +1296,14 @@ static const struct {
};
static const struct hmac_test {
- u8 key[80];
+ u8 key[150];
size_t key_len;
- u8 data[128];
+ u8 data[160];
size_t data_len;
- u8 hash[32];
+ u8 hash[32]; /* HMAC-SHA-256 */
+ u8 hash384[48]; /* HMAC-SHA-384 */
} hmac_tests[] = {
- /* draft-ietf-ipsec-ciph-sha-256-01.txt */
+ /* draft-ietf-ipsec-ciph-sha-256-01.txt; RFC 4231 */
{
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
@@ -1313,7 +1318,8 @@ static const struct hmac_test {
0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a,
0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66,
0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81
- }
+ },
+ { }
},
{
{
@@ -1330,7 +1336,8 @@ static const struct hmac_test {
0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae,
0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49,
0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30
- }
+ },
+ { }
},
{
{
@@ -1348,7 +1355,8 @@ static const struct hmac_test {
0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab,
0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5,
0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3
- }
+ },
+ { }
},
{
{
@@ -1365,9 +1373,34 @@ static const struct hmac_test {
0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5,
0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c,
0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7
+ },
+ { }
+ },
+ { /* RFC 4231 - Test Case 1 */
+ {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b
+ },
+ 20,
+ "Hi There",
+ 8,
+ {
+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53,
+ 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b,
+ 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7,
+ 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7
+ },
+ {
+ 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62,
+ 0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f,
+ 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6,
+ 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c,
+ 0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f,
+ 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6
}
},
- {
+ { /* RFC 4231 - Test Case 2 */
"Jefe",
4,
"what do ya want for nothing?",
@@ -1377,6 +1410,14 @@ static const struct hmac_test {
0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
+ },
+ {
+ 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31,
+ 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b,
+ 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47,
+ 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e,
+ 0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7,
+ 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49
}
},
{
@@ -1402,6 +1443,39 @@ static const struct hmac_test {
0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62,
0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc,
0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0
+ },
+ { }
+ },
+ { /* RFC 4231 - Test Case 3 */
+ {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa
+ },
+ 20,
+ {
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd
+ },
+ 50,
+ {
+ 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46,
+ 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7,
+ 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22,
+ 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe
+ },
+ {
+ 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a,
+ 0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f,
+ 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+ 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b,
+ 0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9,
+ 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27
}
},
{
@@ -1428,6 +1502,40 @@ static const struct hmac_test {
0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55,
0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85,
0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17
+ },
+ { }
+ },
+ { /* RFC 4231 - Test Case 4 */
+ {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19,
+ },
+ 25,
+ {
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd
+ },
+ 50,
+ {
+ 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e,
+ 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a,
+ 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07,
+ 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b
+ },
+ {
+ 0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85,
+ 0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7,
+ 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c,
+ 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e,
+ 0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79,
+ 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb
}
},
{
@@ -1445,7 +1553,8 @@ static const struct hmac_test {
0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17,
0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27,
0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42
- }
+ },
+ { }
},
{
{
@@ -1468,6 +1577,45 @@ static const struct hmac_test {
0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb,
0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e,
0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f
+ },
+ { }
+ },
+ { /* RFC 4231 - Test Case 6 */
+ {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa
+ },
+ 131,
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ 54,
+ {
+ 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f,
+ 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f,
+ 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14,
+ 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54
+ },
+ {
+ 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90,
+ 0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4,
+ 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f,
+ 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6,
+ 0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82,
+ 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52
}
},
{
@@ -1492,6 +1640,45 @@ static const struct hmac_test {
0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8,
0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc,
0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6
+ },
+ { }
+ },
+ { /* RFC 4231 - Test Case 7 */
+ {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa
+ },
+ 131,
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+ 152,
+ {
+ 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb,
+ 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44,
+ 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93,
+ 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2
+ },
+ {
+ 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d,
+ 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c,
+ 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a,
+ 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5,
+ 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d,
+ 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e
}
}
};
@@ -1606,6 +1793,96 @@ static int test_sha256(void)
}
+static int test_sha384(void)
+{
+#ifdef CONFIG_SHA384
+ unsigned int i;
+ u8 hash[48];
+ const u8 *addr[2];
+ size_t len[2];
+ int errors = 0;
+ const char *data = "hello";
+ const u8 hash_res[] = {
+ 0x59, 0xe1, 0x74, 0x87, 0x77, 0x44, 0x8c, 0x69,
+ 0xde, 0x6b, 0x80, 0x0d, 0x7a, 0x33, 0xbb, 0xfb,
+ 0x9f, 0xf1, 0xb4, 0x63, 0xe4, 0x43, 0x54, 0xc3,
+ 0x55, 0x3b, 0xcd, 0xb9, 0xc6, 0x66, 0xfa, 0x90,
+ 0x12, 0x5a, 0x3c, 0x79, 0xf9, 0x03, 0x97, 0xbd,
+ 0xf5, 0xf6, 0xa1, 0x3d, 0xe8, 0x28, 0x68, 0x4f
+ };
+
+ addr[0] = (const u8 *) data;
+ len[0] = 5;
+ if (sha384_vector(1, addr, len, hash) < 0 ||
+ os_memcmp(hash, hash_res, 48) != 0) {
+ wpa_printf(MSG_INFO, "SHA384 test case 1: FAIL");
+ errors++;
+ } else {
+ wpa_printf(MSG_INFO, "SHA384 test case 1: OK");
+ }
+
+ addr[0] = (const u8 *) data;
+ len[0] = 4;
+ addr[1] = (const u8 *) data + 4;
+ len[1] = 1;
+ if (sha384_vector(2, addr, len, hash) < 0 ||
+ os_memcmp(hash, hash_res, 48) != 0) {
+ wpa_printf(MSG_INFO, "SHA384 test case 2: FAIL");
+ errors++;
+ } else {
+ wpa_printf(MSG_INFO, "SHA384 test case 2: OK");
+ }
+
+ for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
+ const struct hmac_test *t = &hmac_tests[i];
+
+ if (t->hash384[0] == 0 && t->hash384[1] == 0 &&
+ t->hash384[2] == 0 && t->hash384[3] == 0)
+ continue;
+ wpa_printf(MSG_INFO, "HMAC-SHA384 test case %d:", i + 1);
+
+ if (hmac_sha384(t->key, t->key_len, t->data, t->data_len,
+ hash) < 0 ||
+ os_memcmp(hash, t->hash384, 48) != 0) {
+ wpa_printf(MSG_INFO, " FAIL");
+ errors++;
+ } else
+ wpa_printf(MSG_INFO, " OK");
+
+ addr[0] = t->data;
+ len[0] = t->data_len;
+ if (hmac_sha384_vector(t->key, t->key_len, 1, addr, len,
+ hash) < 0 ||
+ os_memcmp(hash, t->hash384, 48) != 0) {
+ wpa_printf(MSG_INFO, " FAIL");
+ errors++;
+ } else
+ wpa_printf(MSG_INFO, " OK");
+
+ if (len[0]) {
+ addr[0] = t->data;
+ len[0] = 1;
+ addr[1] = t->data + 1;
+ len[1] = t->data_len - 1;
+ if (hmac_sha384_vector(t->key, t->key_len, 2, addr, len,
+ hash) < 0 ||
+ os_memcmp(hash, t->hash384, 48) != 0) {
+ wpa_printf(MSG_INFO, " FAIL");
+ errors++;
+ } else
+ wpa_printf(MSG_INFO, " OK");
+ }
+ }
+
+ if (!errors)
+ wpa_printf(MSG_INFO, "SHA384 test cases passed");
+ return errors;
+#else /* CONFIG_SHA384 */
+ return 0;
+#endif /* CONFIG_SHA384 */
+}
+
+
static int test_fips186_2_prf(void)
{
/* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
@@ -1635,6 +1912,135 @@ static int test_fips186_2_prf(void)
}
+static int test_extract_expand_hkdf(void)
+{
+ u8 prk[SHA256_MAC_LEN];
+ u8 okm[82];
+
+ /* RFC 5869, A.1 */
+ u8 ikm1[22] = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+ };
+ u8 salt1[13] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c
+ };
+ u8 info1[10] = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9
+ };
+ u8 prk1[32] = {
+ 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
+ 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
+ 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
+ 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5
+ };
+ u8 okm1[42] = {
+ 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
+ 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
+ 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
+ 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
+ 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
+ 0x58, 0x65
+ };
+
+ /* RFC 5869, A.2 */
+ u8 ikm2[80] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+ };
+ u8 salt2[80] = {
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
+ };
+ u8 info2[80] = {
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+ u8 prk2[32] = {
+ 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a,
+ 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c,
+ 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01,
+ 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44
+ };
+ u8 okm2[82] = {
+ 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1,
+ 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34,
+ 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8,
+ 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c,
+ 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72,
+ 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09,
+ 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
+ 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71,
+ 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87,
+ 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f,
+ 0x1d, 0x87
+ };
+
+ wpa_printf(MSG_INFO, "Testing Extract-and-Expand HKDF (RFC 5869)");
+
+ wpa_printf(MSG_INFO, "RFC 5869 - Test Case 1");
+ if (hmac_sha256(salt1, sizeof(salt1), ikm1, sizeof(ikm1), prk) < 0)
+ return -1;
+ if (os_memcmp(prk, prk1, SHA256_MAC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
+ return -1;
+ }
+ if (hmac_sha256_kdf(prk1, sizeof(prk1), NULL, info1, sizeof(info1),
+ okm, sizeof(okm1)) < 0)
+ return -1;
+ if (os_memcmp(okm, okm1, sizeof(okm1)) != 0) {
+ wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "RFC 5869 - Test Case 2");
+ if (hmac_sha256(salt2, sizeof(salt2), ikm2, sizeof(ikm2), prk) < 0)
+ return -1;
+ if (os_memcmp(prk, prk2, SHA256_MAC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
+ return -1;
+ }
+ if (hmac_sha256_kdf(prk2, sizeof(prk2), NULL, info2, sizeof(info2),
+ okm, sizeof(okm2)) < 0)
+ return -1;
+ if (os_memcmp(okm, okm2, sizeof(okm2)) != 0) {
+ wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "Extract-and-Expand HKDF test cases passed");
+
+ return 0;
+}
+
+
static int test_ms_funcs(void)
{
#ifndef CONFIG_FIPS
@@ -1751,7 +2157,9 @@ int crypto_module_tests(void)
test_md5() ||
test_sha1() ||
test_sha256() ||
+ test_sha384() ||
test_fips186_2_prf() ||
+ test_extract_expand_hkdf() ||
test_ms_funcs())
ret = -1;
diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c
new file mode 100644
index 000000000000..4e31bc801132
--- /dev/null
+++ b/src/crypto/crypto_nettle.c
@@ -0,0 +1,437 @@
+/*
+ * Wrapper functions for libnettle and libgmp
+ * Copyright (c) 2017, 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 <nettle/nettle-meta.h>
+#include <nettle/des.h>
+#undef des_encrypt
+#include <nettle/hmac.h>
+#include <nettle/aes.h>
+#undef aes_encrypt
+#undef aes_decrypt
+#include <nettle/arcfour.h>
+#include <nettle/bignum.h>
+
+#include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ struct des_ctx ctx;
+ u8 pkey[8], next, tmp;
+ int i;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ nettle_des_set_key(&ctx, pkey);
+ nettle_des_encrypt(&ctx, DES_BLOCK_SIZE, cypher, clear);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+static int nettle_digest_vector(const struct nettle_hash *alg, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ void *ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ctx = os_malloc(alg->context_size);
+ if (!ctx)
+ return -1;
+ alg->init(ctx);
+ for (i = 0; i < num_elem; i++)
+ alg->update(ctx, len[i], addr[i]);
+ alg->digest(ctx, alg->digest_size, mac);
+ bin_clear_free(ctx, alg->context_size);
+ return 0;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_md4, num_elem, addr, len, mac);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_md5, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_sha1, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_sha256, num_elem, addr, len, mac);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_sha384, num_elem, addr, len, mac);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_sha512, num_elem, addr, len, mac);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_md5_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_md5_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_md5_update(&ctx, len[i], addr[i]);
+ hmac_md5_digest(&ctx, MD5_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_sha1_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_sha1_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_sha1_update(&ctx, len[i], addr[i]);
+ hmac_sha1_digest(&ctx, SHA1_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_sha256_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_sha256_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_sha256_update(&ctx, len[i], addr[i]);
+ hmac_sha256_digest(&ctx, SHA256_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_sha384_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_sha384_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_sha384_update(&ctx, len[i], addr[i]);
+ hmac_sha384_digest(&ctx, SHA384_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_sha512_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_sha512_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_sha512_update(&ctx, len[i], addr[i]);
+ hmac_sha512_digest(&ctx, SHA512_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ struct aes_ctx *ctx;
+
+ if (TEST_FAIL())
+ return NULL;
+ ctx = os_malloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ nettle_aes_set_encrypt_key(ctx, len, key);
+
+ return ctx;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ struct aes_ctx *actx = ctx;
+ nettle_aes_encrypt(actx, AES_BLOCK_SIZE, crypt, plain);
+ return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ struct aes_ctx *actx = ctx;
+ bin_clear_free(actx, sizeof(*actx));
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ struct aes_ctx *ctx;
+
+ if (TEST_FAIL())
+ return NULL;
+ ctx = os_malloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ nettle_aes_set_decrypt_key(ctx, len, key);
+
+ return ctx;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ struct aes_ctx *actx = ctx;
+ nettle_aes_decrypt(actx, AES_BLOCK_SIZE, plain, crypt);
+ return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ struct aes_ctx *actx = ctx;
+ bin_clear_free(actx, sizeof(*actx));
+}
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ mpz_t bn_base, bn_exp, bn_modulus, bn_result;
+ int ret = -1;
+ size_t len;
+
+ mpz_inits(bn_base, bn_exp, bn_modulus, bn_result, NULL);
+ mpz_import(bn_base, base_len, 1, 1, 1, 0, base);
+ mpz_import(bn_exp, power_len, 1, 1, 1, 0, power);
+ mpz_import(bn_modulus, modulus_len, 1, 1, 1, 0, modulus);
+
+ mpz_powm(bn_result, bn_base, bn_exp, bn_modulus);
+ len = mpz_sizeinbase(bn_result, 2);
+ len = (len + 7) / 8;
+ if (*result_len < len)
+ goto error;
+ mpz_export(result, result_len, 1, 1, 1, 0, bn_result);
+ ret = 0;
+
+error:
+ mpz_clears(bn_base, bn_exp, bn_modulus, bn_result, NULL);
+ return ret;
+}
+
+
+struct crypto_cipher {
+ enum crypto_cipher_alg alg;
+ union {
+ struct arcfour_ctx arcfour_ctx;
+ } u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ ctx->alg = alg;
+
+ switch (alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ nettle_arcfour_set_key(&ctx->u.arcfour_ctx, key_len, key);
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ switch (ctx->alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, crypt, plain);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ switch (ctx->alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, plain, crypt);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ bin_clear_free(ctx, sizeof(*ctx));
+}
diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c
index 011f3f35a055..547919418af9 100644
--- a/src/crypto/crypto_none.c
+++ b/src/crypto/crypto_none.c
@@ -18,6 +18,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
}
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
{
+ return 0;
}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 19e0e2be87be..f89053a89d67 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
/*
* Wrapper functions for OpenSSL libcrypto
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -29,11 +29,14 @@
#include "sha1.h"
#include "sha256.h"
#include "sha384.h"
+#include "sha512.h"
#include "md5.h"
#include "aes_wrap.h"
#include "crypto.h"
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
/* Compatibility wrappers for older versions. */
static HMAC_CTX * HMAC_CTX_new(void)
@@ -79,7 +82,9 @@ static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
static BIGNUM * get_group5_prime(void)
{
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
return BN_get_rfc3526_prime_1536(NULL);
#elif !defined(OPENSSL_IS_BORINGSSL)
return get_rfc3526_prime_1536(NULL);
@@ -109,6 +114,9 @@ static BIGNUM * get_group5_prime(void)
#ifdef OPENSSL_NO_SHA256
#define NO_SHA256_WRAPPER
#endif
+#ifdef OPENSSL_NO_SHA512
+#define NO_SHA384_WRAPPER
+#endif
static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
@@ -158,7 +166,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
#endif /* CONFIG_FIPS */
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
{
u8 pkey[8], next, tmp;
int i;
@@ -176,6 +184,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
DES_set_key((DES_cblock *) &pkey, &ks);
DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
DES_ENCRYPT);
+ return 0;
}
@@ -243,15 +252,31 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
#endif /* NO_SHA256_WRAPPER */
+#ifndef NO_SHA384_WRAPPER
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return openssl_digest_vector(EVP_sha384(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA384_WRAPPER */
+
+
+#ifndef NO_SHA512_WRAPPER
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA512_WRAPPER */
+
+
static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
{
switch (keylen) {
case 16:
return EVP_aes_128_ecb();
-#ifndef OPENSSL_IS_BORINGSSL
case 24:
return EVP_aes_192_ecb();
-#endif /* OPENSSL_IS_BORINGSSL */
case 32:
return EVP_aes_256_ecb();
}
@@ -269,8 +294,11 @@ void * aes_encrypt_init(const u8 *key, size_t len)
return NULL;
type = aes_get_evp_cipher(len);
- if (type == NULL)
+ if (!type) {
+ wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
+ __func__, (unsigned int) len);
return NULL;
+ }
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
@@ -284,14 +312,16 @@ void * aes_encrypt_init(const u8 *key, size_t len)
}
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
EVP_CIPHER_CTX *c = ctx;
int clen = 16;
if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
ERR_error_string(ERR_get_error(), NULL));
+ return -1;
}
+ return 0;
}
@@ -321,8 +351,11 @@ void * aes_decrypt_init(const u8 *key, size_t len)
return NULL;
type = aes_get_evp_cipher(len);
- if (type == NULL)
+ if (!type) {
+ wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
+ __func__, (unsigned int) len);
return NULL;
+ }
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
@@ -336,14 +369,16 @@ void * aes_decrypt_init(const u8 *key, size_t len)
}
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
EVP_CIPHER_CTX *c = ctx;
int plen = 16;
if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
ERR_error_string(ERR_get_error(), NULL));
+ return -1;
}
+ return 0;
}
@@ -372,6 +407,8 @@ int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
AES_KEY actx;
int res;
+ if (TEST_FAIL())
+ return -1;
if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
return -1;
res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
@@ -386,6 +423,8 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
AES_KEY actx;
int res;
+ if (TEST_FAIL())
+ return -1;
if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
return -1;
res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
@@ -452,6 +491,42 @@ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
}
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
@@ -611,7 +686,9 @@ void crypto_cipher_deinit(struct crypto_cipher *ctx)
void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
DH *dh;
struct wpabuf *pubkey = NULL, *privkey = NULL;
size_t publen, privlen;
@@ -712,7 +789,9 @@ err:
void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
DH *dh;
dh = DH_new();
@@ -1016,7 +1095,7 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
- len, mac, 32);
+ len, mac, 48);
}
@@ -1029,6 +1108,25 @@ int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr,
+ len, mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
int crypto_get_random(void *buf, size_t len)
{
if (RAND_bytes(buf, len) != 1)
@@ -1150,6 +1248,12 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a,
}
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+ return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1;
+}
+
+
int crypto_bignum_add(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
@@ -1275,6 +1379,15 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a,
}
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+ struct crypto_bignum *r)
+{
+ /* Note: BN_rshift() does not modify the first argument even though it
+ * has not been marked const. */
+ return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1;
+}
+
+
int crypto_bignum_cmp(const struct crypto_bignum *a,
const struct crypto_bignum *b)
{
@@ -1300,6 +1413,12 @@ int crypto_bignum_is_one(const struct crypto_bignum *a)
}
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+ return BN_is_odd((const BIGNUM *) a);
+}
+
+
int crypto_bignum_legendre(const struct crypto_bignum *a,
const struct crypto_bignum *p)
{
@@ -1343,6 +1462,7 @@ fail:
struct crypto_ec {
EC_GROUP *group;
+ int nid;
BN_CTX *bnctx;
BIGNUM *prime;
BIGNUM *order;
@@ -1400,6 +1520,7 @@ struct crypto_ec * crypto_ec_init(int group)
if (e == NULL)
return NULL;
+ e->nid = nid;
e->bnctx = BN_CTX_new();
e->group = EC_GROUP_new_by_curve_name(nid);
e->prime = BN_new();
@@ -1432,6 +1553,13 @@ void crypto_ec_deinit(struct crypto_ec *e)
}
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor)
+{
+ return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor,
+ e->bnctx) == 0 ? -1 : 0;
+}
+
+
struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
{
if (TEST_FAIL())
@@ -1454,6 +1582,12 @@ size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
}
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+ return BN_num_bytes(e->order);
+}
+
+
const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
{
return (const struct crypto_bignum *) e->prime;
@@ -1475,6 +1609,16 @@ void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
}
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+ struct crypto_bignum *x)
+{
+ return EC_POINT_get_affine_coordinates_GFp(e->group,
+ (const EC_POINT *) p,
+ (BIGNUM *) x, NULL,
+ e->bnctx) == 1 ? 0 : -1;
+}
+
+
int crypto_ec_point_to_bin(struct crypto_ec *e,
const struct crypto_ec_point *point, u8 *x, u8 *y)
{
@@ -1640,4 +1784,228 @@ int crypto_ec_point_cmp(const struct crypto_ec *e,
(const EC_POINT *) b, e->bnctx);
}
+
+struct crypto_ecdh {
+ struct crypto_ec *ec;
+ EVP_PKEY *pkey;
+};
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+ struct crypto_ecdh *ecdh;
+ EVP_PKEY *params = NULL;
+ EC_KEY *ec_params;
+ EVP_PKEY_CTX *kctx = NULL;
+
+ ecdh = os_zalloc(sizeof(*ecdh));
+ if (!ecdh)
+ goto fail;
+
+ ecdh->ec = crypto_ec_init(group);
+ if (!ecdh->ec)
+ goto fail;
+
+ ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid);
+ if (!ec_params) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to generate EC_KEY parameters");
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to generate EVP_PKEY parameters");
+ goto fail;
+ }
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (!kctx)
+ goto fail;
+
+ if (EVP_PKEY_keygen_init(kctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EVP_PKEY_keygen_init failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+done:
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+
+ return ecdh;
+fail:
+ crypto_ecdh_deinit(ecdh);
+ ecdh = NULL;
+ goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+ struct wpabuf *buf = NULL;
+ EC_KEY *eckey;
+ const EC_POINT *pubkey;
+ BIGNUM *x, *y = NULL;
+ int len = BN_num_bytes(ecdh->ec->prime);
+ int res;
+
+ eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey);
+ if (!eckey)
+ return NULL;
+
+ pubkey = EC_KEY_get0_public_key(eckey);
+ if (!pubkey)
+ return NULL;
+
+ x = BN_new();
+ if (inc_y) {
+ y = BN_new();
+ if (!y)
+ goto fail;
+ }
+ buf = wpabuf_alloc(inc_y ? 2 * len : len);
+ if (!x || !buf)
+ goto fail;
+
+ if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey,
+ x, y, ecdh->ec->bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ res = crypto_bignum_to_bin((struct crypto_bignum *) x,
+ wpabuf_put(buf, len), len, len);
+ if (res < 0)
+ goto fail;
+
+ if (inc_y) {
+ res = crypto_bignum_to_bin((struct crypto_bignum *) y,
+ wpabuf_put(buf, len), len, len);
+ if (res < 0)
+ goto fail;
+ }
+
+done:
+ BN_clear_free(x);
+ BN_clear_free(y);
+ EC_KEY_free(eckey);
+
+ return buf;
+fail:
+ wpabuf_free(buf);
+ buf = NULL;
+ goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+ const u8 *key, size_t len)
+{
+ BIGNUM *x, *y = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ EVP_PKEY *peerkey = NULL;
+ struct wpabuf *secret = NULL;
+ size_t secret_len;
+ EC_POINT *pub;
+ EC_KEY *eckey = NULL;
+
+ x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL);
+ pub = EC_POINT_new(ecdh->ec->group);
+ if (!x || !pub)
+ goto fail;
+
+ if (inc_y) {
+ y = BN_bin2bn(key + len / 2, len / 2, NULL);
+ if (!y)
+ goto fail;
+ if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub,
+ x, y,
+ ecdh->ec->bnctx)) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ } else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group,
+ pub, x, 0,
+ ecdh->ec->bnctx)) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: ECDH peer public key is not on curve");
+ goto fail;
+ }
+
+ eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid);
+ if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EC_KEY_set_public_key failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ peerkey = EVP_PKEY_new();
+ if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1)
+ goto fail;
+
+ ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL);
+ if (!ctx || EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EVP_PKEY_derive(1) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ secret = wpabuf_alloc(secret_len);
+ if (!secret)
+ goto fail;
+ if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len),
+ &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EVP_PKEY_derive(2) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+done:
+ BN_free(x);
+ BN_free(y);
+ EC_KEY_free(eckey);
+ EC_POINT_free(pub);
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(peerkey);
+ return secret;
+fail:
+ wpabuf_free(secret);
+ secret = NULL;
+ goto done;
+}
+
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+ if (ecdh) {
+ crypto_ec_deinit(ecdh->ec);
+ EVP_PKEY_free(ecdh->pkey);
+ os_free(ecdh);
+ }
+}
+
#endif /* CONFIG_ECC */
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
new file mode 100644
index 000000000000..b5a1e3fa31bc
--- /dev/null
+++ b/src/crypto/crypto_wolfssl.c
@@ -0,0 +1,1791 @@
+/*
+ * Wrapper functions for libwolfssl
+ * Copyright (c) 2004-2017, 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 "crypto.h"
+
+/* wolfSSL headers */
+#include <wolfssl/options.h>
+#include <wolfssl/wolfcrypt/md4.h>
+#include <wolfssl/wolfcrypt/md5.h>
+#include <wolfssl/wolfcrypt/sha.h>
+#include <wolfssl/wolfcrypt/sha256.h>
+#include <wolfssl/wolfcrypt/sha512.h>
+#include <wolfssl/wolfcrypt/hmac.h>
+#include <wolfssl/wolfcrypt/pwdbased.h>
+#include <wolfssl/wolfcrypt/arc4.h>
+#include <wolfssl/wolfcrypt/des3.h>
+#include <wolfssl/wolfcrypt/aes.h>
+#include <wolfssl/wolfcrypt/dh.h>
+#include <wolfssl/wolfcrypt/cmac.h>
+#include <wolfssl/wolfcrypt/ecc.h>
+#include <wolfssl/openssl/bn.h>
+
+
+#ifndef CONFIG_FIPS
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ Md4 md4;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitMd4(&md4);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Md4Update(&md4, addr[i], len[i]);
+
+ wc_Md4Final(&md4, mac);
+
+ return 0;
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ wc_Md5 md5;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitMd5(&md5);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Md5Update(&md5, addr[i], len[i]);
+
+ wc_Md5Final(&md5, mac);
+
+ return 0;
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ wc_Sha sha;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitSha(&sha);
+
+ for (i = 0; i < num_elem; i++)
+ wc_ShaUpdate(&sha, addr[i], len[i]);
+
+ wc_ShaFinal(&sha, mac);
+
+ return 0;
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ wc_Sha256 sha256;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitSha256(&sha256);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Sha256Update(&sha256, addr[i], len[i]);
+
+ wc_Sha256Final(&sha256, mac);
+
+ return 0;
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+#ifdef CONFIG_SHA384
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ wc_Sha384 sha384;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitSha384(&sha384);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Sha384Update(&sha384, addr[i], len[i]);
+
+ wc_Sha384Final(&sha384, mac);
+
+ return 0;
+}
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ wc_Sha512 sha512;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitSha512(&sha512);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Sha512Update(&sha512, addr[i], len[i]);
+
+ wc_Sha512Final(&sha512, mac);
+
+ return 0;
+}
+#endif /* CONFIG_SHA512 */
+
+
+static int wolfssl_hmac_vector(int type, const u8 *key,
+ size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac,
+ unsigned int mdlen)
+{
+ Hmac hmac;
+ size_t i;
+
+ (void) mdlen;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0)
+ return -1;
+ for (i = 0; i < num_elem; i++)
+ if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0)
+ return -1;
+ if (wc_HmacFinal(&hmac, mac) != 0)
+ return -1;
+ return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len,
+ mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len,
+ mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len,
+ mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len,
+ mac, 48);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len,
+ mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid,
+ ssid_len, iterations, buflen, WC_SHA) != 0)
+ return -1;
+ return 0;
+}
+
+
+#ifdef CONFIG_DES
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ Des des;
+ u8 pkey[8], next, tmp;
+ int i;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION);
+ wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE);
+
+ return 0;
+}
+#endif /* CONFIG_DES */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ Aes *aes;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ aes = os_malloc(sizeof(Aes));
+ if (!aes)
+ return NULL;
+
+ if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) {
+ os_free(aes);
+ return NULL;
+ }
+
+ return aes;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ wc_AesEncryptDirect(ctx, crypt, plain);
+ return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ os_free(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ Aes *aes;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ aes = os_malloc(sizeof(Aes));
+ if (!aes)
+ return NULL;
+
+ if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) {
+ os_free(aes);
+ return NULL;
+ }
+
+ return aes;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ wc_AesDecryptDirect(ctx, plain, crypt);
+ return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ os_free(ctx);
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ Aes aes;
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION);
+ if (ret != 0)
+ return -1;
+
+ ret = wc_AesCbcEncrypt(&aes, data, data, data_len);
+ if (ret != 0)
+ return -1;
+ return 0;
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ Aes aes;
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION);
+ if (ret != 0)
+ return -1;
+
+ ret = wc_AesCbcDecrypt(&aes, data, data, data_len);
+ if (ret != 0)
+ return -1;
+ return 0;
+}
+
+
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8,
+ NULL);
+ return ret != (n + 1) * 8 ? -1 : 0;
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+ u8 *plain)
+{
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8,
+ NULL);
+ return ret != n * 8 ? -1 : 0;
+}
+
+
+#ifndef CONFIG_NO_RC4
+int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data,
+ size_t data_len)
+{
+#ifndef NO_RC4
+ Arc4 arc4;
+ unsigned char skip_buf[16];
+
+ wc_Arc4SetKey(&arc4, key, keylen);
+
+ while (skip >= sizeof(skip_buf)) {
+ size_t len = skip;
+
+ if (len > sizeof(skip_buf))
+ len = sizeof(skip_buf);
+ wc_Arc4Process(&arc4, skip_buf, skip_buf, len);
+ skip -= len;
+ }
+
+ wc_Arc4Process(&arc4, data, data, data_len);
+
+ return 0;
+#else /* NO_RC4 */
+ return -1;
+#endif /* NO_RC4 */
+}
+#endif /* CONFIG_NO_RC4 */
+
+
+#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \
+ || defined(EAP_SERVER_IKEV2)
+union wolfssl_cipher {
+ Aes aes;
+ Des3 des3;
+ Arc4 arc4;
+};
+
+struct crypto_cipher {
+ enum crypto_cipher_alg alg;
+ union wolfssl_cipher enc;
+ union wolfssl_cipher dec;
+};
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ switch (alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+ case CRYPTO_CIPHER_ALG_RC4:
+ wc_Arc4SetKey(&ctx->enc.arc4, key, key_len);
+ wc_Arc4SetKey(&ctx->dec.arc4, key, key_len);
+ break;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+ case CRYPTO_CIPHER_ALG_AES:
+ switch (key_len) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+ if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv,
+ AES_ENCRYPTION) ||
+ wc_AesSetKey(&ctx->dec.aes, key, key_len, iv,
+ AES_DECRYPTION)) {
+ os_free(ctx);
+ return NULL;
+ }
+ break;
+#endif /* NO_AES */
+#ifndef NO_DES3
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (key_len != DES3_KEYLEN ||
+ wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) ||
+ wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) {
+ os_free(ctx);
+ return NULL;
+ }
+ break;
+#endif /* NO_DES3 */
+ case CRYPTO_CIPHER_ALG_RC2:
+ case CRYPTO_CIPHER_ALG_DES:
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ ctx->alg = alg;
+
+ return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ switch (ctx->alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+ case CRYPTO_CIPHER_ALG_RC4:
+ wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len);
+ return 0;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+ case CRYPTO_CIPHER_ALG_AES:
+ if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0)
+ return -1;
+ return 0;
+#endif /* NO_AES */
+#ifndef NO_DES3
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0)
+ return -1;
+ return 0;
+#endif /* NO_DES3 */
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ switch (ctx->alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+ case CRYPTO_CIPHER_ALG_RC4:
+ wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len);
+ return 0;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+ case CRYPTO_CIPHER_ALG_AES:
+ if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0)
+ return -1;
+ return 0;
+#endif /* NO_AES */
+#ifndef NO_DES3
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0)
+ return -1;
+ return 0;
+#endif /* NO_DES3 */
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ os_free(ctx);
+}
+
+#endif
+
+
+#ifdef CONFIG_WPS_NFC
+
+static const unsigned char RFC3526_PRIME_1536[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+ 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const unsigned char RFC3526_GENERATOR_1536[] = {
+ 0x02
+};
+
+#define RFC3526_LEN sizeof(RFC3526_PRIME_1536)
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+ WC_RNG rng;
+ DhKey *ret = NULL;
+ DhKey *dh = NULL;
+ struct wpabuf *privkey = NULL;
+ struct wpabuf *pubkey = NULL;
+ word32 priv_sz, pub_sz;
+
+ *priv = NULL;
+ wpabuf_free(*publ);
+ *publ = NULL;
+
+ dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (!dh)
+ return NULL;
+ wc_InitDhKey(dh);
+
+ if (wc_InitRng(&rng) != 0) {
+ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return NULL;
+ }
+
+ privkey = wpabuf_alloc(RFC3526_LEN);
+ pubkey = wpabuf_alloc(RFC3526_LEN);
+ if (!privkey || !pubkey)
+ goto done;
+
+ if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
+ RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
+ != 0)
+ goto done;
+
+ if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz,
+ wpabuf_mhead(pubkey), &pub_sz) != 0)
+ goto done;
+
+ wpabuf_put(privkey, priv_sz);
+ wpabuf_put(pubkey, pub_sz);
+
+ ret = dh;
+ *priv = privkey;
+ *publ = pubkey;
+ dh = NULL;
+ privkey = NULL;
+ pubkey = NULL;
+done:
+ wpabuf_clear_free(pubkey);
+ wpabuf_clear_free(privkey);
+ if (dh) {
+ wc_FreeDhKey(dh);
+ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ }
+ wc_FreeRng(&rng);
+ return ret;
+}
+
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+ DhKey *ret = NULL;
+ DhKey *dh;
+ byte *secret;
+ word32 secret_sz;
+
+ dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (!dh)
+ return NULL;
+ wc_InitDhKey(dh);
+
+ secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (!secret)
+ goto done;
+
+ if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
+ RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
+ != 0)
+ goto done;
+
+ if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv),
+ wpabuf_len(priv), RFC3526_GENERATOR_1536,
+ sizeof(RFC3526_GENERATOR_1536)) != 0)
+ goto done;
+
+ if (secret_sz != wpabuf_len(publ) ||
+ os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0)
+ goto done;
+
+ ret = dh;
+ dh = NULL;
+done:
+ if (dh) {
+ wc_FreeDhKey(dh);
+ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ }
+ XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+ const struct wpabuf *own_private)
+{
+ struct wpabuf *ret = NULL;
+ struct wpabuf *secret;
+ word32 secret_sz;
+
+ secret = wpabuf_alloc(RFC3526_LEN);
+ if (!secret)
+ goto done;
+
+ if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz,
+ wpabuf_head(own_private), wpabuf_len(own_private),
+ wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0)
+ goto done;
+
+ wpabuf_put(secret, secret_sz);
+
+ ret = secret;
+ secret = NULL;
+done:
+ wpabuf_clear_free(secret);
+ return ret;
+}
+
+
+void dh5_free(void *ctx)
+{
+ if (!ctx)
+ return;
+
+ wc_FreeDhKey(ctx);
+ XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ int ret = -1;
+ WC_RNG rng;
+ DhKey *dh = NULL;
+ word32 priv_sz, pub_sz;
+
+ if (TEST_FAIL())
+ return -1;
+
+ dh = os_malloc(sizeof(DhKey));
+ if (!dh)
+ return -1;
+ wc_InitDhKey(dh);
+
+ if (wc_InitRng(&rng) != 0) {
+ os_free(dh);
+ return -1;
+ }
+
+ if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
+ goto done;
+
+ if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz)
+ != 0)
+ goto done;
+
+ if (priv_sz < prime_len) {
+ size_t pad_sz = prime_len - priv_sz;
+
+ os_memmove(privkey + pad_sz, privkey, priv_sz);
+ os_memset(privkey, 0, pad_sz);
+ }
+
+ if (pub_sz < prime_len) {
+ size_t pad_sz = prime_len - pub_sz;
+
+ os_memmove(pubkey + pad_sz, pubkey, pub_sz);
+ os_memset(pubkey, 0, pad_sz);
+ }
+ ret = 0;
+done:
+ wc_FreeDhKey(dh);
+ os_free(dh);
+ wc_FreeRng(&rng);
+ return ret;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ int ret = -1;
+ DhKey *dh;
+ word32 secret_sz;
+
+ dh = os_malloc(sizeof(DhKey));
+ if (!dh)
+ return -1;
+ wc_InitDhKey(dh);
+
+ if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
+ goto done;
+
+ if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey,
+ pubkey_len) != 0)
+ goto done;
+
+ *len = secret_sz;
+ ret = 0;
+done:
+ wc_FreeDhKey(dh);
+ os_free(dh);
+ return ret;
+}
+
+
+#ifdef CONFIG_FIPS
+int crypto_get_random(void *buf, size_t len)
+{
+ int ret = 0;
+ WC_RNG rng;
+
+ if (wc_InitRng(&rng) != 0)
+ return -1;
+ if (wc_RNG_GenerateBlock(&rng, buf, len) != 0)
+ ret = -1;
+ wc_FreeRng(&rng);
+ return ret;
+}
+#endif /* CONFIG_FIPS */
+
+
+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD)
+struct crypto_hash {
+ Hmac hmac;
+ int size;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_hash *ret = NULL;
+ struct crypto_hash *hash;
+ int type;
+
+ hash = os_zalloc(sizeof(*hash));
+ if (!hash)
+ goto done;
+
+ switch (alg) {
+#ifndef NO_MD5
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ hash->size = 16;
+ type = WC_MD5;
+ break;
+#endif /* NO_MD5 */
+#ifndef NO_SHA
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ type = WC_SHA;
+ hash->size = 20;
+ break;
+#endif /* NO_SHA */
+#ifdef CONFIG_SHA256
+#ifndef NO_SHA256
+ case CRYPTO_HASH_ALG_HMAC_SHA256:
+ type = WC_SHA256;
+ hash->size = 32;
+ break;
+#endif /* NO_SHA256 */
+#endif /* CONFIG_SHA256 */
+ default:
+ goto done;
+ }
+
+ if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0)
+ goto done;
+
+ ret = hash;
+ hash = NULL;
+done:
+ os_free(hash);
+ return ret;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+ if (!ctx)
+ return;
+ wc_HmacUpdate(&ctx->hmac, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+ int ret = 0;
+
+ if (!ctx)
+ return -2;
+
+ if (!mac || !len)
+ goto done;
+
+ if (wc_HmacFinal(&ctx->hmac, mac) != 0) {
+ ret = -1;
+ goto done;
+ }
+
+ *len = ctx->size;
+ ret = 0;
+done:
+ bin_clear_free(ctx, sizeof(*ctx));
+ return ret;
+}
+
+#endif
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ Cmac cmac;
+ size_t i;
+ word32 sz;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0)
+ return -1;
+
+ for (i = 0; i < num_elem; i++)
+ if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0)
+ return -1;
+
+ sz = AES_BLOCK_SIZE;
+ if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE)
+ return -1;
+
+ return 0;
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+ mp_int *a;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ a = os_malloc(sizeof(*a));
+ if (!a || mp_init(a) != MP_OKAY) {
+ os_free(a);
+ a = NULL;
+ }
+
+ return (struct crypto_bignum *) a;
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+ mp_int *a;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ a = (mp_int *) crypto_bignum_init();
+ if (!a)
+ return NULL;
+
+ if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) {
+ os_free(a);
+ a = NULL;
+ }
+
+ return (struct crypto_bignum *) a;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+ if (!n)
+ return;
+
+ if (clear)
+ mp_forcezero((mp_int *) n);
+ mp_clear((mp_int *) n);
+ os_free((mp_int *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+ u8 *buf, size_t buflen, size_t padlen)
+{
+ int num_bytes, offset;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (padlen > buflen)
+ return -1;
+
+ num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8;
+ if ((size_t) num_bytes > buflen)
+ return -1;
+ if (padlen > (size_t) num_bytes)
+ offset = padlen - num_bytes;
+ else
+ offset = 0;
+
+ os_memset(buf, 0, offset);
+ mp_to_unsigned_bin((mp_int *) a, buf + offset);
+
+ return num_bytes + offset;
+}
+
+
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+ int ret = 0;
+ WC_RNG rng;
+
+ if (wc_InitRng(&rng) != 0)
+ return -1;
+ if (mp_rand_prime((mp_int *) r,
+ (mp_count_bits((mp_int *) m) + 7) / 8 * 2,
+ &rng, NULL) != 0)
+ ret = -1;
+ if (ret == 0 &&
+ mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
+ ret = -1;
+ wc_FreeRng(&rng);
+ return ret;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *r)
+{
+ return mp_add((mp_int *) a, (mp_int *) b,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+ const struct crypto_bignum *m,
+ struct crypto_bignum *r)
+{
+ return mp_mod((mp_int *) a, (mp_int *) m,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *b,
+ const struct crypto_bignum *e,
+ const struct crypto_bignum *m,
+ struct crypto_bignum *r)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+ const struct crypto_bignum *m,
+ struct crypto_bignum *r)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_invmod((mp_int *) a, (mp_int *) m,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *r)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_add((mp_int *) a, (mp_int *) b,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *d)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d,
+ NULL) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ const struct crypto_bignum *m,
+ struct crypto_bignum *d)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m,
+ (mp_int *) d) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+ struct crypto_bignum *r)
+{
+ if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY)
+ return -1;
+ mp_rshb((mp_int *) r, n);
+ return 0;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+ const struct crypto_bignum *b)
+{
+ return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+int crypto_bignum_bits(const struct crypto_bignum *a)
+{
+ return mp_count_bits((mp_int *) a);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+ return mp_iszero((mp_int *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+ return mp_isone((const mp_int *) a);
+}
+
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+ return mp_isodd((mp_int *) a);
+}
+
+
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+ const struct crypto_bignum *p)
+{
+ mp_int t;
+ int ret;
+ int res = -2;
+
+ if (TEST_FAIL())
+ return -2;
+
+ if (mp_init(&t) != MP_OKAY)
+ return -2;
+
+ /* t = (p-1) / 2 */
+ ret = mp_sub_d((mp_int *) p, 1, &t);
+ if (ret == MP_OKAY)
+ mp_rshb(&t, 1);
+ if (ret == MP_OKAY)
+ ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t);
+ if (ret == MP_OKAY) {
+ if (mp_isone(&t))
+ res = 1;
+ else if (mp_iszero(&t))
+ res = 0;
+ else
+ res = -1;
+ }
+
+ mp_clear(&t);
+ return res;
+}
+
+
+#ifdef CONFIG_ECC
+
+int ecc_map(ecc_point *, mp_int *, mp_digit);
+int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R,
+ mp_int *a, mp_int *modulus, mp_digit mp);
+
+struct crypto_ec {
+ ecc_key key;
+ mp_int a;
+ mp_int prime;
+ mp_int order;
+ mp_digit mont_b;
+ mp_int b;
+};
+
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+ int built = 0;
+ struct crypto_ec *e;
+ int curve_id;
+
+ /* Map from IANA registry for IKE D-H groups to OpenSSL NID */
+ switch (group) {
+ case 19:
+ curve_id = ECC_SECP256R1;
+ break;
+ case 20:
+ curve_id = ECC_SECP384R1;
+ break;
+ case 21:
+ curve_id = ECC_SECP521R1;
+ break;
+ case 25:
+ curve_id = ECC_SECP192R1;
+ break;
+ case 26:
+ curve_id = ECC_SECP224R1;
+ break;
+#ifdef HAVE_ECC_BRAINPOOL
+ case 27:
+ curve_id = ECC_BRAINPOOLP224R1;
+ break;
+ case 28:
+ curve_id = ECC_BRAINPOOLP256R1;
+ break;
+ case 29:
+ curve_id = ECC_BRAINPOOLP384R1;
+ break;
+ case 30:
+ curve_id = ECC_BRAINPOOLP512R1;
+ break;
+#endif /* HAVE_ECC_BRAINPOOL */
+ default:
+ return NULL;
+ }
+
+ e = os_zalloc(sizeof(*e));
+ if (!e)
+ return NULL;
+
+ if (wc_ecc_init(&e->key) != 0 ||
+ wc_ecc_set_curve(&e->key, 0, curve_id) != 0 ||
+ mp_init(&e->a) != MP_OKAY ||
+ mp_init(&e->prime) != MP_OKAY ||
+ mp_init(&e->order) != MP_OKAY ||
+ mp_init(&e->b) != MP_OKAY ||
+ mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY ||
+ mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY ||
+ mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY ||
+ mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY ||
+ mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY)
+ goto done;
+
+ built = 1;
+done:
+ if (!built) {
+ crypto_ec_deinit(e);
+ e = NULL;
+ }
+ return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec* e)
+{
+ if (!e)
+ return;
+
+ mp_clear(&e->b);
+ mp_clear(&e->order);
+ mp_clear(&e->prime);
+ mp_clear(&e->a);
+ wc_ecc_free(&e->key);
+ os_free(e);
+}
+
+
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor)
+{
+ if (!e || !cofactor)
+ return -1;
+
+ mp_set((mp_int *) cofactor, e->key.dp->cofactor);
+ return 0;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+ if (TEST_FAIL())
+ return NULL;
+ if (!e)
+ return NULL;
+ return (struct crypto_ec_point *) wc_ecc_new_point();
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+ return (mp_count_bits(&e->prime) + 7) / 8;
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+ return mp_count_bits(&e->prime);
+}
+
+
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+ return (mp_count_bits(&e->order) + 7) / 8;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+ return (const struct crypto_bignum *) &e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+ return (const struct crypto_bignum *) &e->order;
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+ ecc_point *point = (ecc_point *) p;
+
+ if (!p)
+ return;
+
+ if (clear) {
+ mp_forcezero(point->x);
+ mp_forcezero(point->y);
+ mp_forcezero(point->z);
+ }
+ wc_ecc_del_point(point);
+}
+
+
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+ struct crypto_bignum *x)
+{
+ return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+ const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+ ecc_point *p = (ecc_point *) point;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (!mp_isone(p->z)) {
+ if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY)
+ return -1;
+ }
+
+ if (x) {
+ if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x,
+ e->key.dp->size,
+ e->key.dp->size) <= 0)
+ return -1;
+ }
+
+ if (y) {
+ if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y,
+ e->key.dp->size,
+ e->key.dp->size) <= 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+ const u8 *val)
+{
+ ecc_point *point = NULL;
+ int loaded = 0;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ point = wc_ecc_new_point();
+ if (!point)
+ goto done;
+
+ if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY)
+ goto done;
+ val += e->key.dp->size;
+ if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY)
+ goto done;
+ mp_set(point->z, 1);
+
+ loaded = 1;
+done:
+ if (!loaded) {
+ wc_ecc_del_point(point);
+ point = NULL;
+ }
+ return (struct crypto_ec_point *) point;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b,
+ struct crypto_ec_point *c)
+{
+ mp_int mu;
+ ecc_point *ta = NULL, *tb = NULL;
+ ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b;
+ mp_int *modulus = &e->prime;
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = mp_init(&mu);
+ if (ret != MP_OKAY)
+ return -1;
+
+ ret = mp_montgomery_calc_normalization(&mu, modulus);
+ if (ret != MP_OKAY) {
+ mp_clear(&mu);
+ return -1;
+ }
+
+ if (!mp_isone(&mu)) {
+ ta = wc_ecc_new_point();
+ if (!ta) {
+ mp_clear(&mu);
+ return -1;
+ }
+ tb = wc_ecc_new_point();
+ if (!tb) {
+ wc_ecc_del_point(ta);
+ mp_clear(&mu);
+ return -1;
+ }
+
+ if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY ||
+ mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY ||
+ mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY ||
+ mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY ||
+ mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY ||
+ mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) {
+ ret = -1;
+ goto end;
+ }
+ pa = ta;
+ pb = tb;
+ }
+
+ ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a,
+ &e->prime, e->mont_b);
+ if (ret != 0) {
+ ret = -1;
+ goto end;
+ }
+
+ if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY)
+ ret = -1;
+ else
+ ret = 0;
+end:
+ wc_ecc_del_point(tb);
+ wc_ecc_del_point(ta);
+ mp_clear(&mu);
+ return ret;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+ const struct crypto_bignum *b,
+ struct crypto_ec_point *res)
+{
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res,
+ &e->a, &e->prime, 1);
+ return ret == 0 ? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+ ecc_point *point = (ecc_point *) p;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY)
+ return -1;
+
+ return 0;
+}
+
+
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit)
+{
+ byte buf[1 + 2 * MAX_ECC_BYTES];
+ int ret;
+ int prime_len = crypto_ec_prime_len(e);
+
+ if (TEST_FAIL())
+ return -1;
+
+ buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN;
+ ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len);
+ if (ret <= 0)
+ return -1;
+ ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx,
+ (ecc_point *) p);
+ if (ret != 0)
+ return -1;
+
+ return 0;
+}
+
+
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+ const struct crypto_bignum *x)
+{
+ mp_int *y2 = NULL;
+ mp_int t;
+ int calced = 0;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ if (mp_init(&t) != MP_OKAY)
+ return NULL;
+
+ y2 = (mp_int *) crypto_bignum_init();
+ if (!y2)
+ goto done;
+
+ if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 ||
+ mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 ||
+ mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 ||
+ mp_addmod(y2, &t, &e->prime, y2) != 0 ||
+ mp_addmod(y2, &e->b, &e->prime, y2) != 0)
+ goto done;
+
+ calced = 1;
+done:
+ if (!calced) {
+ if (y2) {
+ mp_clear(y2);
+ os_free(y2);
+ }
+ mp_clear(&t);
+ }
+
+ return (struct crypto_bignum *) y2;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+ const struct crypto_ec_point *p)
+{
+ return wc_ecc_point_is_at_infinity((ecc_point *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+ const struct crypto_ec_point *p)
+{
+ return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) ==
+ MP_OKAY;
+}
+
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+ const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b)
+{
+ return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b);
+}
+
+
+struct crypto_ecdh {
+ struct crypto_ec *ec;
+};
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+ struct crypto_ecdh *ecdh = NULL;
+ WC_RNG rng;
+ int ret;
+
+ if (wc_InitRng(&rng) != 0)
+ goto fail;
+
+ ecdh = os_zalloc(sizeof(*ecdh));
+ if (!ecdh)
+ goto fail;
+
+ ecdh->ec = crypto_ec_init(group);
+ if (!ecdh->ec)
+ goto fail;
+
+ ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key,
+ ecdh->ec->key.dp->id);
+ if (ret < 0)
+ goto fail;
+
+done:
+ wc_FreeRng(&rng);
+
+ return ecdh;
+fail:
+ crypto_ecdh_deinit(ecdh);
+ ecdh = NULL;
+ goto done;
+}
+
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+ if (ecdh) {
+ crypto_ec_deinit(ecdh->ec);
+ os_free(ecdh);
+ }
+}
+
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+ struct wpabuf *buf = NULL;
+ int ret;
+ int len = ecdh->ec->key.dp->size;
+
+ buf = wpabuf_alloc(inc_y ? 2 * len : len);
+ if (!buf)
+ goto fail;
+
+ ret = crypto_bignum_to_bin((struct crypto_bignum *)
+ ecdh->ec->key.pubkey.x, wpabuf_put(buf, len),
+ len, len);
+ if (ret < 0)
+ goto fail;
+ if (inc_y) {
+ ret = crypto_bignum_to_bin((struct crypto_bignum *)
+ ecdh->ec->key.pubkey.y,
+ wpabuf_put(buf, len), len, len);
+ if (ret < 0)
+ goto fail;
+ }
+
+done:
+ return buf;
+fail:
+ wpabuf_free(buf);
+ buf = NULL;
+ goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+ const u8 *key, size_t len)
+{
+ int ret;
+ struct wpabuf *pubkey = NULL;
+ struct wpabuf *secret = NULL;
+ word32 key_len = ecdh->ec->key.dp->size;
+ ecc_point *point = NULL;
+ size_t need_key_len = inc_y ? 2 * key_len : key_len;
+
+ if (len < need_key_len)
+ goto fail;
+ pubkey = wpabuf_alloc(1 + 2 * key_len);
+ if (!pubkey)
+ goto fail;
+ wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN);
+ wpabuf_put_data(pubkey, key, need_key_len);
+
+ point = wc_ecc_new_point();
+ if (!point)
+ goto fail;
+
+ ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len,
+ ecdh->ec->key.idx, point);
+ if (ret != MP_OKAY)
+ goto fail;
+
+ secret = wpabuf_alloc(key_len);
+ if (!secret)
+ goto fail;
+
+ ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point,
+ wpabuf_put(secret, key_len), &key_len);
+ if (ret != MP_OKAY)
+ goto fail;
+
+done:
+ wc_ecc_del_point(point);
+ wpabuf_free(pubkey);
+ return secret;
+fail:
+ wpabuf_free(secret);
+ secret = NULL;
+ goto done;
+}
+
+#endif /* CONFIG_ECC */
diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c
index dec39ef8c61d..4ed6957802b0 100644
--- a/src/crypto/des-internal.c
+++ b/src/crypto/des-internal.c
@@ -48,7 +48,7 @@
static const u32 bytebit[8] =
{
- 0200, 0100, 040, 020, 010, 04, 02, 01
+ 0200, 0100, 040, 020, 010, 04, 02, 01
};
static const u32 bigbyte[24] =
@@ -58,22 +58,22 @@ static const u32 bigbyte[24] =
0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL,
0x800UL, 0x400UL, 0x200UL, 0x100UL,
0x80UL, 0x40UL, 0x20UL, 0x10UL,
- 0x8UL, 0x4UL, 0x2UL, 0x1L
+ 0x8UL, 0x4UL, 0x2UL, 0x1L
};
/* Use the key schedule specific in the standard (ANSI X3.92-1981) */
static const u8 pc1[56] = {
- 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
- 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
- 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
};
static const u8 totrot[16] = {
1, 2, 4, 6,
- 8, 10, 12, 14,
- 15, 17, 19, 21,
+ 8, 10, 12, 14,
+ 15, 17, 19, 21,
23, 25, 27, 28
};
@@ -396,7 +396,7 @@ static void desfunc(u32 *block, const u32 *keys)
/* wpa_supplicant/hostapd specific wrapper */
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
{
u8 pkey[8], next, tmp;
int i;
@@ -421,6 +421,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
os_memset(pkey, 0, sizeof(pkey));
os_memset(ek, 0, sizeof(ek));
+ return 0;
}
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 7912361ff8c6..a9b770ec1f16 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1151,7 +1151,7 @@ static const u8 dh_group24_order[] = {
{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
-
+
static const struct dh_group dh_groups[] = {
DH_GROUP(5, 1),
@@ -1203,19 +1203,6 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
if (*priv == NULL)
return NULL;
- if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
- {
- wpabuf_clear_free(*priv);
- *priv = NULL;
- return NULL;
- }
-
- if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) {
- /* Make sure private value is smaller than prime */
- *(wpabuf_mhead_u8(*priv)) = 0;
- }
- wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
-
pv_len = dh->prime_len;
pv = wpabuf_alloc(pv_len);
if (pv == NULL) {
@@ -1223,17 +1210,17 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
*priv = NULL;
return NULL;
}
- if (crypto_mod_exp(dh->generator, dh->generator_len,
- wpabuf_head(*priv), wpabuf_len(*priv),
- dh->prime, dh->prime_len, wpabuf_mhead(pv),
- &pv_len) < 0) {
+ if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len,
+ wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) {
wpabuf_clear_free(pv);
- wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+ wpa_printf(MSG_INFO, "DH: crypto_dh_init failed");
wpabuf_clear_free(*priv);
*priv = NULL;
return NULL;
}
- wpabuf_put(pv, pv_len);
+ wpabuf_put(*priv, dh->prime_len);
+ wpabuf_put(pv, dh->prime_len);
+ wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
return pv;
@@ -1261,12 +1248,14 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
shared = wpabuf_alloc(shared_len);
if (shared == NULL)
return NULL;
- if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public),
- wpabuf_head(own_private), wpabuf_len(own_private),
- dh->prime, dh->prime_len,
- wpabuf_mhead(shared), &shared_len) < 0) {
+ if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+ wpabuf_head(own_private),
+ wpabuf_len(own_private),
+ wpabuf_head(peer_public),
+ wpabuf_len(peer_public),
+ wpabuf_mhead(shared), &shared_len) < 0) {
wpabuf_clear_free(shared);
- wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+ wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed");
return NULL;
}
wpabuf_put(shared, shared_len);
diff --git a/src/crypto/fips_prf_wolfssl.c b/src/crypto/fips_prf_wolfssl.c
new file mode 100644
index 000000000000..feb39db5a6d9
--- /dev/null
+++ b/src/crypto/fips_prf_wolfssl.c
@@ -0,0 +1,87 @@
+/*
+ * FIPS 186-2 PRF for libcrypto
+ * Copyright (c) 2004-2017, 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 <wolfssl/options.h>
+#include <wolfssl/wolfcrypt/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static void sha1_transform(u32 *state, const u8 data[64])
+{
+ wc_Sha sha;
+
+ os_memset(&sha, 0, sizeof(sha));
+ sha.digest[0] = state[0];
+ sha.digest[1] = state[1];
+ sha.digest[2] = state[2];
+ sha.digest[3] = state[3];
+ sha.digest[4] = state[4];
+ wc_ShaUpdate(&sha, data, 64);
+ state[0] = sha.digest[0];
+ state[1] = sha.digest[1];
+ state[2] = sha.digest[2];
+ state[3] = sha.digest[3];
+ state[4] = sha.digest[4];
+}
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+ u8 xkey[64];
+ u32 t[5], _t[5];
+ int i, j, m, k;
+ u8 *xpos = x;
+ u32 carry;
+
+ if (seed_len < sizeof(xkey))
+ os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+ else
+ seed_len = sizeof(xkey);
+
+ /* FIPS 186-2 + change notice 1 */
+
+ os_memcpy(xkey, seed, seed_len);
+ t[0] = 0x67452301;
+ t[1] = 0xEFCDAB89;
+ t[2] = 0x98BADCFE;
+ t[3] = 0x10325476;
+ t[4] = 0xC3D2E1F0;
+
+ m = xlen / 40;
+ for (j = 0; j < m; j++) {
+ /* XSEED_j = 0 */
+ for (i = 0; i < 2; i++) {
+ /* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+ /* w_i = G(t, XVAL) */
+ os_memcpy(_t, t, 20);
+ sha1_transform(_t, xkey);
+ WPA_PUT_BE32(xpos, _t[0]);
+ WPA_PUT_BE32(xpos + 4, _t[1]);
+ WPA_PUT_BE32(xpos + 8, _t[2]);
+ WPA_PUT_BE32(xpos + 12, _t[3]);
+ WPA_PUT_BE32(xpos + 16, _t[4]);
+
+ /* XKEY = (1 + XKEY + w_i) mod 2^b */
+ carry = 1;
+ for (k = 19; k >= 0; k--) {
+ carry += xkey[k] + xpos[k];
+ xkey[k] = carry & 0xff;
+ carry >>= 8;
+ }
+
+ xpos += 20;
+ }
+ /* x_j = w_0|w_1 */
+ }
+
+ return 0;
+}
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index d0d6a96af2bc..aff7d33f4ea4 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -140,17 +140,20 @@ int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
* @challenge: 8-octet Challenge (IN)
* @password_hash: 16-octet PasswordHash (IN)
* @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
*/
-void challenge_response(const u8 *challenge, const u8 *password_hash,
- u8 *response)
+int challenge_response(const u8 *challenge, const u8 *password_hash,
+ u8 *response)
{
u8 zpwd[7];
- des_encrypt(challenge, password_hash, response);
- des_encrypt(challenge, password_hash + 7, response + 8);
+
+ if (des_encrypt(challenge, password_hash, response) < 0 ||
+ des_encrypt(challenge, password_hash + 7, response + 8) < 0)
+ return -1;
zpwd[0] = password_hash[14];
zpwd[1] = password_hash[15];
os_memset(zpwd + 2, 0, 5);
- des_encrypt(challenge, zpwd, response + 16);
+ return des_encrypt(challenge, zpwd, response + 16);
}
@@ -175,9 +178,9 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
if (challenge_hash(peer_challenge, auth_challenge, username,
username_len, challenge) ||
- nt_password_hash(password, password_len, password_hash))
+ nt_password_hash(password, password_len, password_hash) ||
+ challenge_response(challenge, password_hash, response))
return -1;
- challenge_response(challenge, password_hash, response);
return 0;
}
@@ -202,9 +205,9 @@ int generate_nt_response_pwhash(const u8 *auth_challenge,
if (challenge_hash(peer_challenge, auth_challenge,
username, username_len,
- challenge))
+ challenge) ||
+ challenge_response(challenge, password_hash, response))
return -1;
- challenge_response(challenge, password_hash, response);
return 0;
}
@@ -304,9 +307,10 @@ int nt_challenge_response(const u8 *challenge, const u8 *password,
size_t password_len, u8 *response)
{
u8 password_hash[16];
- if (nt_password_hash(password, password_len, password_hash))
+
+ if (nt_password_hash(password, password_len, password_hash) ||
+ challenge_response(challenge, password_hash, response))
return -1;
- challenge_response(challenge, password_hash, response);
return 0;
}
@@ -487,12 +491,15 @@ int new_password_encrypted_with_old_nt_password_hash(
* @password_hash: 16-octer PasswordHash (IN)
* @block: 16-octet Block (IN)
* @cypher: 16-octer Cypher (OUT)
+ * Returns: 0 on success, -1 on failure
*/
-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
- const u8 *block, u8 *cypher)
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
+ const u8 *block, u8 *cypher)
{
- des_encrypt(password_hash, block, cypher);
- des_encrypt(password_hash + 8, block + 7, cypher + 8);
+ if (des_encrypt(password_hash, block, cypher) < 0 ||
+ des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0)
+ return -1;
+ return 0;
}
@@ -515,10 +522,10 @@ int old_nt_password_hash_encrypted_with_new_nt_password_hash(
if (nt_password_hash(old_password, old_password_len,
old_password_hash) ||
nt_password_hash(new_password, new_password_len,
- new_password_hash))
+ new_password_hash) ||
+ nt_password_hash_encrypted_with_block(old_password_hash,
+ new_password_hash,
+ encrypted_password_hash))
return -1;
- nt_password_hash_encrypted_with_block(old_password_hash,
- new_password_hash,
- encrypted_password_hash);
return 0;
}
diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h
index b5b5918e1a55..b8d55f05326c 100644
--- a/src/crypto/ms_funcs.h
+++ b/src/crypto/ms_funcs.h
@@ -31,8 +31,8 @@ int generate_authenticator_response_pwhash(
int nt_challenge_response(const u8 *challenge, const u8 *password,
size_t password_len, u8 *response);
-void challenge_response(const u8 *challenge, const u8 *password_hash,
- u8 *response);
+int challenge_response(const u8 *challenge, const u8 *password_hash,
+ u8 *response);
int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
const u8 *username, size_t username_len, u8 *challenge);
int nt_password_hash(const u8 *password, size_t password_len,
@@ -50,8 +50,8 @@ int __must_check new_password_encrypted_with_old_nt_password_hash(
const u8 *new_password, size_t new_password_len,
const u8 *old_password, size_t old_password_len,
u8 *encrypted_pw_block);
-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
- const u8 *block, u8 *cypher);
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
+ const u8 *block, u8 *cypher);
int old_nt_password_hash_encrypted_with_new_nt_password_hash(
const u8 *new_password, size_t new_password_len,
const u8 *old_password, size_t old_password_len,
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 3a86a93a46a8..c278d9cb9de9 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -54,7 +54,6 @@ static int random_fd = -1;
static unsigned int own_pool_ready = 0;
#define RANDOM_ENTROPY_SIZE 20
static char *random_entropy_file = NULL;
-static int random_entropy_file_read = 0;
#define MIN_COLLECT_ENTROPY 1000
static unsigned int entropy = 0;
@@ -66,6 +65,9 @@ static void random_write_entropy(void);
static u32 __ROL32(u32 x, u32 y)
{
+ if (y == 0)
+ return x;
+
return (x << (y & 31)) | (x >> (32 - (y & 31)));
}
@@ -354,7 +356,6 @@ static void random_read_entropy(void)
own_pool_ready = (u8) buf[0];
random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
- random_entropy_file_read = 1;
os_free(buf);
wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
"(own_pool_ready=%u)",
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index ffcba66af652..a4917070f196 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -53,7 +53,7 @@ By Steve Reid <sreid@sea-to-sky.net>
100% Public Domain
-----------------
-Modified 7/98
+Modified 7/98
By James H. Brown <jbrown@burgoyne.com>
Still 100% Public Domain
@@ -75,7 +75,7 @@ Since the file IO in main() reads 16K at a time, any file 8K or larger would
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
"a"s).
-I also changed the declaration of variables i & j in SHA1Update to
+I also changed the declaration of variables i & j in SHA1Update to
unsigned long from unsigned int for the same reason.
These changes should make no difference to any 32 bit implementations since
@@ -102,7 +102,7 @@ Still 100% public domain
Modified 4/01
By Saul Kravitz <Saul.Kravitz@celera.com>
Still 100% PD
-Modified to run on Compaq Alpha hardware.
+Modified to run on Compaq Alpha hardware.
-----------------
Modified 4/01
@@ -162,7 +162,7 @@ void SHAPrintContext(SHA1_CTX *context, char *msg)
{
printf("%s (%d,%d) %x %x %x %x %x\n",
msg,
- context->count[0], context->count[1],
+ context->count[0], context->count[1],
context->state[0],
context->state[1],
context->state[2],
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
index 86a548ee472d..ff1e2ba1686d 100644
--- a/src/crypto/sha256-internal.c
+++ b/src/crypto/sha256-internal.c
@@ -69,7 +69,7 @@ static const unsigned long K[64] = {
( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
#define Ch(x,y,z) (z ^ (x & (y ^ z)))
-#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
#define S(x, n) RORc((x), (n))
#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
@@ -100,7 +100,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf)
for (i = 16; i < 64; i++) {
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
W[i - 16];
- }
+ }
/* Compress */
#define RND(a,b,c,d,e,f,g,h,i) \
@@ -111,7 +111,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf)
for (i = 0; i < 64; ++i) {
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
- t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
}
diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c
index e7509ce41aba..af7d954d8a44 100644
--- a/src/crypto/sha256-kdf.c
+++ b/src/crypto/sha256-kdf.c
@@ -1,6 +1,6 @@
/*
- * HMAC-SHA256 KDF (RFC 5295)
- * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -16,7 +16,8 @@
* hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
* @secret: Key for KDF
* @secret_len: Length of the key in bytes
- * @label: A unique label for each purpose of the KDF
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ * RFC 5869 HKDF-Expand() with arbitrary seed (= info)
* @seed: Seed value to bind into the key
* @seed_len: Length of the seed
* @out: Buffer for the generated pseudo-random key
@@ -24,7 +25,9 @@
* Returns: 0 on success, -1 on failure.
*
* This function is used to derive new, cryptographically separate keys from a
- * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2.
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
*/
int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
const char *label, const u8 *seed, size_t seed_len,
@@ -38,8 +41,13 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
addr[0] = T;
len[0] = SHA256_MAC_LEN;
- addr[1] = (const unsigned char *) label;
- len[1] = os_strlen(label) + 1;
+ if (label) {
+ addr[1] = (const unsigned char *) label;
+ len[1] = os_strlen(label) + 1;
+ } else {
+ addr[1] = (const u8 *) "";
+ len[1] = 0;
+ }
addr[2] = seed;
len[2] = seed_len;
addr[3] = &iter;
diff --git a/src/crypto/sha384-kdf.c b/src/crypto/sha384-kdf.c
new file mode 100644
index 000000000000..1d196279086e
--- /dev/null
+++ b/src/crypto/sha384-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869)
+ * Copyright (c) 2014-2017, 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 "sha384.h"
+
+
+/**
+ * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ * RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen)
+{
+ u8 T[SHA384_MAC_LEN];
+ u8 iter = 1;
+ const unsigned char *addr[4];
+ size_t len[4];
+ size_t pos, clen;
+
+ addr[0] = T;
+ len[0] = SHA384_MAC_LEN;
+ if (label) {
+ addr[1] = (const unsigned char *) label;
+ len[1] = os_strlen(label) + 1;
+ } else {
+ addr[1] = (const u8 *) "";
+ len[1] = 0;
+ }
+ addr[2] = seed;
+ len[2] = seed_len;
+ addr[3] = &iter;
+ len[3] = 1;
+
+ if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+ return -1;
+
+ pos = 0;
+ for (;;) {
+ clen = outlen - pos;
+ if (clen > SHA384_MAC_LEN)
+ clen = SHA384_MAC_LEN;
+ os_memcpy(out + pos, T, clen);
+ pos += clen;
+
+ if (pos == outlen)
+ break;
+
+ if (iter == 255) {
+ os_memset(out, 0, outlen);
+ os_memset(T, 0, SHA384_MAC_LEN);
+ return -1;
+ }
+ iter++;
+
+ if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0)
+ {
+ os_memset(out, 0, outlen);
+ os_memset(T, 0, SHA384_MAC_LEN);
+ return -1;
+ }
+ }
+
+ os_memset(T, 0, SHA384_MAC_LEN);
+ return 0;
+}
diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c
index 653920ba283d..03e3cb353a3d 100644
--- a/src/crypto/sha384-prf.c
+++ b/src/crypto/sha384-prf.c
@@ -1,6 +1,6 @@
/*
* SHA384-based KDF (IEEE 802.11ac)
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -22,14 +22,16 @@
* @data_len: Length of the data
* @buf: Buffer for the generated pseudo-random key
* @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
*
* This function is used to derive new, cryptographically separate keys from a
* given key.
*/
-void sha384_prf(const u8 *key, size_t key_len, const char *label,
- const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
{
- sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+ return sha384_prf_bits(key, key_len, label, data, data_len, buf,
+ buf_len * 8);
}
@@ -42,15 +44,16 @@ void sha384_prf(const u8 *key, size_t key_len, const char *label,
* @data_len: Length of the data
* @buf: Buffer for the generated pseudo-random key
* @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
*
* This function is used to derive new, cryptographically separate keys from a
* given key. If the requested buf_len is not divisible by eight, the least
* significant 1-7 bits of the last octet in the output are not part of the
* requested output.
*/
-void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
- const u8 *data, size_t data_len, u8 *buf,
- size_t buf_len_bits)
+int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits)
{
u16 counter = 1;
size_t pos, plen;
@@ -75,11 +78,14 @@ void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
plen = buf_len - pos;
WPA_PUT_LE16(counter_le, counter);
if (plen >= SHA384_MAC_LEN) {
- hmac_sha384_vector(key, key_len, 4, addr, len,
- &buf[pos]);
+ if (hmac_sha384_vector(key, key_len, 4, addr, len,
+ &buf[pos]) < 0)
+ return -1;
pos += SHA384_MAC_LEN;
} else {
- hmac_sha384_vector(key, key_len, 4, addr, len, hash);
+ if (hmac_sha384_vector(key, key_len, 4, addr, len,
+ hash) < 0)
+ return -1;
os_memcpy(&buf[pos], hash, plen);
pos += plen;
break;
@@ -97,4 +103,6 @@ void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
}
os_memset(hash, 0, sizeof(hash));
+
+ return 0;
}
diff --git a/src/crypto/sha384.c b/src/crypto/sha384.c
new file mode 100644
index 000000000000..ee136ce99b7e
--- /dev/null
+++ b/src/crypto/sha384.c
@@ -0,0 +1,104 @@
+/*
+ * SHA-384 hash implementation and interface functions
+ * Copyright (c) 2003-2017, 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 "sha384.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha384_vector - HMAC-SHA384 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (48 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[48];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return -1;
+ }
+
+ /* if key is longer than 128 bytes reset it to key = SHA384(key) */
+ if (key_len > 128) {
+ if (sha384_vector(1, &key, &key_len, tk) < 0)
+ return -1;
+ key = tk;
+ key_len = 48;
+ }
+
+ /* the HMAC_SHA384 transform looks like:
+ *
+ * SHA384(K XOR opad, SHA384(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 128 times
+ * opad is the byte 0x5c repeated 128 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 128; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA384 */
+ _addr[0] = k_pad;
+ _len[0] = 128;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ if (sha384_vector(1 + num_elem, _addr, _len, mac) < 0)
+ return -1;
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 128; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA384 */
+ _addr[0] = k_pad;
+ _len[0] = 128;
+ _addr[1] = mac;
+ _len[1] = SHA384_MAC_LEN;
+ return sha384_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha384 - HMAC-SHA384 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (48 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h
index 3deafa59ec29..2241425385c3 100644
--- a/src/crypto/sha384.h
+++ b/src/crypto/sha384.h
@@ -1,6 +1,6 @@
/*
* SHA384 hash implementation and interface functions
- * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -15,10 +15,13 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac);
int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
size_t data_len, u8 *mac);
-void sha384_prf(const u8 *key, size_t key_len, const char *label,
- const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
-void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
- const u8 *data, size_t data_len, u8 *buf,
- size_t buf_len_bits);
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits);
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
#endif /* SHA384_H */
diff --git a/src/crypto/sha512-kdf.c b/src/crypto/sha512-kdf.c
new file mode 100644
index 000000000000..8b71f9b0e4f9
--- /dev/null
+++ b/src/crypto/sha512-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512.h"
+
+
+/**
+ * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ * RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen)
+{
+ u8 T[SHA512_MAC_LEN];
+ u8 iter = 1;
+ const unsigned char *addr[4];
+ size_t len[4];
+ size_t pos, clen;
+
+ addr[0] = T;
+ len[0] = SHA512_MAC_LEN;
+ if (label) {
+ addr[1] = (const unsigned char *) label;
+ len[1] = os_strlen(label) + 1;
+ } else {
+ addr[1] = (const u8 *) "";
+ len[1] = 0;
+ }
+ addr[2] = seed;
+ len[2] = seed_len;
+ addr[3] = &iter;
+ len[3] = 1;
+
+ if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+ return -1;
+
+ pos = 0;
+ for (;;) {
+ clen = outlen - pos;
+ if (clen > SHA512_MAC_LEN)
+ clen = SHA512_MAC_LEN;
+ os_memcpy(out + pos, T, clen);
+ pos += clen;
+
+ if (pos == outlen)
+ break;
+
+ if (iter == 255) {
+ os_memset(out, 0, outlen);
+ os_memset(T, 0, SHA512_MAC_LEN);
+ return -1;
+ }
+ iter++;
+
+ if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0)
+ {
+ os_memset(out, 0, outlen);
+ os_memset(T, 0, SHA512_MAC_LEN);
+ return -1;
+ }
+ }
+
+ os_memset(T, 0, SHA512_MAC_LEN);
+ return 0;
+}
diff --git a/src/crypto/sha512-prf.c b/src/crypto/sha512-prf.c
new file mode 100644
index 000000000000..3b2ad889d6ef
--- /dev/null
+++ b/src/crypto/sha512-prf.c
@@ -0,0 +1,108 @@
+/*
+ * SHA512-based KDF (IEEE 802.11ac)
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+/**
+ * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ return sha512_prf_bits(key, key_len, label, data, data_len, buf,
+ buf_len * 8);
+}
+
+
+/**
+ * sha512_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits)
+{
+ u16 counter = 1;
+ size_t pos, plen;
+ u8 hash[SHA512_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 counter_le[2], length_le[2];
+ size_t buf_len = (buf_len_bits + 7) / 8;
+
+ addr[0] = counter_le;
+ len[0] = 2;
+ addr[1] = (u8 *) label;
+ len[1] = os_strlen(label);
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = length_le;
+ len[3] = sizeof(length_le);
+
+ WPA_PUT_LE16(length_le, buf_len_bits);
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA512_MAC_LEN) {
+ if (hmac_sha512_vector(key, key_len, 4, addr, len,
+ &buf[pos]) < 0)
+ return -1;
+ pos += SHA512_MAC_LEN;
+ } else {
+ if (hmac_sha512_vector(key, key_len, 4, addr, len,
+ hash) < 0)
+ return -1;
+ os_memcpy(&buf[pos], hash, plen);
+ pos += plen;
+ break;
+ }
+ counter++;
+ }
+
+ /*
+ * Mask out unused bits in the last octet if it does not use all the
+ * bits.
+ */
+ if (buf_len_bits % 8) {
+ u8 mask = 0xff << (8 - buf_len_bits % 8);
+ buf[pos - 1] &= mask;
+ }
+
+ os_memset(hash, 0, sizeof(hash));
+
+ return 0;
+}
diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h
new file mode 100644
index 000000000000..8e64c8b116fa
--- /dev/null
+++ b/src/crypto/sha512.h
@@ -0,0 +1,27 @@
+/*
+ * SHA512 hash implementation and interface functions
+ * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA512_H
+#define SHA512_H
+
+#define SHA512_MAC_LEN 64
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits);
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
+
+#endif /* SHA512_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 11d504a97fc0..481b34681d7b 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -41,6 +41,7 @@ enum tls_fail_reason {
TLS_FAIL_SERVER_CHAIN_PROBE = 8,
TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
TLS_FAIL_DOMAIN_MISMATCH = 10,
+ TLS_FAIL_INSUFFICIENT_KEY_LEN = 11,
};
@@ -63,6 +64,7 @@ union tls_event_data {
size_t hash_len;
const char *altsubject[TLS_MAX_ALT_SUBJECT];
int num_altsubject;
+ const char *serial_num;
} peer_cert;
struct {
@@ -80,6 +82,7 @@ struct tls_config {
int cert_in_cb;
const char *openssl_ciphers;
unsigned int tls_session_lifetime;
+ unsigned int tls_flags;
void (*event_cb)(void *ctx, enum tls_event ev,
union tls_event_data *data);
@@ -97,6 +100,9 @@ struct tls_config {
#define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
#define TLS_CONN_EXT_CERT_CHECK BIT(9)
#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
+#define TLS_CONN_SUITEB BIT(11)
+#define TLS_CONN_SUITEB_NO_ECDH BIT(12)
+#define TLS_CONN_DISABLE_TLSv1_3 BIT(13)
/**
* struct tls_connection_params - Parameters for TLS connection
@@ -248,6 +254,18 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
/**
+ * tls_connection_peer_serial_num - Fetch peer certificate serial number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Allocated string buffer containing the peer certificate serial
+ * number or %NULL on error.
+ *
+ * The caller is responsible for freeing the returned buffer with os_free().
+ */
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn);
+
+/**
* tls_connection_shutdown - Shutdown TLS connection
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index 200f0eda931a..36dafd2603f0 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -1,6 +1,6 @@
/*
* SSL/TLS interface functions for GnuTLS
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -295,6 +295,14 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* TODO */
+ return NULL;
+}
+
+
int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
{
struct tls_global *global = ssl_ctx;
@@ -346,6 +354,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
const struct tls_connection_params *params)
{
int ret;
+ const char *err;
+ char prio_buf[100];
+ const char *prio = NULL;
if (conn == NULL || params == NULL)
return -1;
@@ -397,12 +408,60 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
conn->flags = params->flags;
+ if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 |
+ TLS_CONN_DISABLE_TLSv1_1 |
+ TLS_CONN_DISABLE_TLSv1_2)) {
+ os_snprintf(prio_buf, sizeof(prio_buf),
+ "NORMAL:-VERS-SSL3.0%s%s%s",
+ params->flags & TLS_CONN_DISABLE_TLSv1_0 ?
+ ":-VERS-TLS1.0" : "",
+ params->flags & TLS_CONN_DISABLE_TLSv1_1 ?
+ ":-VERS-TLS1.1" : "",
+ params->flags & TLS_CONN_DISABLE_TLSv1_2 ?
+ ":-VERS-TLS1.2" : "");
+ prio = prio_buf;
+ }
+
if (params->openssl_ciphers) {
- wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
- return -1;
+ if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
+ prio = "SUITEB128";
+ } else if (os_strcmp(params->openssl_ciphers,
+ "SUITEB192") == 0) {
+ prio = "SUITEB192";
+ } else if ((params->flags & TLS_CONN_SUITEB) &&
+ os_strcmp(params->openssl_ciphers,
+ "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+ } else if (os_strcmp(params->openssl_ciphers,
+ "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+ } else if (os_strcmp(params->openssl_ciphers,
+ "DHE-RSA-AES256-GCM-SHA384") == 0) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
+ } else if (os_strcmp(params->openssl_ciphers,
+ "ECDHE-ECDSA-AES256-GCM-SHA384") == 0) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+ } else {
+ wpa_printf(MSG_INFO,
+ "GnuTLS: openssl_ciphers not supported");
+ return -1;
+ }
+ } else if (params->flags & TLS_CONN_SUITEB) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
}
- /* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
+ if (prio) {
+ wpa_printf(MSG_DEBUG, "GnuTLS: Set priority string: %s", prio);
+ ret = gnutls_priority_set_direct(conn->session, prio, &err);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ "GnuTLS: Priority string failure at '%s'",
+ err);
+ return -1;
+ }
+ }
+
+ /* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
* to force peer validation(?) */
if (params->ca_cert) {
@@ -425,6 +484,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
gnutls_strerror(ret));
return -1;
}
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Successfully read CA cert '%s' in PEM format",
+ params->ca_cert);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Successfully read CA cert '%s' in DER format",
+ params->ca_cert);
}
} else if (params->ca_cert_blob) {
gnutls_datum_t ca;
@@ -472,6 +538,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
}
if (params->client_cert && params->private_key) {
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Try to parse client cert '%s' and key '%s' in DER format",
+ params->client_cert, params->private_key);
#if GNUTLS_VERSION_NUMBER >= 0x03010b
ret = gnutls_certificate_set_x509_key_file2(
conn->xcred, params->client_cert, params->private_key,
@@ -483,8 +552,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
GNUTLS_X509_FMT_DER);
#endif
if (ret < 0) {
- wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
- "in DER format: %s", gnutls_strerror(ret));
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Failed to read client cert/key in DER format (%s) - try in PEM format",
+ gnutls_strerror(ret));
#if GNUTLS_VERSION_NUMBER >= 0x03010b
ret = gnutls_certificate_set_x509_key_file2(
conn->xcred, params->client_cert,
@@ -501,11 +571,19 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
gnutls_strerror(ret));
return ret;
}
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Successfully read client cert/key in PEM format");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Successfully read client cert/key in DER format");
}
} else if (params->private_key) {
int pkcs12_ok = 0;
#ifdef PKCS12_FUNCS
/* Try to load in PKCS#12 format */
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Try to parse client cert/key '%s'in PKCS#12 DER format",
+ params->private_key);
ret = gnutls_certificate_set_x509_simple_pkcs12_file(
conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
params->private_key_passwd);
@@ -1333,6 +1411,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
ret = gnutls_handshake(conn->session);
if (ret < 0) {
gnutls_alert_description_t alert;
+ union tls_event_data ev;
switch (ret) {
case GNUTLS_E_AGAIN:
@@ -1343,14 +1422,29 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
conn->push_buf = wpabuf_alloc(0);
}
break;
+ case GNUTLS_E_DH_PRIME_UNACCEPTABLE:
+ wpa_printf(MSG_DEBUG, "GnuTLS: Unacceptable DH prime");
+ if (conn->global->event_cb) {
+ os_memset(&ev, 0, sizeof(ev));
+ ev.alert.is_local = 1;
+ ev.alert.type = "fatal";
+ ev.alert.description = "insufficient security";
+ conn->global->event_cb(conn->global->cb_ctx,
+ TLS_ALERT, &ev);
+ }
+ /*
+ * Could send a TLS Alert to the server, but for now,
+ * simply terminate handshake.
+ */
+ conn->failed++;
+ conn->write_alerts++;
+ break;
case GNUTLS_E_FATAL_ALERT_RECEIVED:
alert = gnutls_alert_get(conn->session);
wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
__func__, gnutls_alert_get_name(alert));
conn->read_alerts++;
if (conn->global->event_cb != NULL) {
- union tls_event_data ev;
-
os_memset(&ev, 0, sizeof(ev));
ev.alert.is_local = 0;
ev.alert.type = gnutls_alert_get_name(alert);
@@ -1501,16 +1595,53 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
- /* TODO */
- return -1;
+ gnutls_protocol_t ver;
+
+ ver = gnutls_protocol_get_version(conn->session);
+ if (ver == GNUTLS_TLS1_0)
+ os_strlcpy(buf, "TLSv1", buflen);
+ else if (ver == GNUTLS_TLS1_1)
+ os_strlcpy(buf, "TLSv1.1", buflen);
+ else if (ver == GNUTLS_TLS1_2)
+ os_strlcpy(buf, "TLSv1.2", buflen);
+ else
+ return -1;
+ return 0;
}
int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
- /* TODO */
- buf[0] = '\0';
+ gnutls_cipher_algorithm_t cipher;
+ gnutls_kx_algorithm_t kx;
+ gnutls_mac_algorithm_t mac;
+ const char *kx_str, *cipher_str, *mac_str;
+ int res;
+
+ cipher = gnutls_cipher_get(conn->session);
+ cipher_str = gnutls_cipher_get_name(cipher);
+ if (!cipher_str)
+ cipher_str = "";
+
+ kx = gnutls_kx_get(conn->session);
+ kx_str = gnutls_kx_get_name(kx);
+ if (!kx_str)
+ kx_str = "";
+
+ mac = gnutls_mac_get(conn->session);
+ mac_str = gnutls_mac_get_name(mac);
+ if (!mac_str)
+ mac_str = "";
+
+ if (kx == GNUTLS_KX_RSA)
+ res = os_snprintf(buf, buflen, "%s-%s", cipher_str, mac_str);
+ else
+ res = os_snprintf(buf, buflen, "%s-%s-%s",
+ kx_str, cipher_str, mac_str);
+ if (os_snprintf_error(buflen, res))
+ return -1;
+
return 0;
}
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index c7cb5ded331f..d289c9442ceb 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -177,6 +177,14 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* TODO */
+ return NULL;
+}
+
+
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index dd5681e9ca3c..5d0c6bda1547 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -45,6 +45,13 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return NULL;
+}
+
+
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
{
return -1;
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 23ac64b48cd9..0d5ebda699f0 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -59,7 +59,8 @@ typedef int stack_index_t;
#endif /* SSL_set_tlsext_status_type */
#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
- defined(LIBRESSL_VERSION_NUMBER)) && \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \
!defined(BORINGSSL_API_VERSION)
/*
* SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
@@ -103,6 +104,21 @@ static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#ifdef CONFIG_SUITEB
+static int RSA_bits(const RSA *r)
+{
+ return BN_num_bits(r->n);
+}
+#endif /* CONFIG_SUITEB */
+
+
+static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+ return ASN1_STRING_data((ASN1_STRING *) x);
+}
+#endif
+
#ifdef ANDROID
#include <openssl/pem.h>
#include <keystore/keystore_get.h>
@@ -222,6 +238,8 @@ struct tls_connection {
unsigned int server_cert_only:1;
unsigned int invalid_hb_used:1;
unsigned int success_data:1;
+ unsigned int client_hello_generated:1;
+ unsigned int server:1;
u8 srv_cert_hash[32];
@@ -233,6 +251,9 @@ struct tls_connection {
unsigned char client_random[SSL3_RANDOM_SIZE];
unsigned char server_random[SSL3_RANDOM_SIZE];
+
+ u16 cipher_suite;
+ int server_dh_prime_len;
};
@@ -919,7 +940,9 @@ void * tls_init(const struct tls_config *conf)
}
#endif /* OPENSSL_FIPS */
#endif /* CONFIG_FIPS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
SSL_load_error_strings();
SSL_library_init();
#ifndef OPENSSL_NO_SHA256
@@ -972,6 +995,14 @@ void * tls_init(const struct tls_config *conf)
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
+#ifdef SSL_MODE_NO_AUTO_CHAIN
+ /* Number of deployed use cases assume the default OpenSSL behavior of
+ * auto chaining the local certificate is in use. BoringSSL removed this
+ * functionality by default, so we need to restore it here to avoid
+ * breaking existing use cases. */
+ SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN);
+#endif /* SSL_MODE_NO_AUTO_CHAIN */
+
SSL_CTX_set_info_callback(ssl, ssl_info_cb);
SSL_CTX_set_app_data(ssl, context);
if (data->tls_session_lifetime > 0) {
@@ -999,8 +1030,10 @@ void * tls_init(const struct tls_config *conf)
#ifndef OPENSSL_NO_ENGINE
wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_load_ENGINE_strings();
ENGINE_load_dynamic();
+#endif /* OPENSSL_VERSION_NUMBER */
if (conf &&
(conf->opensc_engine_path || conf->pkcs11_engine_path ||
@@ -1017,7 +1050,7 @@ void * tls_init(const struct tls_config *conf)
if (conf && conf->openssl_ciphers)
ciphers = conf->openssl_ciphers;
else
- ciphers = "DEFAULT:!EXP:!LOW";
+ ciphers = TLS_DEFAULT_CIPHERS;
if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
wpa_printf(MSG_ERROR,
"OpenSSL: Failed to set cipher string '%s'",
@@ -1043,7 +1076,9 @@ void tls_deinit(void *ssl_ctx)
tls_openssl_ref_count--;
if (tls_openssl_ref_count == 0) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif /* OPENSSL_NO_ENGINE */
@@ -1296,6 +1331,95 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf,
}
+#ifdef CONFIG_SUITEB
+
+static void check_server_hello(struct tls_connection *conn,
+ const u8 *pos, const u8 *end)
+{
+ size_t payload_len, id_len;
+
+ /*
+ * Parse ServerHello to get the selected cipher suite since OpenSSL does
+ * not make it cleanly available during handshake and we need to know
+ * whether DHE was selected.
+ */
+
+ if (end - pos < 3)
+ return;
+ payload_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) < payload_len)
+ return;
+ end = pos + payload_len;
+
+ /* Skip Version and Random */
+ if (end - pos < 2 + SSL3_RANDOM_SIZE)
+ return;
+ pos += 2 + SSL3_RANDOM_SIZE;
+
+ /* Skip Session ID */
+ if (end - pos < 1)
+ return;
+ id_len = *pos++;
+ if ((size_t) (end - pos) < id_len)
+ return;
+ pos += id_len;
+
+ if (end - pos < 2)
+ return;
+ conn->cipher_suite = WPA_GET_BE16(pos);
+ wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x",
+ conn->cipher_suite);
+}
+
+
+static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn,
+ const u8 *pos, const u8 *end)
+{
+ size_t payload_len;
+ u16 dh_len;
+ BIGNUM *p;
+ int bits;
+
+ if (!(conn->flags & TLS_CONN_SUITEB))
+ return;
+
+ /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
+ if (conn->cipher_suite != 0x9f)
+ return;
+
+ if (end - pos < 3)
+ return;
+ payload_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) < payload_len)
+ return;
+ end = pos + payload_len;
+
+ if (end - pos < 2)
+ return;
+ dh_len = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if ((size_t) (end - pos) < dh_len)
+ return;
+ p = BN_bin2bn(pos, dh_len, NULL);
+ if (!p)
+ return;
+
+ bits = BN_num_bits(p);
+ BN_free(p);
+
+ conn->server_dh_prime_len = bits;
+ wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits",
+ conn->server_dh_prime_len);
+}
+
+#endif /* CONFIG_SUITEB */
+
+
static void tls_msg_cb(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl, void *arg)
{
@@ -1322,6 +1446,18 @@ static void tls_msg_cb(int write_p, int version, int content_type,
conn->invalid_hb_used = 1;
}
}
+
+#ifdef CONFIG_SUITEB
+ /*
+ * Need to parse these handshake messages to be able to check DH prime
+ * length since OpenSSL does not expose the new cipher suite and DH
+ * parameters during handshake (e.g., for cert_cb() callback).
+ */
+ if (content_type == 22 && pos && len > 0 && pos[0] == 2)
+ check_server_hello(conn, pos + 1, pos + len);
+ if (content_type == 22 && pos && len > 0 && pos[0] == 12)
+ check_server_key_exchange(ssl, conn, pos + 1, pos + len);
+#endif /* CONFIG_SUITEB */
}
@@ -1410,6 +1546,31 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ ASN1_INTEGER *ser;
+ char *serial_num;
+ size_t len;
+
+ if (!conn->peer_cert)
+ return NULL;
+
+ ser = X509_get_serialNumber(conn->peer_cert);
+ if (!ser)
+ return NULL;
+
+ len = ASN1_STRING_length(ser) * 2 + 1;
+ serial_num = os_malloc(len);
+ if (!serial_num)
+ return NULL;
+ wpa_snprintf_hex_uppercase(serial_num, len,
+ ASN1_STRING_get0_data(ser),
+ ASN1_STRING_length(ser));
+ return serial_num;
+}
+
+
int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
{
if (conn == NULL)
@@ -1694,6 +1855,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
GENERAL_NAME *gen;
void *ext;
stack_index_t i;
+ ASN1_INTEGER *ser;
+ char serial_num[128];
#ifdef CONFIG_SHA256
u8 hash[32];
#endif /* CONFIG_SHA256 */
@@ -1722,6 +1885,14 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
ev.peer_cert.depth = depth;
ev.peer_cert.subject = subject;
+ ser = X509_get_serialNumber(err_cert);
+ if (ser) {
+ wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num),
+ ASN1_STRING_get0_data(ser),
+ ASN1_STRING_length(ser));
+ ev.peer_cert.serial_num = serial_num;
+ }
+
ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
char *pos;
@@ -1916,6 +2087,37 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
TLS_FAIL_SERVER_CHAIN_PROBE);
}
+#ifdef CONFIG_SUITEB
+ if (conn->flags & TLS_CONN_SUITEB) {
+ EVP_PKEY *pk;
+ RSA *rsa;
+ int len = -1;
+
+ pk = X509_get_pubkey(err_cert);
+ if (pk) {
+ rsa = EVP_PKEY_get1_RSA(pk);
+ if (rsa) {
+ len = RSA_bits(rsa);
+ RSA_free(rsa);
+ }
+ EVP_PKEY_free(pk);
+ }
+
+ if (len >= 0) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: RSA modulus size: %d bits", len);
+ if (len < 3072) {
+ preverify_ok = 0;
+ openssl_tls_fail_event(
+ conn, err_cert, err,
+ depth, buf,
+ "Insufficient RSA modulus size",
+ TLS_FAIL_INSUFFICIENT_KEY_LEN);
+ }
+ }
+ }
+#endif /* CONFIG_SUITEB */
+
#ifdef OPENSSL_IS_BORINGSSL
if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
preverify_ok) {
@@ -2249,15 +2451,48 @@ static int tls_connection_set_subject_match(struct tls_connection *conn,
}
-static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
+#ifdef CONFIG_SUITEB
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+static int suiteb_cert_cb(SSL *ssl, void *arg)
+{
+ struct tls_connection *conn = arg;
+
+ /*
+ * This cert_cb() is not really the best location for doing a
+ * constraint check for the ServerKeyExchange message, but this seems to
+ * be the only place where the current OpenSSL sequence can be
+ * terminated cleanly with an TLS alert going out to the server.
+ */
+
+ if (!(conn->flags & TLS_CONN_SUITEB))
+ return 1;
+
+ /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
+ if (conn->cipher_suite != 0x9f)
+ return 1;
+
+ if (conn->server_dh_prime_len >= 3072)
+ return 1;
+
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake",
+ conn->server_dh_prime_len);
+ return 0;
+}
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* CONFIG_SUITEB */
+
+
+static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
+ const char *openssl_ciphers)
{
+ SSL *ssl = conn->ssl;
+
#ifdef SSL_OP_NO_TICKET
if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
SSL_set_options(ssl, SSL_OP_NO_TICKET);
-#ifdef SSL_clear_options
else
SSL_clear_options(ssl, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
#endif /* SSL_OP_NO_TICKET */
#ifdef SSL_OP_NO_TLSv1
@@ -2278,6 +2513,118 @@ static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
else
SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
#endif /* SSL_OP_NO_TLSv1_2 */
+#ifdef SSL_OP_NO_TLSv1_3
+ if (flags & TLS_CONN_DISABLE_TLSv1_3)
+ SSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
+ else
+ SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3);
+#endif /* SSL_OP_NO_TLSv1_3 */
+#ifdef CONFIG_SUITEB
+#ifdef OPENSSL_IS_BORINGSSL
+ /* Start with defaults from BoringSSL */
+ SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0);
+#endif /* OPENSSL_IS_BORINGSSL */
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (flags & TLS_CONN_SUITEB_NO_ECDH) {
+ const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
+
+ if (openssl_ciphers) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Override ciphers for Suite B (no ECDH): %s",
+ openssl_ciphers);
+ ciphers = openssl_ciphers;
+ }
+ if (SSL_set_cipher_list(ssl, ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B ciphers");
+ return -1;
+ }
+ } else if (flags & TLS_CONN_SUITEB) {
+ EC_KEY *ecdh;
+ const char *ciphers =
+ "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
+ int nid[1] = { NID_secp384r1 };
+
+ if (openssl_ciphers) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Override ciphers for Suite B: %s",
+ openssl_ciphers);
+ ciphers = openssl_ciphers;
+ }
+ if (SSL_set_cipher_list(ssl, ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B ciphers");
+ return -1;
+ }
+
+ if (SSL_set1_curves(ssl, nid, 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B curves");
+ return -1;
+ }
+
+ ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
+ if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) {
+ EC_KEY_free(ecdh);
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set ECDH parameter");
+ return -1;
+ }
+ EC_KEY_free(ecdh);
+ }
+ if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+#ifdef OPENSSL_IS_BORINGSSL
+ uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 };
+
+ if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
+ 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B sigalgs");
+ return -1;
+ }
+#else /* OPENSSL_IS_BORINGSSL */
+ /* ECDSA+SHA384 if need to add EC support here */
+ if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B sigalgs");
+ return -1;
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+
+ SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+ SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+ SSL_set_cert_cb(ssl, suiteb_cert_cb, conn);
+ }
+#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */
+ if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Suite B RSA case not supported with this OpenSSL version");
+ return -1;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
+ uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 };
+ int nid[1] = { NID_secp384r1 };
+
+ if (SSL_set1_curves(ssl, nid, 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B curves");
+ return -1;
+ }
+
+ if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
+ 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B sigalgs");
+ return -1;
+ }
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+#endif /* CONFIG_SUITEB */
+
+ return 0;
}
@@ -2301,7 +2648,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
}
- tls_set_conn_flags(conn->ssl, flags);
+ if (tls_set_conn_flags(conn, flags, NULL) < 0)
+ return -1;
conn->flags = flags;
SSL_set_accept_state(conn->ssl);
@@ -2334,7 +2682,7 @@ static int tls_connection_client_cert(struct tls_connection *conn,
return 0;
#ifdef PKCS12_FUNCS
-#if OPENSSL_VERSION_NUMBER < 0x10002000L
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
/*
* Clear previously set extra chain certificates, if any, from PKCS#12
* processing in tls_parse_pkcs12() to allow OpenSSL to build a new
@@ -2365,13 +2713,24 @@ static int tls_connection_client_cert(struct tls_connection *conn,
int ret = -1;
if (bio) {
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
- BIO_free(bio);
}
if (x509) {
if (SSL_use_certificate(conn->ssl, x509) == 1)
ret = 0;
X509_free(x509);
}
+
+ /* Read additional certificates into the chain. */
+ while (bio) {
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (x509) {
+ /* Takes ownership of x509 */
+ SSL_add0_chain_cert(conn->ssl, x509);
+ } else {
+ BIO_free(bio);
+ bio = NULL;
+ }
+ }
return ret;
}
#endif /* ANDROID */
@@ -2430,16 +2789,6 @@ static int tls_global_client_cert(struct tls_data *data,
}
-static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
-{
- if (password == NULL) {
- return 0;
- }
- os_strlcpy(buf, (char *) password, size);
- return os_strlen(buf);
-}
-
-
#ifdef PKCS12_FUNCS
static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
const char *passwd)
@@ -2758,6 +3107,64 @@ static int tls_connection_engine_private_key(struct tls_connection *conn)
}
+#ifndef OPENSSL_NO_STDIO
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+ if (!password)
+ return 0;
+ os_strlcpy(buf, (const char *) password, size);
+ return os_strlen(buf);
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
+static int tls_use_private_key_file(struct tls_data *data, SSL *ssl,
+ const char *private_key,
+ const char *private_key_passwd)
+{
+#ifndef OPENSSL_NO_STDIO
+ BIO *bio;
+ EVP_PKEY *pkey;
+ int ret;
+
+ /* First try ASN.1 (DER). */
+ bio = BIO_new_file(private_key, "r");
+ if (!bio)
+ return -1;
+ pkey = d2i_PrivateKey_bio(bio, NULL);
+ BIO_free(bio);
+
+ if (pkey) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__);
+ } else {
+ /* Try PEM with the provided password. */
+ bio = BIO_new_file(private_key, "r");
+ if (!bio)
+ return -1;
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb,
+ (void *) private_key_passwd);
+ BIO_free(bio);
+ if (!pkey)
+ return -1;
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__);
+ /* Clear errors from the previous failed load. */
+ ERR_clear_error();
+ }
+
+ if (ssl)
+ ret = SSL_use_PrivateKey(ssl, pkey);
+ else
+ ret = SSL_CTX_use_PrivateKey(data->ssl, pkey);
+
+ EVP_PKEY_free(pkey);
+ return ret == 1 ? 0 : -1;
+#else /* OPENSSL_NO_STDIO */
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+ return -1;
+#endif /* OPENSSL_NO_STDIO */
+}
+
+
static int tls_connection_private_key(struct tls_data *data,
struct tls_connection *conn,
const char *private_key,
@@ -2765,23 +3172,11 @@ static int tls_connection_private_key(struct tls_data *data,
const u8 *private_key_blob,
size_t private_key_blob_len)
{
- SSL_CTX *ssl_ctx = data->ssl;
- char *passwd;
int ok;
if (private_key == NULL && private_key_blob == NULL)
return 0;
- if (private_key_passwd) {
- passwd = os_strdup(private_key_passwd);
- if (passwd == NULL)
- return -1;
- } else
- passwd = NULL;
-
- SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
- SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
-
ok = 0;
while (private_key_blob) {
if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
@@ -2812,7 +3207,8 @@ static int tls_connection_private_key(struct tls_data *data,
}
if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
- private_key_blob_len, passwd) == 0) {
+ private_key_blob_len,
+ private_key_passwd) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
"OK");
ok = 1;
@@ -2823,29 +3219,14 @@ static int tls_connection_private_key(struct tls_data *data,
}
while (!ok && private_key) {
-#ifndef OPENSSL_NO_STDIO
- if (SSL_use_PrivateKey_file(conn->ssl, private_key,
- SSL_FILETYPE_ASN1) == 1) {
- wpa_printf(MSG_DEBUG, "OpenSSL: "
- "SSL_use_PrivateKey_File (DER) --> OK");
+ if (tls_use_private_key_file(data, conn->ssl, private_key,
+ private_key_passwd) == 0) {
ok = 1;
break;
}
- if (SSL_use_PrivateKey_file(conn->ssl, private_key,
- SSL_FILETYPE_PEM) == 1) {
- wpa_printf(MSG_DEBUG, "OpenSSL: "
- "SSL_use_PrivateKey_File (PEM) --> OK");
- ok = 1;
- break;
- }
-#else /* OPENSSL_NO_STDIO */
- wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
- __func__);
-#endif /* OPENSSL_NO_STDIO */
-
- if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
- == 0) {
+ if (tls_read_pkcs12(data, conn->ssl, private_key,
+ private_key_passwd) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
"--> OK");
ok = 1;
@@ -2865,12 +3246,9 @@ static int tls_connection_private_key(struct tls_data *data,
if (!ok) {
tls_show_errors(MSG_INFO, __func__,
"Failed to load private key");
- os_free(passwd);
return -1;
}
ERR_clear_error();
- SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
- os_free(passwd);
if (!SSL_check_private_key(conn->ssl)) {
tls_show_errors(MSG_INFO, __func__, "Private key failed "
@@ -2888,37 +3266,19 @@ static int tls_global_private_key(struct tls_data *data,
const char *private_key_passwd)
{
SSL_CTX *ssl_ctx = data->ssl;
- char *passwd;
if (private_key == NULL)
return 0;
- if (private_key_passwd) {
- passwd = os_strdup(private_key_passwd);
- if (passwd == NULL)
- return -1;
- } else
- passwd = NULL;
-
- SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
- SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
- if (
-#ifndef OPENSSL_NO_STDIO
- SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
- SSL_FILETYPE_ASN1) != 1 &&
- SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
- SSL_FILETYPE_PEM) != 1 &&
-#endif /* OPENSSL_NO_STDIO */
- tls_read_pkcs12(data, NULL, private_key, passwd)) {
+ if (tls_use_private_key_file(data, NULL, private_key,
+ private_key_passwd) &&
+ tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) {
tls_show_errors(MSG_INFO, __func__,
"Failed to load private key");
- os_free(passwd);
ERR_clear_error();
return -1;
}
- os_free(passwd);
ERR_clear_error();
- SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
if (!SSL_CTX_check_private_key(ssl_ctx)) {
tls_show_errors(MSG_INFO, __func__,
@@ -3105,7 +3465,9 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
#ifdef OPENSSL_NEED_EAP_FAST_PRF
static int openssl_get_keyblock_size(SSL *ssl)
{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
const EVP_CIPHER *c;
const EVP_MD *h;
int md_size;
@@ -3252,8 +3614,7 @@ int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
static struct wpabuf *
-openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
- int server)
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data)
{
int res;
struct wpabuf *out_data;
@@ -3271,7 +3632,7 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
}
/* Initiate TLS handshake or continue the existing handshake */
- if (server)
+ if (conn->server)
res = SSL_accept(conn->ssl);
else
res = SSL_connect(conn->ssl);
@@ -3286,8 +3647,57 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
else {
tls_show_errors(MSG_INFO, __func__, "SSL_connect");
conn->failed++;
+ if (!conn->server && !conn->client_hello_generated) {
+ /* The server would not understand TLS Alert
+ * before ClientHello, so simply terminate
+ * handshake on this type of error case caused
+ * by a likely internal error like no ciphers
+ * available. */
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not generate ClientHello");
+ conn->write_alerts++;
+ return NULL;
+ }
+ }
+ }
+
+ if (!conn->server && !conn->failed)
+ conn->client_hello_generated = 1;
+
+#ifdef CONFIG_SUITEB
+ if ((conn->flags & TLS_CONN_SUITEB) && !conn->server &&
+ os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
+ conn->server_dh_prime_len < 3072) {
+ struct tls_context *context = conn->context;
+
+ /*
+ * This should not be reached since earlier cert_cb should have
+ * terminated the handshake. Keep this check here for extra
+ * protection if anything goes wrong with the more low-level
+ * checks based on having to parse the TLS handshake messages.
+ */
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Server DH prime length: %d bits",
+ conn->server_dh_prime_len);
+
+ if (context->event_cb) {
+ union tls_event_data ev;
+
+ os_memset(&ev, 0, sizeof(ev));
+ ev.alert.is_local = 1;
+ ev.alert.type = "fatal";
+ ev.alert.description = "insufficient security";
+ context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
}
+ /*
+ * Could send a TLS Alert to the server, but for now, simply
+ * terminate handshake.
+ */
+ conn->failed++;
+ conn->write_alerts++;
+ return NULL;
}
+#endif /* CONFIG_SUITEB */
/* Get the TLS handshake data to be sent to the server */
res = BIO_ctrl_pending(conn->ssl_out);
@@ -3358,14 +3768,14 @@ openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
static struct wpabuf *
openssl_connection_handshake(struct tls_connection *conn,
const struct wpabuf *in_data,
- struct wpabuf **appl_data, int server)
+ struct wpabuf **appl_data)
{
struct wpabuf *out_data;
if (appl_data)
*appl_data = NULL;
- out_data = openssl_handshake(conn, in_data, server);
+ out_data = openssl_handshake(conn, in_data);
if (out_data == NULL)
return NULL;
if (conn->invalid_hb_used) {
@@ -3402,7 +3812,7 @@ tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
- return openssl_connection_handshake(conn, in_data, appl_data, 0);
+ return openssl_connection_handshake(conn, in_data, appl_data);
}
@@ -3411,7 +3821,8 @@ struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
- return openssl_connection_handshake(conn, in_data, appl_data, 1);
+ conn->server = 1;
+ return openssl_connection_handshake(conn, in_data, appl_data);
}
@@ -3506,7 +3917,7 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
{
- return conn ? SSL_cache_hit(conn->ssl) : 0;
+ return conn ? SSL_session_reused(conn->ssl) : 0;
}
@@ -3747,7 +4158,7 @@ static int ocsp_resp_cb(SSL *s, void *arg)
{
struct tls_connection *conn = arg;
const unsigned char *p;
- int len, status, reason;
+ int len, status, reason, res;
OCSP_RESPONSE *rsp;
OCSP_BASICRESP *basic;
OCSP_CERTID *id;
@@ -3842,16 +4253,33 @@ static int ocsp_resp_cb(SSL *s, void *arg)
return 0;
}
- id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
+ id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer);
if (!id) {
- wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return 0;
}
- if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
- &this_update, &next_update)) {
+ res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+ &this_update, &next_update);
+ if (!res) {
+ id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
+ if (!id) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ return 0;
+ }
+
+ res = OCSP_resp_find_status(basic, id, &status, &reason,
+ &produced_at, &this_update,
+ &next_update);
+ }
+
+ if (!res) {
wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
(conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
" (OCSP not required)");
@@ -3935,6 +4363,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
const char *cert_id = params->cert_id;
const char *ca_cert_id = params->ca_cert_id;
const char *engine_id = params->engine ? params->engine_id : NULL;
+ const char *ciphers;
if (conn == NULL)
return -1;
@@ -3976,7 +4405,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
engine_id = "pkcs11";
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
if (params->flags & TLS_CONN_EAP_FAST) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Use TLSv1_method() for EAP-FAST");
@@ -3987,6 +4416,17 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
}
}
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+#ifdef SSL_OP_NO_TLSv1_3
+ if (params->flags & TLS_CONN_EAP_FAST) {
+ /* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1
+ * refuses to start the handshake with the modified ciphersuite
+ * list (no TLS v1.3 ciphersuites included) for EAP-FAST. */
+ wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST");
+ SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3);
+ }
+#endif /* SSL_OP_NO_TLSv1_3 */
+#endif
#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
while ((err = ERR_get_error())) {
@@ -4045,15 +4485,27 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
return -1;
}
- if (params->openssl_ciphers &&
- SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+ ciphers = params->openssl_ciphers;
+#ifdef CONFIG_SUITEB
+#ifdef OPENSSL_IS_BORINGSSL
+ if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) {
+ /* BoringSSL removed support for SUITEB192, so need to handle
+ * this with hardcoded ciphersuite and additional checks for
+ * other parameters. */
+ ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384";
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+#endif /* CONFIG_SUITEB */
+ if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set cipher string '%s'",
- params->openssl_ciphers);
+ ciphers);
return -1;
}
- tls_set_conn_flags(conn->ssl, params->flags);
+ if (tls_set_conn_flags(conn, params->flags,
+ params->openssl_ciphers) < 0)
+ return -1;
#ifdef OPENSSL_IS_BORINGSSL
if (params->flags & TLS_CONN_REQUEST_OCSP) {
@@ -4120,10 +4572,8 @@ int tls_global_set_params(void *tls_ctx,
#ifdef SSL_OP_NO_TICKET
if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
-#ifdef SSL_CTX_clear_options
else
SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
#endif /* SSL_OP_NO_TICKET */
#ifdef HAVE_OCSP
@@ -4159,7 +4609,9 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
struct tls_connection *conn = arg;
int ret;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
if (conn == NULL || conn->session_ticket_cb == NULL)
return 0;
@@ -4212,11 +4664,10 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
"extension", data, len);
- conn->session_ticket = os_malloc(len);
+ conn->session_ticket = os_memdup(data, len);
if (conn->session_ticket == NULL)
return 0;
- os_memcpy(conn->session_ticket, data, len);
conn->session_ticket_len = len;
return 1;
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
new file mode 100644
index 000000000000..cc8c704466d2
--- /dev/null
+++ b/src/crypto/tls_wolfssl.c
@@ -0,0 +1,2175 @@
+/*
+ * SSL/TLS interface functions for wolfSSL TLS case
+ * Copyright (c) 2004-2017, 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 "crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "tls.h"
+
+/* wolfSSL includes */
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/error-ssl.h>
+#include <wolfssl/wolfcrypt/asn.h>
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#define HAVE_AESGCM
+#include <wolfssl/wolfcrypt/aes.h>
+#endif
+
+#if !defined(CONFIG_FIPS) && \
+ (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
+ defined(EAP_SERVER_FAST))
+#define WOLFSSL_NEED_EAP_FAST_PRF
+#endif
+
+#define SECRET_LEN 48
+#define RAN_LEN 32
+#define SESSION_TICKET_LEN 256
+
+static int tls_ref_count = 0;
+
+static int tls_ex_idx_session = 0;
+
+
+/* tls input data for wolfSSL Read Callback */
+struct tls_in_data {
+ const struct wpabuf *in_data;
+ size_t consumed; /* how many bytes have we used already */
+};
+
+/* tls output data for wolfSSL Write Callback */
+struct tls_out_data {
+ struct wpabuf *out_data;
+};
+
+struct tls_context {
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
+ int cert_in_cb;
+ char *ocsp_stapling_response;
+};
+
+static struct tls_context *tls_global = NULL;
+
+/* wolfssl tls_connection */
+struct tls_connection {
+ struct tls_context *context;
+ WOLFSSL *ssl;
+ int read_alerts;
+ int write_alerts;
+ int failed;
+ struct tls_in_data input;
+ struct tls_out_data output;
+ char *subject_match;
+ char *alt_subject_match;
+ char *suffix_match;
+ char *domain_match;
+
+ u8 srv_cert_hash[32];
+
+ unsigned char client_random[RAN_LEN];
+ unsigned char server_random[RAN_LEN];
+ unsigned int flags;
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+ tls_session_ticket_cb session_ticket_cb;
+ void *session_ticket_cb_ctx;
+ byte session_ticket[SESSION_TICKET_LEN];
+#endif
+ unsigned int ca_cert_verify:1;
+ unsigned int cert_probe:1;
+ unsigned int server_cert_only:1;
+ unsigned int success_data:1;
+
+ WOLFSSL_X509 *peer_cert;
+ WOLFSSL_X509 *peer_issuer;
+ WOLFSSL_X509 *peer_issuer_issuer;
+};
+
+
+static struct tls_context * tls_context_new(const struct tls_config *conf)
+{
+ struct tls_context *context = os_zalloc(sizeof(*context));
+
+ if (!context)
+ return NULL;
+
+ if (conf) {
+ context->event_cb = conf->event_cb;
+ context->cb_ctx = conf->cb_ctx;
+ context->cert_in_cb = conf->cert_in_cb;
+ }
+
+ return context;
+}
+
+
+static void wolfssl_reset_in_data(struct tls_in_data *in,
+ const struct wpabuf *buf)
+{
+ /* old one not owned by us so don't free */
+ in->in_data = buf;
+ in->consumed = 0;
+}
+
+
+static void wolfssl_reset_out_data(struct tls_out_data *out)
+{
+ /* old one not owned by us so don't free */
+ out->out_data = wpabuf_alloc_copy("", 0);
+}
+
+
+/* wolfSSL I/O Receive CallBack */
+static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
+{
+ size_t get = sz;
+ struct tls_in_data *data = ctx;
+
+ if (!data)
+ return -1;
+
+ if (get > (wpabuf_len(data->in_data) - data->consumed))
+ get = wpabuf_len(data->in_data) - data->consumed;
+
+ os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get);
+ data->consumed += get;
+
+ if (get == 0)
+ return -2; /* WANT_READ */
+
+ return (int) get;
+}
+
+
+/* wolfSSL I/O Send CallBack */
+static int wolfssl_send_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
+{
+ struct wpabuf *tmp;
+ struct tls_out_data *data = ctx;
+
+ if (!data)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "SSL: adding %d bytes", sz);
+
+ tmp = wpabuf_alloc_copy(buf, sz);
+ if (!tmp)
+ return -1;
+ data->out_data = wpabuf_concat(data->out_data, tmp);
+ if (!data->out_data)
+ return -1;
+
+ return sz;
+}
+
+
+static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess)
+{
+ struct wpabuf *buf;
+
+ buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+ if (!buf)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Free application session data %p (sess %p)",
+ buf, sess);
+ wpabuf_free(buf);
+
+ wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+ WOLFSSL_CTX *ssl_ctx;
+ struct tls_context *context;
+ const char *ciphers;
+
+#ifdef DEBUG_WOLFSSL
+ wolfSSL_Debugging_ON();
+#endif /* DEBUG_WOLFSSL */
+
+ context = tls_context_new(conf);
+ if (!context)
+ return NULL;
+
+ if (tls_ref_count == 0) {
+ tls_global = context;
+
+ if (wolfSSL_Init() < 0)
+ return NULL;
+ /* wolfSSL_Debugging_ON(); */
+ }
+
+ tls_ref_count++;
+
+ /* start as client */
+ ssl_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
+ if (!ssl_ctx) {
+ tls_ref_count--;
+ if (context != tls_global)
+ os_free(context);
+ if (tls_ref_count == 0) {
+ os_free(tls_global);
+ tls_global = NULL;
+ }
+ }
+ wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb);
+ wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb);
+ wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context);
+
+ if (conf->tls_session_lifetime > 0) {
+ wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1);
+ wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
+ SSL_SESS_CACHE_SERVER);
+ wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime);
+ wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb);
+ } else {
+ wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
+ SSL_SESS_CACHE_CLIENT);
+ }
+
+ if (conf && conf->openssl_ciphers)
+ ciphers = conf->openssl_ciphers;
+ else
+ ciphers = "ALL";
+ if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
+ wpa_printf(MSG_ERROR,
+ "wolfSSL: Failed to set cipher string '%s'",
+ ciphers);
+ tls_deinit(ssl_ctx);
+ return NULL;
+ }
+
+ return ssl_ctx;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+ struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+
+ if (context != tls_global)
+ os_free(context);
+
+ wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx);
+
+ tls_ref_count--;
+ if (tls_ref_count == 0) {
+ wolfSSL_Cleanup();
+ os_free(tls_global);
+ tls_global = NULL;
+ }
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+#ifdef DEBUG_WOLFSSL
+#if 0
+ unsigned long err;
+
+ err = wolfSSL_ERR_peek_last_error_line(NULL, NULL);
+ if (err != 0) {
+ wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+ wolfSSL_ERR_error_string(err, NULL));
+ return 1;
+ }
+#endif
+#endif /* DEBUG_WOLFSSL */
+ return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+ WOLFSSL_CTX *ssl_ctx = tls_ctx;
+ struct tls_connection *conn;
+
+ wpa_printf(MSG_DEBUG, "SSL: connection init");
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ return NULL;
+ conn->ssl = wolfSSL_new(ssl_ctx);
+ if (!conn->ssl) {
+ os_free(conn);
+ return NULL;
+ }
+
+ wolfSSL_SetIOReadCtx(conn->ssl, &conn->input);
+ wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output);
+ wolfSSL_set_ex_data(conn->ssl, 0, conn);
+ conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+
+ /* Need randoms post-hanshake for EAP-FAST, export key and deriving
+ * session ID in EAP methods. */
+ wolfSSL_KeepArrays(conn->ssl);
+ wolfSSL_KeepHandshakeResources(conn->ssl);
+ wolfSSL_UseClientSuites(conn->ssl);
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+ if (!conn)
+ return;
+
+ wpa_printf(MSG_DEBUG, "SSL: connection deinit");
+
+ /* parts */
+ wolfSSL_free(conn->ssl);
+ os_free(conn->subject_match);
+ os_free(conn->alt_subject_match);
+ os_free(conn->suffix_match);
+ os_free(conn->domain_match);
+
+ /* self */
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+ return conn ? wolfSSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* TODO */
+ return NULL;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+ WOLFSSL_SESSION *session;
+
+ if (!conn)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "SSL: connection shutdown");
+
+ /* Set quiet as OpenSSL does */
+ wolfSSL_set_quiet_shutdown(conn->ssl, 1);
+ wolfSSL_shutdown(conn->ssl);
+
+ session = wolfSSL_get_session(conn->ssl);
+ if (wolfSSL_clear(conn->ssl) != 1)
+ return -1;
+ wolfSSL_set_session(conn->ssl, session);
+
+ return 0;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_connection *conn,
+ const char *subject_match,
+ const char *alt_subject_match,
+ const char *suffix_match,
+ const char *domain_match)
+{
+ os_free(conn->subject_match);
+ conn->subject_match = NULL;
+ if (subject_match) {
+ conn->subject_match = os_strdup(subject_match);
+ if (!conn->subject_match)
+ return -1;
+ }
+
+ os_free(conn->alt_subject_match);
+ conn->alt_subject_match = NULL;
+ if (alt_subject_match) {
+ conn->alt_subject_match = os_strdup(alt_subject_match);
+ if (!conn->alt_subject_match)
+ return -1;
+ }
+
+ os_free(conn->suffix_match);
+ conn->suffix_match = NULL;
+ if (suffix_match) {
+ conn->suffix_match = os_strdup(suffix_match);
+ if (!conn->suffix_match)
+ return -1;
+ }
+
+ os_free(conn->domain_match);
+ conn->domain_match = NULL;
+ if (domain_match) {
+ conn->domain_match = os_strdup(domain_match);
+ if (!conn->domain_match)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int tls_connection_dh(struct tls_connection *conn, const char *dh_file,
+ const u8 *dh_blob, size_t blob_len)
+{
+ if (!dh_file && !dh_blob)
+ return 0;
+
+ wolfSSL_set_accept_state(conn->ssl);
+
+ if (dh_blob) {
+ if (wolfSSL_SetTmpDH_buffer(conn->ssl, dh_blob, blob_len,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO, "SSL: use DH DER blob failed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use DH blob OK");
+ return 0;
+ }
+
+ if (dh_file) {
+ wpa_printf(MSG_INFO, "SSL: use DH PEM file: %s", dh_file);
+ if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
+ SSL_FILETYPE_PEM) < 0) {
+ wpa_printf(MSG_INFO, "SSL: use DH PEM file failed");
+ if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use DH DER file failed");
+ return -1;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use DH file OK");
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+ const char *client_cert,
+ const u8 *client_cert_blob,
+ size_t blob_len)
+{
+ if (!client_cert && !client_cert_blob)
+ return 0;
+
+ if (client_cert_blob) {
+ if (wolfSSL_use_certificate_chain_buffer_format(
+ conn->ssl, client_cert_blob, blob_len,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use client cert DER blob failed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK");
+ return 0;
+ }
+
+ if (client_cert) {
+ if (wolfSSL_use_certificate_chain_file(conn->ssl,
+ client_cert) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use client cert PEM file failed");
+ if (wolfSSL_use_certificate_chain_file_format(
+ conn->ssl, client_cert,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use client cert DER file failed");
+ return -1;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use client cert file OK");
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+ if (!password)
+ return 0;
+ os_strlcpy(buf, (char *) password, size);
+ return os_strlen(buf);
+}
+
+
+static int tls_connection_private_key(void *tls_ctx,
+ struct tls_connection *conn,
+ const char *private_key,
+ const char *private_key_passwd,
+ const u8 *private_key_blob,
+ size_t blob_len)
+{
+ WOLFSSL_CTX *ctx = tls_ctx;
+ char *passwd = NULL;
+ int ok = 0;
+
+ if (!private_key && !private_key_blob)
+ return 0;
+
+ if (private_key_passwd) {
+ passwd = os_strdup(private_key_passwd);
+ if (!passwd)
+ return -1;
+ }
+
+ wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
+ wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
+
+ if (private_key_blob) {
+ if (wolfSSL_use_PrivateKey_buffer(conn->ssl,
+ private_key_blob, blob_len,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use private DER blob failed");
+ } else {
+ wpa_printf(MSG_DEBUG, "SSL: use private key blob OK");
+ ok = 1;
+ }
+ }
+
+ if (!ok && private_key) {
+ if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
+ SSL_FILETYPE_PEM) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use private key PEM file failed");
+ if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
+ SSL_FILETYPE_ASN1) < 0)
+ {
+ wpa_printf(MSG_INFO,
+ "SSL: use private key DER file failed");
+ } else {
+ ok = 1;
+ }
+ } else {
+ ok = 1;
+ }
+
+ if (ok)
+ wpa_printf(MSG_DEBUG, "SSL: use private key file OK");
+ }
+
+ wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
+ os_free(passwd);
+
+ if (!ok)
+ return -1;
+
+ return 0;
+}
+
+
+static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type,
+ const char *value, size_t len)
+{
+ WOLFSSL_ASN1_OBJECT *gen;
+ void *ext;
+ int found = 0;
+ int i;
+
+ ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
+
+ for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
+ gen = wolfSSL_sk_value(ext, i);
+ if (gen->type != type)
+ continue;
+ if (os_strlen((char *) gen->obj) == len &&
+ os_memcmp(value, gen->obj, len) == 0)
+ found++;
+ }
+
+ wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+ return found;
+}
+
+
+static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match)
+{
+ int type;
+ const char *pos, *end;
+ size_t len;
+
+ pos = match;
+ do {
+ if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+ type = GEN_EMAIL;
+ pos += 6;
+ } else if (os_strncmp(pos, "DNS:", 4) == 0) {
+ type = GEN_DNS;
+ pos += 4;
+ } else if (os_strncmp(pos, "URI:", 4) == 0) {
+ type = GEN_URI;
+ pos += 4;
+ } else {
+ wpa_printf(MSG_INFO,
+ "TLS: Invalid altSubjectName match '%s'",
+ pos);
+ return 0;
+ }
+ end = os_strchr(pos, ';');
+ while (end) {
+ if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+ os_strncmp(end + 1, "DNS:", 4) == 0 ||
+ os_strncmp(end + 1, "URI:", 4) == 0)
+ break;
+ end = os_strchr(end + 1, ';');
+ }
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (tls_match_alt_subject_component(cert, type, pos, len) > 0)
+ return 1;
+ pos = end + 1;
+ } while (end);
+
+ return 0;
+}
+
+
+static int domain_suffix_match(const char *val, size_t len, const char *match,
+ int full)
+{
+ size_t i, match_len;
+
+ /* Check for embedded nuls that could mess up suffix matching */
+ for (i = 0; i < len; i++) {
+ if (val[i] == '\0') {
+ wpa_printf(MSG_DEBUG,
+ "TLS: Embedded null in a string - reject");
+ return 0;
+ }
+ }
+
+ match_len = os_strlen(match);
+ if (match_len > len || (full && match_len != len))
+ return 0;
+
+ if (os_strncasecmp(val + len - match_len, match, match_len) != 0)
+ return 0; /* no match */
+
+ if (match_len == len)
+ return 1; /* exact match */
+
+ if (val[len - match_len - 1] == '.')
+ return 1; /* full label match completes suffix match */
+
+ wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
+ return 0;
+}
+
+
+static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
+{
+ WOLFSSL_ASN1_OBJECT *gen;
+ void *ext;
+ int i;
+ int j;
+ int dns_name = 0;
+ WOLFSSL_X509_NAME *name;
+
+ wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+ full ? "" : "suffix ", match);
+
+ ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
+
+ for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
+ gen = wolfSSL_sk_value(ext, j);
+ if (gen->type != ALT_NAMES_OID)
+ continue;
+ dns_name++;
+ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
+ gen->obj, os_strlen((char *)gen->obj));
+ if (domain_suffix_match((const char *) gen->obj,
+ os_strlen((char *) gen->obj), match,
+ full) == 1) {
+ wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+ full ? "Match" : "Suffix match");
+ wolfSSL_sk_ASN1_OBJECT_free(ext);
+ return 1;
+ }
+ }
+ wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+ if (dns_name) {
+ wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+ return 0;
+ }
+
+ name = wolfSSL_X509_get_subject_name(cert);
+ i = -1;
+ for (;;) {
+ WOLFSSL_X509_NAME_ENTRY *e;
+ WOLFSSL_ASN1_STRING *cn;
+
+ i = wolfSSL_X509_NAME_get_index_by_NID(name, ASN_COMMON_NAME,
+ i);
+ if (i == -1)
+ break;
+ e = wolfSSL_X509_NAME_get_entry(name, i);
+ if (!e)
+ continue;
+ cn = wolfSSL_X509_NAME_ENTRY_get_data(e);
+ if (!cn)
+ continue;
+ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+ cn->data, cn->length);
+ if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
+ {
+ wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+ full ? "Match" : "Suffix match");
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+ full ? "" : "suffix ");
+ return 0;
+}
+
+
+static enum tls_fail_reason wolfssl_tls_fail_reason(int err)
+{
+ switch (err) {
+ case X509_V_ERR_CERT_REVOKED:
+ return TLS_FAIL_REVOKED;
+ case ASN_BEFORE_DATE_E:
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ return TLS_FAIL_NOT_YET_VALID;
+ case ASN_AFTER_DATE_E:
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ return TLS_FAIL_EXPIRED;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ case X509_V_ERR_INVALID_CA:
+ return TLS_FAIL_UNTRUSTED;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_CERT_REJECTED:
+ return TLS_FAIL_BAD_CERTIFICATE;
+ default:
+ return TLS_FAIL_UNSPECIFIED;
+ }
+}
+
+
+static const char * wolfssl_tls_err_string(int err, const char *err_str)
+{
+ switch (err) {
+ case ASN_BEFORE_DATE_E:
+ return "certificate is not yet valid";
+ case ASN_AFTER_DATE_E:
+ return "certificate has expired";
+ default:
+ return err_str;
+ }
+}
+
+
+static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert)
+{
+ struct wpabuf *buf = NULL;
+ const u8 *data;
+ int cert_len;
+
+ data = wolfSSL_X509_get_der(cert, &cert_len);
+ if (!data)
+ buf = wpabuf_alloc_copy(data, cert_len);
+
+ return buf;
+}
+
+
+static void wolfssl_tls_fail_event(struct tls_connection *conn,
+ WOLFSSL_X509 *err_cert, int err, int depth,
+ const char *subject, const char *err_str,
+ enum tls_fail_reason reason)
+{
+ union tls_event_data ev;
+ struct wpabuf *cert = NULL;
+ struct tls_context *context = conn->context;
+
+ if (!context->event_cb)
+ return;
+
+ cert = get_x509_cert(err_cert);
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+ reason : wolfssl_tls_fail_reason(err);
+ ev.cert_fail.depth = depth;
+ ev.cert_fail.subject = subject;
+ ev.cert_fail.reason_txt = wolfssl_tls_err_string(err, err_str);
+ ev.cert_fail.cert = cert;
+ context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+ wpabuf_free(cert);
+}
+
+
+static void wolfssl_tls_cert_event(struct tls_connection *conn,
+ WOLFSSL_X509 *err_cert, int depth,
+ const char *subject)
+{
+ struct wpabuf *cert = NULL;
+ union tls_event_data ev;
+ struct tls_context *context = conn->context;
+ char *alt_subject[TLS_MAX_ALT_SUBJECT];
+ int alt, num_alt_subject = 0;
+ WOLFSSL_ASN1_OBJECT *gen;
+ void *ext;
+ int i;
+#ifdef CONFIG_SHA256
+ u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+ if (!context->event_cb)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
+ if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
+ context->cert_in_cb) {
+ cert = get_x509_cert(err_cert);
+ ev.peer_cert.cert = cert;
+ }
+
+#ifdef CONFIG_SHA256
+ if (cert) {
+ const u8 *addr[1];
+ size_t len[1];
+
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) == 0) {
+ ev.peer_cert.hash = hash;
+ ev.peer_cert.hash_len = sizeof(hash);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+
+ ev.peer_cert.depth = depth;
+ ev.peer_cert.subject = subject;
+
+ ext = wolfSSL_X509_get_ext_d2i(err_cert, ALT_NAMES_OID, NULL, NULL);
+ for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
+ char *pos;
+
+ if (num_alt_subject == TLS_MAX_ALT_SUBJECT)
+ break;
+ gen = wolfSSL_sk_value((void *) ext, i);
+ if (gen->type != GEN_EMAIL &&
+ gen->type != GEN_DNS &&
+ gen->type != GEN_URI)
+ continue;
+
+ pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1);
+ if (!pos)
+ break;
+ alt_subject[num_alt_subject++] = pos;
+
+ switch (gen->type) {
+ case GEN_EMAIL:
+ os_memcpy(pos, "EMAIL:", 6);
+ pos += 6;
+ break;
+ case GEN_DNS:
+ os_memcpy(pos, "DNS:", 4);
+ pos += 4;
+ break;
+ case GEN_URI:
+ os_memcpy(pos, "URI:", 4);
+ pos += 4;
+ break;
+ }
+
+ os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj));
+ pos += os_strlen((char *)gen->obj);
+ *pos = '\0';
+ }
+ wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+ for (alt = 0; alt < num_alt_subject; alt++)
+ ev.peer_cert.altsubject[alt] = alt_subject[alt];
+ ev.peer_cert.num_altsubject = num_alt_subject;
+
+ context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+ wpabuf_free(cert);
+ for (alt = 0; alt < num_alt_subject; alt++)
+ os_free(alt_subject[alt]);
+}
+
+
+static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx)
+{
+ char buf[256];
+ WOLFSSL_X509 *err_cert;
+ int err, depth;
+ WOLFSSL *ssl;
+ struct tls_connection *conn;
+ struct tls_context *context;
+ char *match, *altmatch, *suffix_match, *domain_match;
+ const char *err_str;
+
+ err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
+ if (!err_cert) {
+ wpa_printf(MSG_DEBUG, "wolfSSL: No Cert");
+ return 0;
+ }
+
+ err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx);
+ depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx);
+ ssl = wolfSSL_X509_STORE_CTX_get_ex_data(
+ x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx());
+ wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf,
+ sizeof(buf));
+
+ conn = wolfSSL_get_ex_data(ssl, 0);
+ if (!conn) {
+ wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data");
+ return 0;
+ }
+
+ if (depth == 0)
+ conn->peer_cert = err_cert;
+ else if (depth == 1)
+ conn->peer_issuer = err_cert;
+ else if (depth == 2)
+ conn->peer_issuer_issuer = err_cert;
+
+ context = conn->context;
+ match = conn->subject_match;
+ altmatch = conn->alt_subject_match;
+ suffix_match = conn->suffix_match;
+ domain_match = conn->domain_match;
+
+ if (!preverify_ok && !conn->ca_cert_verify)
+ preverify_ok = 1;
+ if (!preverify_ok && depth > 0 && conn->server_cert_only)
+ preverify_ok = 1;
+ if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
+ (err == X509_V_ERR_CERT_HAS_EXPIRED ||
+ err == ASN_AFTER_DATE_E || err == ASN_BEFORE_DATE_E ||
+ err == X509_V_ERR_CERT_NOT_YET_VALID)) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Ignore certificate validity time mismatch");
+ preverify_ok = 1;
+ }
+
+ err_str = wolfSSL_X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+ /*
+ * Do not require preverify_ok so we can explicity allow otherwise
+ * invalid pinned server certificates.
+ */
+ if (depth == 0 && conn->server_cert_only) {
+ struct wpabuf *cert;
+
+ cert = get_x509_cert(err_cert);
+ if (!cert) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Could not fetch server certificate data");
+ preverify_ok = 0;
+ } else {
+ u8 hash[32];
+ const u8 *addr[1];
+ size_t len[1];
+
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) < 0 ||
+ os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+ err_str = "Server certificate mismatch";
+ err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+ preverify_ok = 0;
+ } else if (!preverify_ok) {
+ /*
+ * Certificate matches pinned certificate, allow
+ * regardless of other problems.
+ */
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Ignore validation issues for a pinned server certificate");
+ preverify_ok = 1;
+ }
+ wpabuf_free(cert);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+
+ if (!preverify_ok) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'",
+ err, err_str, depth, buf);
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ err_str, TLS_FAIL_UNSPECIFIED);
+ return preverify_ok;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+ __func__, preverify_ok, err, err_str,
+ conn->ca_cert_verify, depth, buf);
+ if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Subject '%s' did not match with '%s'",
+ buf, match);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Subject mismatch",
+ TLS_FAIL_SUBJECT_MISMATCH);
+ } else if (depth == 0 && altmatch &&
+ !tls_match_alt_subject(err_cert, altmatch)) {
+ wpa_printf(MSG_WARNING,
+ "TLS: altSubjectName match '%s' not found",
+ altmatch);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "AltSubject mismatch",
+ TLS_FAIL_ALTSUBJECT_MISMATCH);
+ } else if (depth == 0 && suffix_match &&
+ !tls_match_suffix(err_cert, suffix_match, 0)) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Domain suffix match '%s' not found",
+ suffix_match);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Domain suffix mismatch",
+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+ } else if (depth == 0 && domain_match &&
+ !tls_match_suffix(err_cert, domain_match, 1)) {
+ wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+ domain_match);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Domain mismatch",
+ TLS_FAIL_DOMAIN_MISMATCH);
+ } else {
+ wolfssl_tls_cert_event(conn, err_cert, depth, buf);
+ }
+
+ if (conn->cert_probe && preverify_ok && depth == 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Reject server certificate on probe-only run");
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Server certificate chain probe",
+ TLS_FAIL_SERVER_CHAIN_PROBE);
+ }
+
+#ifdef HAVE_OCSP_WOLFSSL
+ if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+ preverify_ok) {
+ enum ocsp_result res;
+
+ res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+ conn->peer_issuer,
+ conn->peer_issuer_issuer);
+ if (res == OCSP_REVOKED) {
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "certificate revoked",
+ TLS_FAIL_REVOKED);
+ if (err == X509_V_OK)
+ X509_STORE_CTX_set_error(
+ x509_ctx, X509_V_ERR_CERT_REVOKED);
+ } else if (res != OCSP_GOOD &&
+ (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "bad certificate status response",
+ TLS_FAIL_UNSPECIFIED);
+ }
+ }
+#endif /* HAVE_OCSP_WOLFSSL */
+ if (depth == 0 && preverify_ok && context->event_cb != NULL)
+ context->event_cb(context->cb_ctx,
+ TLS_CERT_CHAIN_SUCCESS, NULL);
+
+ return preverify_ok;
+}
+
+
+static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn,
+ const char *ca_cert,
+ const u8 *ca_cert_blob, size_t blob_len,
+ const char *ca_path)
+{
+ WOLFSSL_CTX *ctx = tls_ctx;
+
+ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ conn->ca_cert_verify = 1;
+
+ if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Probe for server certificate chain");
+ conn->cert_probe = 1;
+ conn->ca_cert_verify = 0;
+ return 0;
+ }
+
+ if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+ const char *pos = ca_cert + 7;
+
+ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Unsupported ca_cert hash value '%s'",
+ ca_cert);
+ return -1;
+ }
+ pos += 14;
+ if (os_strlen(pos) != 32 * 2) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Unexpected SHA256 hash length in ca_cert '%s'",
+ ca_cert);
+ return -1;
+ }
+ if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Invalid SHA256 hash value in ca_cert '%s'",
+ ca_cert);
+ return -1;
+ }
+ conn->server_cert_only = 1;
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Checking only server certificate match");
+ return 0;
+#else /* CONFIG_SHA256 */
+ wpa_printf(MSG_INFO,
+ "No SHA256 included in the build - cannot validate server certificate hash");
+ return -1;
+#endif /* CONFIG_SHA256 */
+ }
+
+ if (ca_cert_blob) {
+ if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len,
+ SSL_FILETYPE_ASN1) !=
+ SSL_SUCCESS) {
+ wpa_printf(MSG_INFO, "SSL: failed to load CA blob");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK");
+ return 0;
+ }
+
+ if (ca_cert || ca_path) {
+ WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new();
+
+ if (!cm) {
+ wpa_printf(MSG_INFO,
+ "SSL: failed to create certificate store");
+ return -1;
+ }
+ wolfSSL_CTX_set_cert_store(ctx, cm);
+
+ if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) !=
+ SSL_SUCCESS) {
+ wpa_printf(MSG_INFO,
+ "SSL: failed to load ca_cert as PEM");
+
+ if (!ca_cert)
+ return -1;
+
+ if (wolfSSL_CTX_der_load_verify_locations(
+ ctx, ca_cert, SSL_FILETYPE_ASN1) !=
+ SSL_SUCCESS) {
+ wpa_printf(MSG_INFO,
+ "SSL: failed to load ca_cert as DER");
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ conn->ca_cert_verify = 0;
+ return 0;
+}
+
+
+static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags)
+{
+#ifdef HAVE_SESSION_TICKET
+#if 0
+ if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET))
+ wolfSSL_UseSessionTicket(ssl);
+#endif
+#endif /* HAVE_SESSION_TICKET */
+
+ if (flags & TLS_CONN_DISABLE_TLSv1_0)
+ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1);
+ if (flags & TLS_CONN_DISABLE_TLSv1_1)
+ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+ if (flags & TLS_CONN_DISABLE_TLSv1_2)
+ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+ wpa_printf(MSG_DEBUG, "SSL: set params");
+
+ if (tls_connection_set_subject_match(conn, params->subject_match,
+ params->altsubject_match,
+ params->suffix_match,
+ params->domain_match) < 0) {
+ wpa_printf(MSG_INFO, "Error setting subject match");
+ return -1;
+ }
+
+ if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
+ params->ca_cert_blob,
+ params->ca_cert_blob_len,
+ params->ca_path) < 0) {
+ wpa_printf(MSG_INFO, "Error setting CA cert");
+ return -1;
+ }
+
+ if (tls_connection_client_cert(conn, params->client_cert,
+ params->client_cert_blob,
+ params->client_cert_blob_len) < 0) {
+ wpa_printf(MSG_INFO, "Error setting client cert");
+ return -1;
+ }
+
+ if (tls_connection_private_key(tls_ctx, conn, params->private_key,
+ params->private_key_passwd,
+ params->private_key_blob,
+ params->private_key_blob_len) < 0) {
+ wpa_printf(MSG_INFO, "Error setting private key");
+ return -1;
+ }
+
+ if (tls_connection_dh(conn, params->dh_file, params->dh_blob,
+ params->dh_blob_len) < 0) {
+ wpa_printf(MSG_INFO, "Error setting DH");
+ return -1;
+ }
+
+ if (params->openssl_ciphers &&
+ wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "wolfSSL: Failed to set cipher string '%s'",
+ params->openssl_ciphers);
+ return -1;
+ }
+
+ tls_set_conn_flags(conn->ssl, params->flags);
+
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ if (wolfSSL_UseOCSPStapling(conn->ssl, WOLFSSL_CSR_OCSP,
+ WOLFSSL_CSR_OCSP_USE_NONCE) !=
+ SSL_SUCCESS)
+ return -1;
+ wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
+ }
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ if (wolfSSL_UseOCSPStaplingV2(conn->ssl,
+ WOLFSSL_CSR2_OCSP_MULTI, 0) !=
+ SSL_SUCCESS)
+ return -1;
+ wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
+ }
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
+ !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
+#ifdef HAVE_OCSP
+ if (params->flags & TLS_CONN_REQUEST_OCSP)
+ wolfSSL_CTX_EnableOCSP(ctx, 0);
+#else /* HAVE_OCSP */
+ if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+ wpa_printf(MSG_INFO,
+ "wolfSSL: No OCSP support included - reject configuration");
+ return -1;
+ }
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: No OCSP support included - allow optional OCSP case to continue");
+ }
+#endif /* HAVE_OCSP */
+#endif /* !HAVE_CERTIFICATE_STATUS_REQUEST &&
+ * !HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+
+ conn->flags = params->flags;
+
+ return 0;
+}
+
+
+static int tls_global_ca_cert(void *ssl_ctx, const char *ca_cert)
+{
+ WOLFSSL_CTX *ctx = ssl_ctx;
+
+ if (ca_cert) {
+ if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL) != 1)
+ {
+ wpa_printf(MSG_WARNING,
+ "Failed to load root certificates");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "TLS: Trusted root certificate(s) loaded");
+ }
+
+ return 0;
+}
+
+
+static int tls_global_client_cert(void *ssl_ctx, const char *client_cert)
+{
+ WOLFSSL_CTX *ctx = ssl_ctx;
+
+ if (!client_cert)
+ return 0;
+
+ if (wolfSSL_CTX_use_certificate_chain_file_format(ctx, client_cert,
+ SSL_FILETYPE_ASN1) !=
+ SSL_SUCCESS &&
+ wolfSSL_CTX_use_certificate_chain_file(ctx, client_cert) !=
+ SSL_SUCCESS) {
+ wpa_printf(MSG_INFO, "Failed to load client certificate");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Loaded global client certificate: %s",
+ client_cert);
+
+ return 0;
+}
+
+
+static int tls_global_private_key(void *ssl_ctx, const char *private_key,
+ const char *private_key_passwd)
+{
+ WOLFSSL_CTX *ctx = ssl_ctx;
+ char *passwd = NULL;
+ int ret = 0;
+
+ if (!private_key)
+ return 0;
+
+ if (private_key_passwd) {
+ passwd = os_strdup(private_key_passwd);
+ if (!passwd)
+ return -1;
+ }
+
+ wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
+ wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
+
+ if (wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
+ SSL_FILETYPE_ASN1) != 1 &&
+ wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
+ SSL_FILETYPE_PEM) != 1) {
+ wpa_printf(MSG_INFO, "Failed to load private key");
+ ret = -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Loaded global private key");
+
+ os_free(passwd);
+ wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
+
+ return ret;
+}
+
+
+static int tls_global_dh(void *ssl_ctx, const char *dh_file,
+ const u8 *dh_blob, size_t blob_len)
+{
+ WOLFSSL_CTX *ctx = ssl_ctx;
+
+ if (!dh_file && !dh_blob)
+ return 0;
+
+ if (dh_blob) {
+ if (wolfSSL_CTX_SetTmpDH_buffer(ctx, dh_blob, blob_len,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: global use DH DER blob failed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: global use DH blob OK");
+ return 0;
+ }
+
+ if (dh_file) {
+ if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) <
+ 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: global use DH PEM file failed");
+ if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: global use DH DER file failed");
+ return -1;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "SSL: global use DH file OK");
+ return 0;
+ }
+
+ return 0;
+}
+
+
+#ifdef HAVE_OCSP
+
+int ocsp_status_cb(void *unused, const char *url, int url_sz,
+ unsigned char *request, int request_sz,
+ unsigned char **response)
+{
+ size_t len;
+
+ (void) unused;
+
+ if (!url) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: OCSP status callback - no response configured");
+ *response = NULL;
+ return 0;
+ }
+
+ *response = (unsigned char *) os_readfile(url, &len);
+ if (!*response) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: OCSP status callback - could not read response file");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: OCSP status callback - send cached response");
+ return len;
+}
+
+
+void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response)
+{
+ os_free(response);
+}
+
+#endif /* HAVE_OCSP */
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+ wpa_printf(MSG_DEBUG, "SSL: global set params");
+
+ if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
+ wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
+ params->ca_cert);
+ return -1;
+ }
+
+ if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: Failed to load client cert file '%s'",
+ params->client_cert);
+ return -1;
+ }
+
+ if (tls_global_private_key(tls_ctx, params->private_key,
+ params->private_key_passwd) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: Failed to load private key file '%s'",
+ params->private_key);
+ return -1;
+ }
+
+ if (tls_global_dh(tls_ctx, params->dh_file, params->dh_blob,
+ params->dh_blob_len) < 0) {
+ wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'",
+ params->dh_file);
+ return -1;
+ }
+
+ if (params->openssl_ciphers &&
+ wolfSSL_CTX_set_cipher_list(tls_ctx,
+ params->openssl_ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "wolfSSL: Failed to set cipher string '%s'",
+ params->openssl_ciphers);
+ return -1;
+ }
+
+#ifdef HAVE_SESSION_TICKET
+ /* Session ticket is off by default - can't disable once on. */
+ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
+ wolfSSL_CTX_UseSessionTicket(tls_ctx);
+#endif /* HAVE_SESSION_TICKET */
+
+#ifdef HAVE_OCSP
+ if (params->ocsp_stapling_response) {
+ wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx,
+ params->ocsp_stapling_response);
+ wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb,
+ ocsp_resp_free_cb, NULL);
+ }
+#endif /* HAVE_OCSP */
+
+ return 0;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+ wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl);
+
+ if (check_crl) {
+ /* Hack to Enable CRLs. */
+ wolfSSL_CTX_LoadCRLBuffer(tls_ctx, NULL, 0, SSL_FILETYPE_PEM);
+ }
+
+ return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+ int verify_peer, unsigned int flags,
+ const u8 *session_ctx, size_t session_ctx_len)
+{
+ if (!conn)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer);
+
+ if (verify_peer) {
+ conn->ca_cert_verify = 1;
+ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ tls_verify_cb);
+ } else {
+ conn->ca_cert_verify = 0;
+ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+ }
+
+ wolfSSL_set_accept_state(conn->ssl);
+
+ /* TODO: do we need to fake a session like OpenSSL does here? */
+
+ return 0;
+}
+
+
+static struct wpabuf * wolfssl_handshake(struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ int server)
+{
+ int res;
+
+ wolfssl_reset_out_data(&conn->output);
+
+ /* Initiate TLS handshake or continue the existing handshake */
+ if (server) {
+ wolfSSL_set_accept_state(conn->ssl);
+ res = wolfSSL_accept(conn->ssl);
+ wpa_printf(MSG_DEBUG, "SSL: wolfSSL_accept: %d", res);
+ } else {
+ wolfSSL_set_connect_state(conn->ssl);
+ res = wolfSSL_connect(conn->ssl);
+ wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res);
+ }
+
+ if (res != 1) {
+ int err = wolfSSL_get_error(conn->ssl, res);
+
+ if (err == SSL_ERROR_WANT_READ) {
+ wpa_printf(MSG_DEBUG,
+ "SSL: wolfSSL_connect - want more data");
+ } else if (err == SSL_ERROR_WANT_WRITE) {
+ wpa_printf(MSG_DEBUG,
+ "SSL: wolfSSL_connect - want to write");
+ } else {
+ char msg[80];
+
+ wpa_printf(MSG_DEBUG,
+ "SSL: wolfSSL_connect - failed %s",
+ wolfSSL_ERR_error_string(err, msg));
+ conn->failed++;
+ }
+ }
+
+ return conn->output.out_data;
+}
+
+
+static struct wpabuf * wolfssl_get_appl_data(struct tls_connection *conn,
+ size_t max_len)
+{
+ int res;
+ struct wpabuf *appl_data = wpabuf_alloc(max_len + 100);
+
+ if (!appl_data)
+ return NULL;
+
+ res = wolfSSL_read(conn->ssl, wpabuf_mhead(appl_data),
+ wpabuf_size(appl_data));
+ if (res < 0) {
+ int err = wolfSSL_get_error(conn->ssl, res);
+
+ if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+ wpa_printf(MSG_DEBUG,
+ "SSL: No Application Data included");
+ } else {
+ char msg[80];
+
+ wpa_printf(MSG_DEBUG,
+ "Failed to read possible Application Data %s",
+ wolfSSL_ERR_error_string(err, msg));
+ }
+
+ wpabuf_free(appl_data);
+ return NULL;
+ }
+
+ wpabuf_put(appl_data, res);
+ wpa_hexdump_buf_key(MSG_MSGDUMP,
+ "SSL: Application Data in Finished message",
+ appl_data);
+ return appl_data;
+}
+
+
+static struct wpabuf *
+wolfssl_connection_handshake(struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data, int server)
+{
+ struct wpabuf *out_data;
+
+ wolfssl_reset_in_data(&conn->input, in_data);
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ out_data = wolfssl_handshake(conn, in_data, server);
+ if (!out_data)
+ return NULL;
+
+ if (wolfSSL_is_init_finished(conn->ssl)) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Handshake finished - resumed=%d",
+ tls_connection_resumed(NULL, conn));
+ if (appl_data && in_data)
+ *appl_data = wolfssl_get_appl_data(conn,
+ wpabuf_len(in_data));
+ }
+
+ return out_data;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return wolfssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return wolfssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ int res;
+
+ if (!conn)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data));
+
+ wolfssl_reset_out_data(&conn->output);
+
+ res = wolfSSL_write(conn->ssl, wpabuf_head(in_data),
+ wpabuf_len(in_data));
+ if (res < 0) {
+ int err = wolfSSL_get_error(conn->ssl, res);
+ char msg[80];
+
+ wpa_printf(MSG_INFO, "Encryption failed - SSL_write: %s",
+ wolfSSL_ERR_error_string(err, msg));
+ return NULL;
+ }
+
+ return conn->output.out_data;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ int res;
+ struct wpabuf *buf;
+
+ if (!conn)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "SSL: decrypt");
+
+ wolfssl_reset_in_data(&conn->input, in_data);
+
+ /* Read decrypted data for further processing */
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (!buf)
+ return NULL;
+ res = wolfSSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Decryption failed - SSL_read");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+
+ wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf));
+
+ return buf;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+ return conn ? wolfSSL_session_reused(conn->ssl) : 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+ char buf[128], *pos, *end;
+ u8 *c;
+ int ret;
+
+ if (!conn || !conn->ssl || !ciphers)
+ return -1;
+
+ buf[0] = '\0';
+ pos = buf;
+ end = pos + sizeof(buf);
+
+ c = ciphers;
+ while (*c != TLS_CIPHER_NONE) {
+ const char *suite;
+
+ switch (*c) {
+ case TLS_CIPHER_RC4_SHA:
+ suite = "RC4-SHA";
+ break;
+ case TLS_CIPHER_AES128_SHA:
+ suite = "AES128-SHA";
+ break;
+ case TLS_CIPHER_RSA_DHE_AES128_SHA:
+ suite = "DHE-RSA-AES128-SHA";
+ break;
+ case TLS_CIPHER_ANON_DH_AES128_SHA:
+ suite = "ADH-AES128-SHA";
+ break;
+ case TLS_CIPHER_RSA_DHE_AES256_SHA:
+ suite = "DHE-RSA-AES256-SHA";
+ break;
+ case TLS_CIPHER_AES256_SHA:
+ suite = "AES256-SHA";
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "TLS: Unsupported cipher selection: %d", *c);
+ return -1;
+ }
+ ret = os_snprintf(pos, end - pos, ":%s", suite);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+
+ c++;
+ }
+
+ wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1);
+
+ if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+ wpa_printf(MSG_DEBUG, "Cipher suite configuration failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ WOLFSSL_CIPHER *cipher;
+ const char *name;
+
+ if (!conn || !conn->ssl)
+ return -1;
+
+ cipher = wolfSSL_get_current_cipher(conn->ssl);
+ if (!cipher)
+ return -1;
+
+ name = wolfSSL_CIPHER_get_name(cipher);
+ if (!name)
+ return -1;
+
+ if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0)
+ os_strlcpy(buf, "RC4-SHA", buflen);
+ else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0)
+ os_strlcpy(buf, "AES128-SHA", buflen);
+ else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0)
+ os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen);
+ else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0)
+ os_strlcpy(buf, "ADH-AES128-SHA", buflen);
+ else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0)
+ os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen);
+ else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0)
+ os_strlcpy(buf, "AES256-SHA", buflen);
+ else
+ os_strlcpy(buf, name, buflen);
+
+ return 0;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* no empty fragments in wolfSSL for now */
+ return 0;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+ if (!conn)
+ return -1;
+
+ return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+ if (!conn)
+ return -1;
+
+ /* TODO: this is not incremented anywhere */
+ return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ if (!conn)
+ return -1;
+
+ /* TODO: this is not incremented anywhere */
+ return conn->write_alerts;
+}
+
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+ return os_snprintf(buf, buf_len, "wolfSSL build=%s run=%s",
+ WOLFSSL_VERSION, wolfSSL_lib_version());
+}
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ const char *name;
+
+ if (!conn || !conn->ssl)
+ return -1;
+
+ name = wolfSSL_get_version(conn->ssl);
+ if (!name)
+ return -1;
+
+ os_strlcpy(buf, name, buflen);
+ return 0;
+}
+
+
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+ struct tls_random *keys)
+{
+ WOLFSSL *ssl;
+
+ if (!conn || !keys)
+ return -1;
+ ssl = conn->ssl;
+ if (!ssl)
+ return -1;
+
+ os_memset(keys, 0, sizeof(*keys));
+ keys->client_random = conn->client_random;
+ keys->client_random_len = wolfSSL_get_client_random(
+ ssl, conn->client_random, sizeof(conn->client_random));
+ keys->server_random = conn->server_random;
+ keys->server_random_len = wolfSSL_get_server_random(
+ ssl, conn->server_random, sizeof(conn->server_random));
+
+ return 0;
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+ const char *label, u8 *out, size_t out_len)
+{
+ if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
+ return -1;
+ return 0;
+}
+
+
+#define SEED_LEN (RAN_LEN + RAN_LEN)
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+ u8 *out, size_t out_len)
+{
+ byte seed[SEED_LEN];
+ int ret = -1;
+ WOLFSSL *ssl;
+ byte *tmp_out;
+ byte *_out;
+ int skip = 0;
+ byte *master_key;
+ unsigned int master_key_len;
+ byte *server_random;
+ unsigned int server_len;
+ byte *client_random;
+ unsigned int client_len;
+
+ if (!conn || !conn->ssl)
+ return -1;
+ ssl = conn->ssl;
+
+ skip = 2 * (wolfSSL_GetKeySize(ssl) + wolfSSL_GetHmacSize(ssl) +
+ wolfSSL_GetIVSize(ssl));
+
+ tmp_out = os_malloc(skip + out_len);
+ if (!tmp_out)
+ return -1;
+ _out = tmp_out;
+
+ wolfSSL_get_keys(ssl, &master_key, &master_key_len, &server_random,
+ &server_len, &client_random, &client_len);
+ os_memcpy(seed, server_random, RAN_LEN);
+ os_memcpy(seed + RAN_LEN, client_random, RAN_LEN);
+
+ if (wolfSSL_GetVersion(ssl) == WOLFSSL_TLSV1_2) {
+ tls_prf_sha256(master_key, master_key_len,
+ "key expansion", seed, sizeof(seed),
+ _out, skip + out_len);
+ ret = 0;
+ } else {
+ ret = tls_prf_sha1_md5(master_key, master_key_len,
+ "key expansion", seed, sizeof(seed),
+ _out, skip + out_len);
+ }
+
+ os_memset(master_key, 0, master_key_len);
+ if (ret == 0)
+ os_memcpy(out, _out + skip, out_len);
+ bin_clear_free(tmp_out, skip + out_len);
+
+ return ret;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ (void) ssl_ctx;
+
+ if (!conn || !conn->ssl || ext_type != 35)
+ return -1;
+
+ if (wolfSSL_set_SessionTicket(conn->ssl, data,
+ (unsigned int) data_len) != 1)
+ return -1;
+
+ return 0;
+}
+
+
+static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg)
+{
+ struct tls_connection *conn = arg;
+ int ret;
+ unsigned char client_random[RAN_LEN];
+ unsigned char server_random[RAN_LEN];
+ word32 ticket_len = sizeof(conn->session_ticket);
+
+ if (!conn || !conn->session_ticket_cb)
+ return 1;
+
+ if (wolfSSL_get_client_random(s, client_random,
+ sizeof(client_random)) == 0 ||
+ wolfSSL_get_server_random(s, server_random,
+ sizeof(server_random)) == 0 ||
+ wolfSSL_get_SessionTicket(s, conn->session_ticket,
+ &ticket_len) != 1)
+ return 1;
+
+ if (ticket_len == 0)
+ return 0;
+
+ ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+ conn->session_ticket, ticket_len,
+ client_random, server_random, secret);
+ if (ret <= 0)
+ return 1;
+
+ *secret_len = SECRET_LEN;
+ return 0;
+}
+
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+ struct tls_connection *conn,
+ tls_session_ticket_cb cb,
+ void *ctx)
+{
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+ conn->session_ticket_cb = cb;
+ conn->session_ticket_cb_ctx = ctx;
+
+ if (cb) {
+ if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+ conn) != 1)
+ return -1;
+ } else {
+ if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+ return -1;
+ }
+
+ return 0;
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+ return -1;
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Success data accepted for resumed session");
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+ WOLFSSL_SESSION *sess;
+
+ sess = wolfSSL_get_session(conn->ssl);
+ if (!sess)
+ return;
+
+ wolfSSL_SSL_SESSION_set_timeout(sess, 0);
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Removed cached session to disable session resumption");
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data)
+{
+ WOLFSSL_SESSION *sess;
+ struct wpabuf *old;
+
+ wpa_printf(MSG_DEBUG, "wolfSSL: Set success data");
+
+ sess = wolfSSL_get_session(conn->ssl);
+ if (!sess) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: No session found for success data");
+ goto fail;
+ }
+
+ old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+ if (old) {
+ wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p",
+ old);
+ wpabuf_free(old);
+ }
+ if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data);
+ conn->success_data = 1;
+ return;
+
+fail:
+ wpa_printf(MSG_INFO, "wolfSSL: Failed to store success data");
+ wpabuf_free(data);
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+ WOLFSSL_SESSION *sess;
+
+ wpa_printf(MSG_DEBUG, "wolfSSL: Get success data");
+
+ sess = wolfSSL_get_session(conn->ssl);
+ if (!sess)
+ return NULL;
+ return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index a449cc934735..4ac9f16a0efc 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1,6 +1,6 @@
/*
* Driver interface definition
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -21,6 +21,10 @@
#include "common/defs.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+#ifdef CONFIG_MACSEC
+#include "pae/ieee802_1x_kay.h"
+#endif /* CONFIG_MACSEC */
#include "utils/list.h"
#define HOSTAPD_CHAN_DISABLED 0x00000001
@@ -61,6 +65,10 @@
/* Filter unicast IP packets encrypted using the GTK */
#define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2)
+#define HOSTAPD_DFS_REGION_FCC 1
+#define HOSTAPD_DFS_REGION_ETSI 2
+#define HOSTAPD_DFS_REGION_JP 3
+
/**
* enum reg_change_initiator - Regulatory change initiator
*/
@@ -133,6 +141,29 @@ struct hostapd_channel_data {
unsigned int dfs_cac_ms;
};
+#define HE_MAX_NUM_SS 8
+#define HE_MAX_PHY_CAPAB_SIZE 3
+
+/**
+ * struct he_ppe_threshold - IEEE 802.11ax HE PPE Threshold
+ */
+struct he_ppe_threshold {
+ u32 numss_m1;
+ u32 ru_count;
+ u32 ppet16_ppet8_ru3_ru0[HE_MAX_NUM_SS];
+};
+
+/**
+ * struct he_capabilities - IEEE 802.11ax HE capabilities
+ */
+struct he_capabilities {
+ u8 he_supported;
+ u32 phy_cap[HE_MAX_PHY_CAPAB_SIZE];
+ u32 mac_cap;
+ u32 mcs;
+ struct he_ppe_threshold ppet;
+};
+
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
@@ -191,6 +222,11 @@ struct hostapd_hw_modes {
u8 vht_mcs_set[8];
unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
+
+ /**
+ * he_capab - HE (IEEE 802.11ax) capabilities
+ */
+ struct he_capabilities he_capab;
};
@@ -233,6 +269,9 @@ struct hostapd_hw_modes {
* @est_throughput: Estimated throughput in kbps (this is calculated during
* scan result processing if left zero by the driver wrapper)
* @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
+ * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms
+ * of TSF of the BSS specified by %tsf_bssid.
+ * @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
* @ie_len: length of the following IE field in octets
* @beacon_ie_len: length of the following Beacon IE field in octets
*
@@ -263,6 +302,8 @@ struct wpa_scan_res {
unsigned int age;
unsigned int est_throughput;
int snr;
+ u64 parent_tsf;
+ u8 tsf_bssid[ETH_ALEN];
size_t ie_len;
size_t beacon_ie_len;
/* Followed by ie_len + beacon_ie_len octets of IE data */
@@ -447,6 +488,15 @@ struct wpa_driver_scan_params {
unsigned int sched_scan_plans_num;
/**
+ * sched_scan_start_delay - Delay to use before starting the first scan
+ *
+ * Delay (in seconds) before scheduling first scan plan cycle. The
+ * driver may ignore this parameter and start immediately (or at any
+ * other time), if this feature is not supported.
+ */
+ u32 sched_scan_start_delay;
+
+ /**
* bssid - Specific BSSID to scan for
*
* This optional parameter can be used to replace the default wildcard
@@ -455,6 +505,80 @@ struct wpa_driver_scan_params {
*/
const u8 *bssid;
+ /**
+ * scan_cookie - Unique identification representing the scan request
+ *
+ * This scan_cookie carries a unique identification representing the
+ * scan request if the host driver/kernel supports concurrent scan
+ * requests. This cookie is returned from the corresponding driver
+ * interface.
+ *
+ * Note: Unlike other parameters in this structure, scan_cookie is used
+ * only to return information instead of setting parameters for the
+ * scan.
+ */
+ u64 scan_cookie;
+
+ /**
+ * duration - Dwell time on each channel
+ *
+ * This optional parameter can be used to set the dwell time on each
+ * channel. In TUs.
+ */
+ u16 duration;
+
+ /**
+ * duration_mandatory - Whether the specified duration is mandatory
+ *
+ * If this is set, the duration specified by the %duration field is
+ * mandatory (and the driver should reject the scan request if it is
+ * unable to comply with the specified duration), otherwise it is the
+ * maximum duration and the actual duration may be shorter.
+ */
+ unsigned int duration_mandatory:1;
+
+ /**
+ * relative_rssi_set - Whether relative RSSI parameters are set
+ */
+ unsigned int relative_rssi_set:1;
+
+ /**
+ * relative_rssi - Relative RSSI for reporting better BSSs
+ *
+ * Amount of RSSI by which a BSS should be better than the current
+ * connected BSS to report the new BSS to user space.
+ */
+ s8 relative_rssi;
+
+ /**
+ * relative_adjust_band - Band to which RSSI should be adjusted
+ *
+ * The relative_adjust_rssi should be added to the band specified
+ * by relative_adjust_band.
+ */
+ enum set_band relative_adjust_band;
+
+ /**
+ * relative_adjust_rssi - RSSI to be added to relative_adjust_band
+ *
+ * An amount of relative_band_rssi should be added to the BSSs that
+ * belong to the band specified by relative_adjust_band while comparing
+ * with other bands for BSS reporting.
+ */
+ s8 relative_adjust_rssi;
+
+ /**
+ * oce_scan
+ *
+ * Enable the following OCE scan features: (WFA OCE TechSpec v1.0)
+ * - Accept broadcast Probe Response frame.
+ * - Probe Request frame deferral and suppression.
+ * - Max Channel Time - driver fills FILS request params IE with
+ * Maximum Channel Time.
+ * - Send 1st Probe Request frame in rate of minimum 5.5 Mbps.
+ */
+ unsigned int oce_scan:1;
+
/*
* NOTE: Whenever adding new parameters here, please make sure
* wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -485,17 +609,18 @@ struct wpa_driver_auth_params {
int p2p;
/**
- * sae_data - SAE elements for Authentication frame
+ * auth_data - Additional elements for Authentication frame
*
* This buffer starts with the Authentication transaction sequence
- * number field. If SAE is not used, this pointer is %NULL.
+ * number field. If no special handling of such elements is needed, this
+ * pointer is %NULL. This is used with SAE and FILS.
*/
- const u8 *sae_data;
+ const u8 *auth_data;
/**
- * sae_data_len - Length of sae_data buffer in octets
+ * auth_data_len - Length of auth_data buffer in octets
*/
- size_t sae_data_len;
+ size_t auth_data_len;
};
/**
@@ -577,6 +702,68 @@ struct hostapd_freq_params {
};
/**
+ * struct wpa_driver_sta_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::sta_auth().
+ */
+struct wpa_driver_sta_auth_params {
+
+ /**
+ * own_addr - Source address and BSSID for authentication frame
+ */
+ const u8 *own_addr;
+
+ /**
+ * addr - MAC address of the station to associate
+ */
+ const u8 *addr;
+
+ /**
+ * seq - authentication sequence number
+ */
+ u16 seq;
+
+ /**
+ * status - authentication response status code
+ */
+ u16 status;
+
+ /**
+ * ie - authentication frame ie buffer
+ */
+ const u8 *ie;
+
+ /**
+ * len - ie buffer length
+ */
+ size_t len;
+
+ /**
+ * fils_auth - Indicates whether FILS authentication is being performed
+ */
+ int fils_auth;
+
+ /**
+ * fils_anonce - ANonce (required for FILS)
+ */
+ u8 fils_anonce[WPA_NONCE_LEN];
+
+ /**
+ * fils_snonce - SNonce (required for FILS)
+ */
+ u8 fils_snonce[WPA_NONCE_LEN];
+
+ /**
+ * fils_kek - key for encryption (required for FILS)
+ */
+ u8 fils_kek[WPA_KEK_MAX_LEN];
+
+ /**
+ * fils_kek_len - Length of the fils_kek in octets (required for FILS)
+ */
+ size_t fils_kek_len;
+};
+
+/**
* struct wpa_driver_associate_params - Association parameters
* Data for struct wpa_driver_ops::associate().
*/
@@ -639,7 +826,7 @@ struct wpa_driver_associate_params {
* WPA information element to be included in (Re)Association
* Request (including information element id and length). Use
* of this WPA IE is optional. If the driver generates the WPA
- * IE, it can use pairwise_suite, group_suite, and
+ * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and
* key_mgmt_suite to select proper algorithms. In this case,
* the driver has to notify wpa_supplicant about the used WPA
* IE by generating an event that the interface code will
@@ -679,6 +866,13 @@ struct wpa_driver_associate_params {
unsigned int group_suite;
/**
+ * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ unsigned int mgmt_group_suite;
+
+ /**
* key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
*
* This is usually ignored if @wpa_ie is used.
@@ -717,43 +911,6 @@ struct wpa_driver_associate_params {
enum mfp_options mgmt_frame_protection;
/**
- * ft_ies - IEEE 802.11r / FT information elements
- * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
- * for fast transition, this parameter is set to include the IEs that
- * are to be sent in the next FT Authentication Request message.
- * update_ft_ies() handler is called to update the IEs for further
- * FT messages in the sequence.
- *
- * The driver should use these IEs only if the target AP is advertising
- * the same mobility domain as the one included in the MDIE here.
- *
- * In ap_scan=2 mode, the driver can use these IEs when moving to a new
- * AP after the initial association. These IEs can only be used if the
- * target AP is advertising support for FT and is using the same MDIE
- * and SSID as the current AP.
- *
- * The driver is responsible for reporting the FT IEs received from the
- * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
- * type. update_ft_ies() handler will then be called with the FT IEs to
- * include in the next frame in the authentication sequence.
- */
- const u8 *ft_ies;
-
- /**
- * ft_ies_len - Length of ft_ies in bytes
- */
- size_t ft_ies_len;
-
- /**
- * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
- *
- * This value is provided to allow the driver interface easier access
- * to the current mobility domain. This value is set to %NULL if no
- * mobility domain is currently active.
- */
- const u8 *ft_md;
-
- /**
* passphrase - RSN passphrase for PSK
*
* This value is made available only for WPA/WPA2-Personal (PSK) and
@@ -882,6 +1039,64 @@ struct wpa_driver_associate_params {
* AP as usual. Valid for DMG network only.
*/
int pbss;
+
+ /**
+ * fils_kek - KEK for FILS association frame protection (AES-SIV)
+ */
+ const u8 *fils_kek;
+
+ /**
+ * fils_kek_len: Length of fils_kek in bytes
+ */
+ size_t fils_kek_len;
+
+ /**
+ * fils_nonces - Nonces for FILS association frame protection
+ * (AES-SIV AAD)
+ */
+ const u8 *fils_nonces;
+
+ /**
+ * fils_nonces_len: Length of fils_nonce in bytes
+ */
+ size_t fils_nonces_len;
+
+ /**
+ * fils_erp_username - Username part of keyName-NAI
+ */
+ const u8 *fils_erp_username;
+
+ /**
+ * fils_erp_username_len - Length of fils_erp_username in bytes
+ */
+ size_t fils_erp_username_len;
+
+ /**
+ * fils_erp_realm - Realm/domain name to use in FILS ERP
+ */
+ const u8 *fils_erp_realm;
+
+ /**
+ * fils_erp_realm_len - Length of fils_erp_realm in bytes
+ */
+ size_t fils_erp_realm_len;
+
+ /**
+ * fils_erp_next_seq_num - The next sequence number to use in FILS ERP
+ * messages
+ */
+ u16 fils_erp_next_seq_num;
+
+ /**
+ * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI
+ * specified by fils_erp_username@fils_erp_realm.
+ */
+ const u8 *fils_erp_rrk;
+
+ /**
+ * fils_erp_rrk_len - Length of fils_erp_rrk in bytes
+ */
+ size_t fils_erp_rrk_len;
};
enum hide_ssid {
@@ -940,6 +1155,22 @@ struct wpa_driver_ap_params {
int *basic_rates;
/**
+ * beacon_rate: Beacon frame data rate
+ *
+ * This parameter can be used to set a specific Beacon frame data rate
+ * for the BSS. The interpretation of this value depends on the
+ * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS). If
+ * beacon_rate == 0 and rate_type == 0 (BEACON_RATE_LEGACY), the default
+ * Beacon frame data rate is used.
+ */
+ unsigned int beacon_rate;
+
+ /**
+ * beacon_rate_type: Beacon data rate type (legacy/HT/VHT)
+ */
+ enum beacon_rate_type rate_type;
+
+ /**
* proberesp - Probe Response template
*
* This is used by drivers that reply to Probe Requests internally in
@@ -1115,6 +1346,27 @@ struct wpa_driver_ap_params {
* infrastructure BSS. Valid only for DMG network.
*/
int pbss;
+
+ /**
+ * multicast_to_unicast - Whether to use multicast_to_unicast
+ *
+ * If this is non-zero, the AP is requested to perform 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).
+ */
+ int multicast_to_unicast;
};
struct wpa_driver_mesh_bss_params {
@@ -1122,6 +1374,7 @@ struct wpa_driver_mesh_bss_params {
#define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002
#define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004
#define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008
+#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD 0x00000010
/*
* TODO: Other mesh configuration parameters would go here.
* See NL80211_MESHCONF_* for all the mesh config parameters.
@@ -1130,6 +1383,7 @@ struct wpa_driver_mesh_bss_params {
int auto_plinks;
int peer_link_timeout;
int max_peer_links;
+ int rssi_threshold;
u16 ht_opmode;
};
@@ -1164,6 +1418,12 @@ struct wpa_driver_capa {
#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080
#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100
#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200
+#define WPA_DRIVER_CAPA_KEY_MGMT_OWE 0x00000400
+#define WPA_DRIVER_CAPA_KEY_MGMT_DPP 0x00000800
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 0x00001000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
/** Bitfield of supported key management suites */
unsigned int key_mgmt;
@@ -1286,6 +1546,39 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL
/** Driver supports P2P Listen offload */
#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL
+/** Driver supports FILS */
+#define WPA_DRIVER_FLAGS_SUPPORT_FILS 0x0000040000000000ULL
+/** Driver supports Beacon frame TX rate configuration (legacy rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY 0x0000080000000000ULL
+/** Driver supports Beacon frame TX rate configuration (HT rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_HT 0x0000100000000000ULL
+/** Driver supports Beacon frame TX rate configuration (VHT rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_VHT 0x0000200000000000ULL
+/** Driver supports mgmt_tx with random TX address in non-connected state */
+#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA 0x0000400000000000ULL
+/** Driver supports mgmt_tx with random TX addr in connected state */
+#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED 0x0000800000000000ULL
+/** Driver supports better BSS reporting with sched_scan in connected mode */
+#define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI 0x0001000000000000ULL
+/** Driver supports HE capabilities */
+#define WPA_DRIVER_FLAGS_HE_CAPABILITIES 0x0002000000000000ULL
+/** Driver supports FILS shared key offload */
+#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD 0x0004000000000000ULL
+/** Driver supports all OCE STA specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_STA 0x0008000000000000ULL
+/** Driver supports all OCE AP specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_AP 0x0010000000000000ULL
+/**
+ * Driver supports all OCE STA-CFON specific mandatory features only.
+ * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the
+ * userspace shall assume that this driver may not support all OCE AP
+ * functionality but can support only OCE STA-CFON functionality.
+ */
+#define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL
+/** Driver supports MFP-optional in the connect command */
+#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL
+/** Driver is a self-managed regulatory device */
+#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL
u64 flags;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -1386,6 +1679,11 @@ struct wpa_driver_capa {
*/
#define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010
+/** Driver supports setting the scan dwell time */
+#define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL 0x00000020
+/** Driver supports Beacon Report Measurement */
+#define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT 0x00000040
+
u32 rrm_flags;
/* Driver concurrency capabilities */
@@ -1402,18 +1700,35 @@ struct wpa_driver_capa {
struct hostapd_data;
+#define STA_DRV_DATA_TX_MCS BIT(0)
+#define STA_DRV_DATA_RX_MCS BIT(1)
+#define STA_DRV_DATA_TX_VHT_MCS BIT(2)
+#define STA_DRV_DATA_RX_VHT_MCS BIT(3)
+#define STA_DRV_DATA_TX_VHT_NSS BIT(4)
+#define STA_DRV_DATA_RX_VHT_NSS BIT(5)
+#define STA_DRV_DATA_TX_SHORT_GI BIT(6)
+#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
+#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
+
struct hostap_sta_driver_data {
unsigned long rx_packets, tx_packets;
unsigned long long rx_bytes, tx_bytes;
int bytes_64bit; /* whether 64-bit byte counters are supported */
unsigned long current_tx_rate;
+ unsigned long current_rx_rate;
unsigned long inactive_msec;
- unsigned long flags;
+ unsigned long flags; /* bitfield of STA_DRV_DATA_* */
unsigned long num_ps_buf_frames;
unsigned long tx_retry_failed;
unsigned long tx_retry_count;
- int last_rssi;
- int last_ack_rssi;
+ s8 last_ack_rssi;
+ s8 signal;
+ u8 rx_vhtmcs;
+ u8 tx_vhtmcs;
+ u8 rx_mcs;
+ u8 tx_mcs;
+ u8 rx_vht_nss;
+ u8 tx_vht_nss;
};
struct hostapd_sta_add_params {
@@ -1576,6 +1891,17 @@ enum wnm_oper {
WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */
};
+/* enum smps_mode - SMPS mode definitions */
+enum smps_mode {
+ SMPS_AUTOMATIC,
+ SMPS_OFF,
+ SMPS_DYNAMIC,
+ SMPS_STATIC,
+
+ /* Keep last */
+ SMPS_INVALID,
+};
+
/* enum chan_width - Channel width definitions */
enum chan_width {
CHAN_WIDTH_20_NOHT,
@@ -1587,8 +1913,21 @@ enum chan_width {
CHAN_WIDTH_UNKNOWN
};
+#define WPA_INVALID_NOISE 9999
+
/**
* struct wpa_signal_info - Information about channel signal quality
+ * @frequency: control frequency
+ * @above_threshold: true if the above threshold was crossed
+ * (relevant for a CQM event)
+ * @current_signal: in dBm
+ * @avg_signal: in dBm
+ * @avg_beacon_signal: in dBm
+ * @current_noise: %WPA_INVALID_NOISE if not supported
+ * @current_txrate: current TX rate
+ * @chanwidth: channel width
+ * @center_frq1: center frequency for the first segment
+ * @center_frq2: center frequency for the second segment (if relevant)
*/
struct wpa_signal_info {
u32 frequency;
@@ -1720,6 +2059,67 @@ struct drv_acs_params {
const int *freq_list;
};
+struct wpa_bss_trans_info {
+ u8 mbo_transition_reason;
+ u8 n_candidates;
+ u8 *bssid;
+};
+
+struct wpa_bss_candidate_info {
+ u8 num;
+ struct candidate_list {
+ u8 bssid[ETH_ALEN];
+ u8 is_accept;
+ u32 reject_reason;
+ } *candidates;
+};
+
+struct wpa_pmkid_params {
+ const u8 *bssid;
+ const u8 *ssid;
+ size_t ssid_len;
+ const u8 *fils_cache_id;
+ const u8 *pmkid;
+ const u8 *pmk;
+ size_t pmk_len;
+};
+
+/* Mask used to specify which connection parameters have to be updated */
+enum wpa_drv_update_connect_params_mask {
+ WPA_DRV_UPDATE_ASSOC_IES = BIT(0),
+ WPA_DRV_UPDATE_FILS_ERP_INFO = BIT(1),
+ WPA_DRV_UPDATE_AUTH_TYPE = BIT(2),
+};
+
+/**
+ * struct external_auth - External authentication trigger parameters
+ *
+ * These are used across the external authentication request and event
+ * interfaces.
+ * @action: Action type / trigger for external authentication. Only significant
+ * for the event interface.
+ * @bssid: BSSID of the peer with which the authentication has to happen. Used
+ * by both the request and event interface.
+ * @ssid: SSID of the AP. Used by both the request and event interface.
+ * @ssid_len: SSID length in octets.
+ * @key_mgmt_suite: AKM suite of the respective authentication. Optional for
+ * the request interface.
+ * @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication,
+ * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give
+ * the real status code for failures. Used only for the request interface
+ * from user space to the driver.
+ */
+struct external_auth {
+ enum {
+ EXT_AUTH_START,
+ EXT_AUTH_ABORT,
+ } action;
+ u8 bssid[ETH_ALEN];
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ unsigned int key_mgmt_suite;
+ u16 status;
+};
/**
* struct wpa_driver_ops - Driver interface API definition
@@ -1902,13 +2302,14 @@ struct wpa_driver_ops {
/**
* add_pmkid - Add PMKSA cache entry to the driver
* @priv: private driver interface data
- * @bssid: BSSID for the PMKSA cache entry
- * @pmkid: PMKID for the PMKSA cache entry
+ * @params: PMKSA parameters
*
* Returns: 0 on success, -1 on failure
*
* This function is called when a new PMK is received, as a result of
- * either normal authentication or RSN pre-authentication.
+ * either normal authentication or RSN pre-authentication. The PMKSA
+ * parameters are either a set of bssid, pmkid, and pmk; or a set of
+ * ssid, fils_cache_id, pmkid, and pmk.
*
* If the driver generates RSN IE, i.e., it does not use wpa_ie in
* associate(), add_pmkid() can be used to add new PMKSA cache entries
@@ -1916,18 +2317,18 @@ struct wpa_driver_ops {
* driver_ops function does not need to be implemented. Likewise, if
* the driver does not support WPA, this function is not needed.
*/
- int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+ int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params);
/**
* remove_pmkid - Remove PMKSA cache entry to the driver
* @priv: private driver interface data
- * @bssid: BSSID for the PMKSA cache entry
- * @pmkid: PMKID for the PMKSA cache entry
+ * @params: PMKSA parameters
*
* Returns: 0 on success, -1 on failure
*
* This function is called when the supplicant drops a PMKSA cache
- * entry for any reason.
+ * entry for any reason. The PMKSA parameters are either a set of
+ * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid.
*
* If the driver generates RSN IE, i.e., it does not use wpa_ie in
* associate(), remove_pmkid() can be used to synchronize PMKSA caches
@@ -1936,7 +2337,7 @@ struct wpa_driver_ops {
* implemented. Likewise, if the driver does not support WPA, this
* function is not needed.
*/
- int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+ int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params);
/**
* flush_pmkid - Flush PMKSA cache
@@ -2051,12 +2452,13 @@ struct wpa_driver_ops {
* @priv: Private driver interface data
* @num_modes: Variable for returning the number of returned modes
* flags: Variable for returning hardware feature flags
+ * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*)
* Returns: Pointer to allocated hardware data on success or %NULL on
* failure. Caller is responsible for freeing this.
*/
struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
u16 *num_modes,
- u16 *flags);
+ u16 *flags, u8 *dfs);
/**
* send_mlme - Send management frame from MLME
@@ -2673,6 +3075,9 @@ struct wpa_driver_ops {
* transmitted on that channel; alternatively the frame may be sent on
* the current operational channel (if in associated state in station
* mode or while operating as an AP.)
+ *
+ * If @src differs from the device MAC address, use of a random
+ * transmitter address is requested for this message exchange.
*/
int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
const u8 *dst, const u8 *src, const u8 *bssid,
@@ -3058,19 +3463,13 @@ struct wpa_driver_ops {
/**
* sta_auth - Station authentication indication
- * @priv: Private driver interface data
- * @own_addr: Source address and BSSID for authentication frame
- * @addr: MAC address of the station to associate
- * @seq: authentication sequence number
- * @status: authentication response status code
- * @ie: authentication frame ie buffer
- * @len: ie buffer length
+ * @priv: private driver interface data
+ * @params: Station authentication parameters
*
- * This function indicates the driver to send Authentication frame
- * to the station.
+ * Returns: 0 on success, -1 on failure
*/
- int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
- u16 seq, u16 status, const u8 *ie, size_t len);
+ int (*sta_auth)(void *priv,
+ struct wpa_driver_sta_auth_params *params);
/**
* add_tspec - Add traffic stream
@@ -3282,6 +3681,17 @@ struct wpa_driver_ops {
int (*roaming)(void *priv, int allowed, const u8 *bssid);
/**
+ * disable_fils - Enable/disable FILS feature
+ * @priv: Private driver interface data
+ * @disable: 0-enable and 1-disable FILS feature
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback can be used to configure driver and below layers to
+ * enable/disable all FILS features.
+ */
+ int (*disable_fils)(void *priv, int disable);
+
+ /**
* set_mac_addr - Set MAC address
* @priv: Private driver interface data
* @addr: MAC address to use or %NULL for setting back to permanent
@@ -3295,6 +3705,14 @@ struct wpa_driver_ops {
int (*macsec_deinit)(void *priv);
/**
+ * macsec_get_capability - Inform MKA of this driver's capability
+ * @priv: Private driver interface data
+ * @cap: Driver's capability
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
+
+ /**
* enable_protect_frames - Set protect frames status
* @priv: Private driver interface data
* @enabled: TRUE = protect frames enabled
@@ -3304,6 +3722,15 @@ struct wpa_driver_ops {
int (*enable_protect_frames)(void *priv, Boolean enabled);
/**
+ * enable_encrypt - Set encryption status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = encrypt outgoing traffic
+ * FALSE = integrity-only protection on outgoing traffic
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*enable_encrypt)(void *priv, Boolean enabled);
+
+ /**
* set_replay_protect - Set replay protect status and window size
* @priv: Private driver interface data
* @enabled: TRUE = replay protect enabled
@@ -3333,155 +3760,129 @@ struct wpa_driver_ops {
/**
* get_receive_lowest_pn - Get receive lowest pn
* @priv: Private driver interface data
- * @channel: secure channel
- * @an: association number
- * @lowest_pn: lowest accept pn
+ * @sa: secure association
* Returns: 0 on success, -1 on failure (or if not supported)
*/
- int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an,
- u32 *lowest_pn);
+ int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa);
/**
* get_transmit_next_pn - Get transmit next pn
* @priv: Private driver interface data
- * @channel: secure channel
- * @an: association number
- * @next_pn: next pn
+ * @sa: secure association
* Returns: 0 on success, -1 on failure (or if not supported)
*/
- int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an,
- u32 *next_pn);
+ int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa);
/**
* set_transmit_next_pn - Set transmit next pn
* @priv: Private driver interface data
- * @channel: secure channel
- * @an: association number
- * @next_pn: next pn
+ * @sa: secure association
* Returns: 0 on success, -1 on failure (or if not supported)
*/
- int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an,
- u32 next_pn);
-
- /**
- * get_available_receive_sc - get available receive channel
- * @priv: Private driver interface data
- * @channel: secure channel
- * Returns: 0 on success, -1 on failure (or if not supported)
- */
- int (*get_available_receive_sc)(void *priv, u32 *channel);
+ int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
/**
* create_receive_sc - create secure channel for receiving
* @priv: Private driver interface data
- * @channel: secure channel
- * @sci_addr: secure channel identifier - address
- * @sci_port: secure channel identifier - port
+ * @sc: secure channel
* @conf_offset: confidentiality offset (0, 30, or 50)
* @validation: frame validation policy (0 = Disabled, 1 = Checked,
* 2 = Strict)
* Returns: 0 on success, -1 on failure (or if not supported)
*/
- int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr,
- u16 sci_port, unsigned int conf_offset,
+ int (*create_receive_sc)(void *priv, struct receive_sc *sc,
+ unsigned int conf_offset,
int validation);
/**
* delete_receive_sc - delete secure connection for receiving
* @priv: private driver interface data from init()
- * @channel: secure channel
+ * @sc: secure channel
* Returns: 0 on success, -1 on failure
*/
- int (*delete_receive_sc)(void *priv, u32 channel);
+ int (*delete_receive_sc)(void *priv, struct receive_sc *sc);
/**
* create_receive_sa - create secure association for receive
* @priv: private driver interface data from init()
- * @channel: secure channel
- * @an: association number
- * @lowest_pn: the lowest packet number can be received
- * @sak: the secure association key
+ * @sa: secure association
* Returns: 0 on success, -1 on failure
*/
- int (*create_receive_sa)(void *priv, u32 channel, u8 an,
- u32 lowest_pn, const u8 *sak);
+ int (*create_receive_sa)(void *priv, struct receive_sa *sa);
/**
- * enable_receive_sa - enable the SA for receive
- * @priv: private driver interface data from init()
- * @channel: secure channel
- * @an: association number
+ * delete_receive_sa - Delete secure association for receive
+ * @priv: Private driver interface data from init()
+ * @sa: Secure association
* Returns: 0 on success, -1 on failure
*/
- int (*enable_receive_sa)(void *priv, u32 channel, u8 an);
+ int (*delete_receive_sa)(void *priv, struct receive_sa *sa);
/**
- * disable_receive_sa - disable SA for receive
+ * enable_receive_sa - enable the SA for receive
* @priv: private driver interface data from init()
- * @channel: secure channel index
- * @an: association number
+ * @sa: secure association
* Returns: 0 on success, -1 on failure
*/
- int (*disable_receive_sa)(void *priv, u32 channel, u8 an);
+ int (*enable_receive_sa)(void *priv, struct receive_sa *sa);
/**
- * get_available_transmit_sc - get available transmit channel
- * @priv: Private driver interface data
- * @channel: secure channel
- * Returns: 0 on success, -1 on failure (or if not supported)
+ * disable_receive_sa - disable SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
*/
- int (*get_available_transmit_sc)(void *priv, u32 *channel);
+ int (*disable_receive_sa)(void *priv, struct receive_sa *sa);
/**
* create_transmit_sc - create secure connection for transmit
* @priv: private driver interface data from init()
- * @channel: secure channel
- * @sci_addr: secure channel identifier - address
- * @sci_port: secure channel identifier - port
+ * @sc: secure channel
+ * @conf_offset: confidentiality offset (0, 30, or 50)
* Returns: 0 on success, -1 on failure
*/
- int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr,
- u16 sci_port, unsigned int conf_offset);
+ int (*create_transmit_sc)(void *priv, struct transmit_sc *sc,
+ unsigned int conf_offset);
/**
* delete_transmit_sc - delete secure connection for transmit
* @priv: private driver interface data from init()
- * @channel: secure channel
+ * @sc: secure channel
* Returns: 0 on success, -1 on failure
*/
- int (*delete_transmit_sc)(void *priv, u32 channel);
+ int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc);
/**
* create_transmit_sa - create secure association for transmit
* @priv: private driver interface data from init()
- * @channel: secure channel index
- * @an: association number
- * @next_pn: the packet number used as next transmit packet
- * @confidentiality: True if the SA is to provide confidentiality
- * as well as integrity
- * @sak: the secure association key
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+ /**
+ * delete_transmit_sa - Delete secure association for transmit
+ * @priv: Private driver interface data from init()
+ * @sa: Secure association
* Returns: 0 on success, -1 on failure
*/
- int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn,
- Boolean confidentiality, const u8 *sak);
+ int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa);
/**
* enable_transmit_sa - enable SA for transmit
* @priv: private driver interface data from init()
- * @channel: secure channel
- * @an: association number
+ * @sa: secure association
* Returns: 0 on success, -1 on failure
*/
- int (*enable_transmit_sa)(void *priv, u32 channel, u8 an);
+ int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa);
/**
* disable_transmit_sa - disable SA for transmit
* @priv: private driver interface data from init()
- * @channel: secure channel
- * @an: association number
+ * @sa: secure association
* Returns: 0 on success, -1 on failure
*/
- int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
+ int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa);
#endif /* CONFIG_MACSEC */
/**
@@ -3555,9 +3956,12 @@ struct wpa_driver_ops {
/**
* abort_scan - Request the driver to abort an ongoing scan
* @priv: Private driver interface data
+ * @scan_cookie: Cookie identifying the scan request. This is used only
+ * when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN
+ * was used to trigger scan. Otherwise, 0 is used.
* Returns 0 on success, -1 on failure
*/
- int (*abort_scan)(void *priv);
+ int (*abort_scan)(void *priv, u64 scan_cookie);
/**
* configure_data_frame_filters - Request to configure frame filters
@@ -3623,8 +4027,72 @@ struct wpa_driver_ops {
*/
int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len);
-};
+ /**
+ * set_tdls_mode - Set TDLS trigger mode to the host driver
+ * @priv: Private driver interface data
+ * @tdls_external_control: Represents if TDLS external trigger control
+ * mode is enabled/disabled.
+ *
+ * This optional callback can be used to configure the TDLS external
+ * trigger control mode to the host driver.
+ */
+ int (*set_tdls_mode)(void *priv, int tdls_external_control);
+ /**
+ * get_bss_transition_status - Get candidate BSS's transition status
+ * @priv: Private driver interface data
+ * @params: Candidate BSS list
+ *
+ * Get the accept or reject reason code for a list of BSS transition
+ * candidates.
+ */
+ struct wpa_bss_candidate_info *
+ (*get_bss_transition_status)(void *priv,
+ struct wpa_bss_trans_info *params);
+ /**
+ * ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+ * @priv: Private driver interface data
+ * @ignore_disallow: 0 to not ignore, 1 to ignore
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
+
+ /**
+ * set_bssid_blacklist - Set blacklist of BSSIDs to the driver
+ * @priv: Private driver interface data
+ * @num_bssid: Number of blacklist BSSIDs
+ * @bssids: List of blacklisted BSSIDs
+ */
+ int (*set_bssid_blacklist)(void *priv, unsigned int num_bssid,
+ const u8 *bssid);
+
+ /**
+ * update_connect_params - Update the connection parameters
+ * @priv: Private driver interface data
+ * @params: Association parameters
+ * @mask: Bit mask indicating which parameters in @params have to be
+ * updated
+ * Returns: 0 on success, -1 on failure
+ *
+ * Update the connection parameters when in connected state so that the
+ * driver uses the updated parameters for subsequent roaming. This is
+ * used only with drivers that implement internal BSS selection and
+ * roaming.
+ */
+ int (*update_connect_params)(
+ void *priv, struct wpa_driver_associate_params *params,
+ enum wpa_drv_update_connect_params_mask mask);
+
+ /**
+ * send_external_auth_status - Indicate the status of external
+ * authentication processing to the host driver.
+ * @priv: Private driver interface data
+ * @params: Status of authentication processing.
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_external_auth_status)(void *priv,
+ struct external_auth *params);
+};
/**
* enum wpa_event_type - Event type for wpa_supplicant_event() calls
@@ -3734,17 +4202,6 @@ enum wpa_event_type {
EVENT_PMKID_CANDIDATE,
/**
- * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
- *
- * This event can be used to inform wpa_supplicant about desire to set
- * up secure direct link connection between two stations as defined in
- * IEEE 802.11e with a new PeerKey mechanism that replaced the original
- * STAKey negotiation. The caller will need to set peer address for the
- * event.
- */
- EVENT_STKSTART,
-
- /**
* EVENT_TDLS - Request TDLS operation
*
* This event can be used to request a TDLS operation to be performed.
@@ -4043,7 +4500,7 @@ enum wpa_event_type {
* EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
*
* The CAC was not successful, and the channel remains in the previous
- * state. This may happen due to a radar beeing detected or other
+ * state. This may happen due to a radar being detected or other
* external influences.
*/
EVENT_DFS_CAC_ABORTED,
@@ -4112,6 +4569,65 @@ enum wpa_event_type {
* EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped
*/
EVENT_P2P_LO_STOP,
+
+ /**
+ * EVENT_BEACON_LOSS - Beacon loss detected
+ *
+ * This event indicates that no Beacon frames has been received from
+ * the current AP. This may indicate that the AP is not anymore in
+ * range.
+ */
+ EVENT_BEACON_LOSS,
+
+ /**
+ * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check
+ * done previously (Pre-CAC) on the channel has expired. This would
+ * normally be on a non-ETSI DFS regulatory domain. DFS state of the
+ * channel will be moved from available to usable. A new CAC has to be
+ * performed before start operating on this channel.
+ */
+ EVENT_DFS_PRE_CAC_EXPIRED,
+
+ /**
+ * EVENT_EXTERNAL_AUTH - This event interface is used by host drivers
+ * that do not define separate commands for authentication and
+ * association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11
+ * authentication to wpa_supplicant. This event carries all the
+ * necessary information from the host driver for the authentication to
+ * happen.
+ */
+ EVENT_EXTERNAL_AUTH,
+
+ /**
+ * EVENT_PORT_AUTHORIZED - Notification that a connection is authorized
+ *
+ * This event should be indicated when the driver completes the 4-way
+ * handshake. This event should be preceded by an EVENT_ASSOC that
+ * indicates the completion of IEEE 802.11 association.
+ */
+ EVENT_PORT_AUTHORIZED,
+
+ /**
+ * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode
+ * change event.
+ */
+ EVENT_STATION_OPMODE_CHANGED,
+
+ /**
+ * EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed
+ *
+ * This event is emitted when the MAC changes while the interface is
+ * enabled. When an interface was disabled and becomes enabled, it
+ * must be always assumed that the MAC possibly changed.
+ */
+ EVENT_INTERFACE_MAC_CHANGED,
+
+ /**
+ * EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status
+ *
+ * This event is emitted when an interface is added/removed for WDS STA.
+ */
+ EVENT_WDS_STA_INTERFACE_STATUS,
};
@@ -4204,6 +4720,16 @@ union wpa_event_data {
size_t resp_ies_len;
/**
+ * resp_frame - (Re)Association Response frame
+ */
+ const u8 *resp_frame;
+
+ /**
+ * resp_frame_len - (Re)Association Response frame length
+ */
+ size_t resp_frame_len;
+
+ /**
* beacon_ies - Beacon or Probe Response IEs
*
* Optional Beacon/ProbeResp data: IEs included in Beacon or
@@ -4280,6 +4806,8 @@ union wpa_event_data {
/**
* ptk_kek - The derived PTK KEK
+ * This is used in key management offload and also in FILS SK
+ * offload.
*/
const u8 *ptk_kek;
@@ -4293,6 +4821,36 @@ union wpa_event_data {
* 0 = unknown, 1 = unchanged, 2 = changed
*/
u8 subnet_status;
+
+ /**
+ * The following information is used in FILS SK offload
+ * @fils_erp_next_seq_num
+ * @fils_pmk
+ * @fils_pmk_len
+ * @fils_pmkid
+ */
+
+ /**
+ * fils_erp_next_seq_num - The next sequence number to use in
+ * FILS ERP messages
+ */
+ u16 fils_erp_next_seq_num;
+
+ /**
+ * fils_pmk - A new PMK if generated in case of FILS
+ * authentication
+ */
+ const u8 *fils_pmk;
+
+ /**
+ * fils_pmk_len - Length of fils_pmk
+ */
+ size_t fils_pmk_len;
+
+ /**
+ * fils_pmkid - PMKID used or generated in FILS authentication
+ */
+ const u8 *fils_pmkid;
} assoc_info;
/**
@@ -4389,13 +4947,6 @@ union wpa_event_data {
} pmkid_candidate;
/**
- * struct stkstart - Data for EVENT_STKSTART
- */
- struct stkstart {
- u8 peer[ETH_ALEN];
- } stkstart;
-
- /**
* struct tdls - Data for EVENT_TDLS
*/
struct tdls {
@@ -4503,6 +5054,17 @@ union wpa_event_data {
* than explicit rejection response from the AP.
*/
int timed_out;
+
+ /**
+ * timeout_reason - Reason for the timeout
+ */
+ const char *timeout_reason;
+
+ /**
+ * fils_erp_next_seq_num - The next sequence number to use in
+ * FILS ERP messages
+ */
+ u16 fils_erp_next_seq_num;
} assoc_reject;
struct timeout_event {
@@ -4586,6 +5148,11 @@ union wpa_event_data {
* @external_scan: Whether the scan info is for an external scan
* @nl_scan_event: 1 if the source of this scan event is a normal scan,
* 0 if the source of the scan event is a vendor scan
+ * @scan_start_tsf: Time when the scan started in terms of TSF of the
+ * BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
+ * is set.
*/
struct scan_info {
int aborted;
@@ -4595,6 +5162,8 @@ union wpa_event_data {
size_t num_ssids;
int external_scan;
int nl_scan_event;
+ u64 scan_start_tsf;
+ u8 scan_start_tsf_bssid[ETH_ALEN];
} scan_info;
/**
@@ -4684,9 +5253,12 @@ union wpa_event_data {
/**
* struct low_ack - Data for EVENT_STATION_LOW_ACK events
* @addr: station address
+ * @num_packets: Number of packets lost (consecutive packets not
+ * acknowledged)
*/
struct low_ack {
u8 addr[ETH_ALEN];
+ u32 num_packets;
} low_ack;
/**
@@ -4858,6 +5430,37 @@ union wpa_event_data {
P2P_LO_STOPPED_REASON_NOT_SUPPORTED,
} reason_code;
} p2p_lo_stop;
+
+ /* For EVENT_EXTERNAL_AUTH */
+ struct external_auth external_auth;
+
+ /**
+ * struct sta_opmode - Station's operation mode change event
+ * @addr: The station MAC address
+ * @smps_mode: SMPS mode of the station
+ * @chan_width: Channel width of the station
+ * @rx_nss: RX_NSS of the station
+ *
+ * This is used as data with EVENT_STATION_OPMODE_CHANGED.
+ */
+ struct sta_opmode {
+ const u8 *addr;
+ enum smps_mode smps_mode;
+ enum chan_width chan_width;
+ u8 rx_nss;
+ } sta_opmode;
+
+ /**
+ * struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS.
+ */
+ struct wds_sta_interface {
+ const u8 *sta_addr;
+ const char *ifname;
+ enum {
+ INTERFACE_ADDED,
+ INTERFACE_REMOVED
+ } istatus;
+ } wds_sta_interface;
};
/**
@@ -4973,6 +5576,10 @@ extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
/* driver_macsec_qca.c */
extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops;
#endif /* CONFIG_DRIVER_MACSEC_QCA */
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
+/* driver_macsec_linux.c */
+extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops;
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
#ifdef CONFIG_DRIVER_ROBOSWITCH
/* driver_roboswitch.c */
extern const struct wpa_driver_ops wpa_driver_roboswitch_ops;
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index a88345fc92f8..62f5baad6361 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -36,6 +36,10 @@
#include "ieee80211_external.h"
+/* Avoid conflicting definition from the driver header files with
+ * common/wpa_common.h */
+#undef WPA_OUI_TYPE
+
#ifdef CONFIG_WPS
#include <netpacket/packet.h>
@@ -55,7 +59,7 @@
#include "netlink.h"
#include "linux_ioctl.h"
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS)
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) || defined(CONFIG_FILS)
#define ATHEROS_USE_RAW_RECEIVE
#endif
@@ -70,6 +74,7 @@ struct atheros_driver_data {
int ioctl_sock; /* socket for ioctl() use */
struct netlink_data *netlink;
int we_version;
+ int fils_en; /* FILS enable/disable in driver */
u8 acct_mac[ETH_ALEN];
struct hostap_sta_driver_data acct_data;
@@ -178,6 +183,25 @@ static const char * athr_get_param_name(int op)
}
+#ifdef CONFIG_FILS
+static int
+get80211param(struct atheros_driver_data *drv, int op, int *data)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.mode = op;
+
+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0)
+ return -1;
+
+ *data = iwr.u.mode;
+ return 0;
+}
+#endif /* CONFIG_FILS */
+
+
static int
set80211priv(struct atheros_driver_data *drv, int op, void *data, int len)
{
@@ -692,10 +716,14 @@ atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
wpabuf_free(drv->wpa_ie);
- drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+ if (ie)
+ drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+ else
+ drv->wpa_ie = NULL;
app_ie = (struct ieee80211req_getset_appiebuf *) buf;
- os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
+ if (ie)
+ os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
app_ie->app_buflen = ie_len;
app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
@@ -903,6 +931,12 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
break;
os_memset(&event, 0, sizeof(event));
+ if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) {
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+ break;
+ }
os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
@@ -931,11 +965,11 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv)
#ifdef CONFIG_WPS
filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
#endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
IEEE80211_FILTER_TYPE_AUTH |
IEEE80211_FILTER_TYPE_ACTION);
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
#ifdef CONFIG_WNM
filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
#endif /* CONFIG_WNM */
@@ -949,12 +983,12 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv)
return ret;
}
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
atheros_raw_receive, drv, 1);
if (drv->sock_raw == NULL)
return -1;
-#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */
return ret;
}
@@ -981,7 +1015,8 @@ atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
beac_ie->app_frmtype = frametype;
beac_ie->app_buflen = len;
- os_memcpy(&(beac_ie->app_buf[0]), ie, len);
+ if (ie)
+ os_memcpy(&(beac_ie->app_buf[0]), ie, len);
/* append the WPA/RSN IE if it is set already */
if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
@@ -1034,32 +1069,56 @@ atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
#define atheros_set_ap_wps_ie NULL
#endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
static int
-atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
- u16 status_code, const u8 *ie, size_t len)
+atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params)
{
struct atheros_driver_data *drv = priv;
struct ieee80211req_mlme mlme;
int ret;
wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
- __func__, ether_sprintf(addr), status_code);
+ __func__, ether_sprintf(params->addr), params->status);
+#ifdef CONFIG_FILS
+ /* Copy FILS AAD parameters if the driver supports FILS */
+ if (params->fils_auth && drv->fils_en) {
+ wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS",
+ __func__);
+ os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce,
+ IEEE80211_FILS_NONCE_LEN);
+ os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce,
+ IEEE80211_FILS_NONCE_LEN);
+ os_memcpy(mlme.fils_aad.kek, params->fils_kek,
+ IEEE80211_MAX_WPA_KEK_LEN);
+ mlme.fils_aad.kek_len = params->fils_kek_len;
+ mlme.im_op = IEEE80211_MLME_AUTH_FILS;
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce",
+ mlme.fils_aad.ANonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce",
+ mlme.fils_aad.SNonce, FILS_NONCE_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: KEK",
+ mlme.fils_aad.kek, mlme.fils_aad.kek_len);
+ } else {
+ mlme.im_op = IEEE80211_MLME_AUTH;
+ }
+#else /* CONFIG_FILS */
mlme.im_op = IEEE80211_MLME_AUTH;
- mlme.im_reason = status_code;
- mlme.im_seq = seq;
- os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- mlme.im_optie_len = len;
- if (len) {
- if (len < IEEE80211_MAX_OPT_IE) {
- os_memcpy(mlme.im_optie, ie, len);
+#endif /* CONFIG_FILS */
+
+ mlme.im_reason = params->status;
+ mlme.im_seq = params->seq;
+ os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN);
+ mlme.im_optie_len = params->len;
+ if (params->len) {
+ if (params->len < IEEE80211_MAX_OPT_IE) {
+ os_memcpy(mlme.im_optie, params->ie, params->len);
} else {
wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
"opt_ie STA (addr " MACSTR " reason %d, "
"ie_len %d)",
- __func__, MAC2STR(addr), status_code,
- (int) len);
+ __func__, MAC2STR(params->addr),
+ params->status, (int) params->len);
return -1;
}
}
@@ -1067,7 +1126,7 @@ atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
if (ret < 0) {
wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
" reason %d)",
- __func__, MAC2STR(addr), status_code);
+ __func__, MAC2STR(params->addr), params->status);
}
return ret;
}
@@ -1110,7 +1169,7 @@ atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr,
}
return ret;
}
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
static void
atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
@@ -1257,7 +1316,7 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
atheros_raw_receive(drv, NULL,
(u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
#endif /* CONFIG_WPS */
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
} else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) {
/* Format: "Manage.assoc_req <frame len>" | zero padding |
* frame */
@@ -1270,20 +1329,20 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
}
atheros_raw_receive(drv, NULL,
(u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
- } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) {
+ } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) {
/* Format: "Manage.auth <frame len>" | zero padding | frame */
int len = atoi(custom + 12);
- if (len < 0 ||
- MGMT_FRAM_TAG_SIZE + len > end - custom) {
+ if (len < 0 ||
+ MGMT_FRAM_TAG_SIZE + len > end - custom) {
wpa_printf(MSG_DEBUG,
"Invalid Manage.auth event length %d", len);
return;
}
atheros_raw_receive(drv, NULL,
(u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R || CONFIG_FILS */
#ifdef ATHEROS_USE_RAW_RECEIVE
- } else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
+ } else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
/* Format: "Manage.assoc_req <frame len>" | zero padding | frame
*/
int len = atoi(custom + 14);
@@ -1299,6 +1358,40 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
}
}
+
+static void send_action_cb_event(struct atheros_driver_data *drv,
+ char *data, size_t data_len)
+{
+ union wpa_event_data event;
+ struct ieee80211_send_action_cb *sa;
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ if (data_len < sizeof(*sa) + 24) {
+ wpa_printf(MSG_DEBUG,
+ "athr: Too short event message (data_len=%d sizeof(*sa)=%d)",
+ (int) data_len, (int) sizeof(*sa));
+ wpa_hexdump(MSG_DEBUG, "athr: Short event message",
+ data, data_len);
+ return;
+ }
+
+ sa = (struct ieee80211_send_action_cb *) data;
+
+ hdr = (const struct ieee80211_hdr *) (sa + 1);
+ fc = le_to_host16(hdr->frame_control);
+
+ os_memset(&event, 0, sizeof(event));
+ event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+ event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+ event.tx_status.dst = sa->dst_addr;
+ event.tx_status.data = (const u8 *) hdr;
+ event.tx_status.data_len = data_len - sizeof(*sa);
+ event.tx_status.ack = sa->ack;
+ wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
+}
+
+
/*
* Handle size of data problem. WEXT only allows data of 256 bytes for custom
* events, and p2p data can be much bigger. So the athr driver sends a small
@@ -1363,6 +1456,11 @@ static void fetch_pending_big_events(struct atheros_driver_data *drv)
&event);
continue;
}
+ } else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) {
+ wpa_printf(MSG_DEBUG,
+ "%s: ACTION_CB frame_type=%u len=%zu",
+ __func__, frame_type, data_len);
+ send_action_cb_event(drv, (void *) mgmt, data_len);
} else {
wpa_printf(MSG_DEBUG, "athr: %s unknown type %d",
__func__, frame_type);
@@ -1376,6 +1474,10 @@ atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv,
int opcode, char *buf, int len)
{
switch (opcode) {
+ case IEEE80211_EV_P2P_SEND_ACTION_CB:
+ wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB");
+ fetch_pending_big_events(drv);
+ break;
case IEEE80211_EV_RX_MGMT:
wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT");
fetch_pending_big_events(drv);
@@ -1603,6 +1705,27 @@ handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
len - sizeof(struct l2_ethhdr));
}
+
+static void atheros_read_fils_cap(struct atheros_driver_data *drv)
+{
+ int fils = 0;
+
+#ifdef CONFIG_FILS
+ /* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value
+ * to automatically check this against the driver header files. */
+ if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to get FILS capability from driver",
+ __func__);
+ /* Assume driver does not support FILS */
+ fils = 0;
+ }
+#endif /* CONFIG_FILS */
+ drv->fils_en = fils;
+ wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en);
+}
+
+
static void *
atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
{
@@ -1683,6 +1806,9 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
if (atheros_wireless_event_init(drv))
goto bad;
+ /* Read FILS capability from the driver */
+ atheros_read_fils_cap(drv);
+
return drv;
bad:
atheros_reset_appfilter(drv);
@@ -1735,7 +1861,7 @@ atheros_set_ssid(void *priv, const u8 *buf, int len)
os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
iwr.u.essid.flags = 1; /* SSID active */
iwr.u.essid.pointer = (caddr_t) buf;
- iwr.u.essid.length = len + 1;
+ iwr.u.essid.length = len;
if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
@@ -1848,7 +1974,7 @@ static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params)
}
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
int noack, unsigned int freq,
@@ -1874,7 +2000,7 @@ static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
sizeof(struct ieee80211req_mgmtbuf) + data_len);
}
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
@@ -2158,11 +2284,11 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = {
.set_ap_wps_ie = atheros_set_ap_wps_ie,
.set_authmode = atheros_set_authmode,
.set_ap = atheros_set_ap,
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
.sta_assoc = atheros_sta_assoc,
.sta_auth = atheros_sta_auth,
.send_mlme = atheros_send_mgmt,
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
.add_tspec = atheros_add_tspec,
.add_sta_node = atheros_add_sta_node,
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 2afd7df9637d..8621aa0b0099 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -726,7 +726,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
}
-static int
+static int
bsd_flush(void *priv)
{
u8 allsta[IEEE80211_ADDR_LEN];
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index c7107ba899b0..ac0916e4061f 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -1,6 +1,6 @@
/*
* Common driver-related functions
- * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -35,7 +35,6 @@ const char * event_to_string(enum wpa_event_type event)
E2S(ASSOCINFO);
E2S(INTERFACE_STATUS);
E2S(PMKID_CANDIDATE);
- E2S(STKSTART);
E2S(TDLS);
E2S(FT_RESPONSE);
E2S(IBSS_RSN_START);
@@ -81,6 +80,13 @@ const char * event_to_string(enum wpa_event_type event)
E2S(ACS_CHANNEL_SELECTED);
E2S(DFS_CAC_STARTED);
E2S(P2P_LO_STOP);
+ E2S(BEACON_LOSS);
+ E2S(DFS_PRE_CAC_EXPIRED);
+ E2S(EXTERNAL_AUTH);
+ E2S(PORT_AUTHORIZED);
+ E2S(STATION_OPMODE_CHANGED);
+ E2S(INTERFACE_MAC_CHANGED);
+ E2S(WDS_STA_INTERFACE_STATUS);
}
return "UNKNOWN";
@@ -267,6 +273,19 @@ const char * driver_flag_to_string(u64 flag)
DF2S(OFFCHANNEL_SIMULTANEOUS);
DF2S(FULL_AP_CLIENT_STATE);
DF2S(P2P_LISTEN_OFFLOAD);
+ DF2S(SUPPORT_FILS);
+ DF2S(BEACON_RATE_LEGACY);
+ DF2S(BEACON_RATE_HT);
+ DF2S(BEACON_RATE_VHT);
+ DF2S(MGMT_TX_RANDOM_TA);
+ DF2S(MGMT_TX_RANDOM_TA_CONNECTED);
+ DF2S(SCHED_SCAN_RELATIVE_RSSI);
+ DF2S(HE_CAPABILITIES);
+ DF2S(FILS_SK_OFFLOAD);
+ DF2S(OCE_STA);
+ DF2S(OCE_AP);
+ DF2S(OCE_STA_CFON);
+ DF2S(MFP_OPTIONAL);
}
return "UNKNOWN";
#undef DF2S
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 517a3bbb5d30..597da335e474 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -741,10 +741,9 @@ static int hostap_set_generic_elem(void *priv,
drv->generic_ie = NULL;
drv->generic_ie_len = 0;
if (elem) {
- drv->generic_ie = os_malloc(elem_len);
+ drv->generic_ie = os_memdup(elem, elem_len);
if (drv->generic_ie == NULL)
return -1;
- os_memcpy(drv->generic_ie, elem, elem_len);
drv->generic_ie_len = elem_len;
}
@@ -768,11 +767,10 @@ static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
drv->wps_ie = NULL;
drv->wps_ie_len = 0;
if (proberesp) {
- drv->wps_ie = os_malloc(wpabuf_len(proberesp));
+ drv->wps_ie = os_memdup(wpabuf_head(proberesp),
+ wpabuf_len(proberesp));
if (drv->wps_ie == NULL)
return -1;
- os_memcpy(drv->wps_ie, wpabuf_head(proberesp),
- wpabuf_len(proberesp));
drv->wps_ie_len = wpabuf_len(proberesp);
}
@@ -1090,7 +1088,7 @@ static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
u16 *num_modes,
- u16 *flags)
+ u16 *flags, u8 *dfs)
{
struct hostapd_hw_modes *mode;
int i, clen, rlen;
@@ -1105,6 +1103,7 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
*num_modes = 1;
*flags = 0;
+ *dfs = 0;
mode->mode = HOSTAPD_MODE_IEEE80211B;
mode->num_channels = 14;
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
new file mode 100644
index 000000000000..4f6629f5aaa2
--- /dev/null
+++ b/src/drivers/driver_macsec_linux.c
@@ -0,0 +1,1310 @@
+/*
+ * Driver interaction with Linux MACsec kernel module
+ * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/macsec.h>
+#include <linux/if_macsec.h>
+#include <inttypes.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "pae/ieee802_1x_kay.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#define DRV_PREFIX "macsec_linux: "
+
+#define UNUSED_SCI 0xffffffffffffffff
+
+struct cb_arg {
+ struct macsec_drv_data *drv;
+ u32 *pn;
+ int ifindex;
+ u8 txsa;
+ u8 rxsa;
+ u64 rxsci;
+};
+
+struct macsec_genl_ctx {
+ struct nl_sock *sk;
+ int macsec_genl_id;
+ struct cb_arg cb_arg;
+};
+
+struct macsec_drv_data {
+ struct driver_wired_common_data common;
+ struct rtnl_link *link;
+ struct nl_cache *link_cache;
+ struct nl_sock *sk;
+ struct macsec_genl_ctx ctx;
+
+ struct netlink_data *netlink;
+ struct nl_handle *nl;
+ char ifname[IFNAMSIZ + 1];
+ int ifi;
+ int parent_ifi;
+
+ Boolean created_link;
+
+ Boolean controlled_port_enabled;
+ Boolean controlled_port_enabled_set;
+
+ Boolean protect_frames;
+ Boolean protect_frames_set;
+
+ Boolean encrypt;
+ Boolean encrypt_set;
+
+ Boolean replay_protect;
+ Boolean replay_protect_set;
+
+ u32 replay_window;
+
+ u8 encoding_sa;
+ Boolean encoding_sa_set;
+};
+
+
+static int dump_callback(struct nl_msg *msg, void *argp);
+
+
+static struct nl_msg * msg_prepare(enum macsec_nl_commands cmd,
+ const struct macsec_genl_ctx *ctx,
+ unsigned int ifindex)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message");
+ return NULL;
+ }
+
+ if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex);
+
+ return msg;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return NULL;
+}
+
+
+static int nla_put_rxsc_config(struct nl_msg *msg, u64 sci)
+{
+ struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG);
+
+ if (!nest)
+ return -1;
+
+ NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci);
+
+ nla_nest_end(msg, nest);
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+
+static int init_genl_ctx(struct macsec_drv_data *drv)
+{
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+
+ ctx->sk = nl_socket_alloc();
+ if (!ctx->sk) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+ return -1;
+ }
+
+ if (genl_connect(ctx->sk) < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "connection to genl socket failed");
+ goto out_free;
+ }
+
+ ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec");
+ if (ctx->macsec_genl_id < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed");
+ goto out_free;
+ }
+
+ memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg));
+ ctx->cb_arg.drv = drv;
+
+ nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback,
+ &ctx->cb_arg);
+
+ return 0;
+
+out_free:
+ nl_socket_free(ctx->sk);
+ ctx->sk = NULL;
+ return -1;
+}
+
+
+static int try_commit(struct macsec_drv_data *drv)
+{
+ int err;
+
+ if (!drv->sk)
+ return 0;
+
+ if (!drv->link)
+ return 0;
+
+ if (drv->controlled_port_enabled_set) {
+ struct rtnl_link *change = rtnl_link_alloc();
+
+ if (!change)
+ return -1;
+
+ rtnl_link_set_name(change, drv->ifname);
+
+ if (drv->controlled_port_enabled)
+ rtnl_link_set_flags(change, IFF_UP);
+ else
+ rtnl_link_unset_flags(change, IFF_UP);
+
+ err = rtnl_link_change(drv->sk, change, change, 0);
+ if (err < 0)
+ return err;
+
+ rtnl_link_put(change);
+
+ drv->controlled_port_enabled_set = FALSE;
+ }
+
+ if (drv->protect_frames_set)
+ rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
+
+ if (drv->encrypt_set)
+ rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
+
+ if (drv->replay_protect_set) {
+ rtnl_link_macsec_set_replay_protect(drv->link,
+ drv->replay_protect);
+ if (drv->replay_protect)
+ rtnl_link_macsec_set_window(drv->link,
+ drv->replay_window);
+ }
+
+ if (drv->encoding_sa_set)
+ rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa);
+
+ err = rtnl_link_add(drv->sk, drv->link, 0);
+ if (err < 0)
+ return err;
+
+ drv->protect_frames_set = FALSE;
+ drv->encrypt_set = FALSE;
+ drv->replay_protect_set = FALSE;
+
+ return 0;
+}
+
+
+static void macsec_drv_wpa_deinit(void *priv)
+{
+ struct macsec_drv_data *drv = priv;
+
+ driver_wired_deinit_common(&drv->common);
+ os_free(drv);
+}
+
+
+static int macsec_check_macsec(void)
+{
+ struct nl_sock *sk;
+ int err = -1;
+
+ sk = nl_socket_alloc();
+ if (!sk) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+ return -1;
+ }
+
+ if (genl_connect(sk) < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "connection to genl socket failed");
+ goto out_free;
+ }
+
+ if (genl_ctrl_resolve(sk, "macsec") < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "genl resolve failed - macsec kernel module not present?");
+ goto out_free;
+ }
+
+ err = 0;
+
+out_free:
+ nl_socket_free(sk);
+ return err;
+}
+
+
+static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
+{
+ struct macsec_drv_data *drv;
+
+ if (macsec_check_macsec() < 0)
+ return NULL;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (!drv)
+ return NULL;
+
+ if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
+ os_free(drv);
+ return NULL;
+ }
+
+ return drv;
+}
+
+
+static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params)
+{
+ struct macsec_drv_data *drv = priv;
+ int err;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ drv->sk = nl_socket_alloc();
+ if (!drv->sk)
+ return -1;
+
+ err = nl_connect(drv->sk, NETLINK_ROUTE);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX
+ "Unable to connect NETLINK_ROUTE socket: %s",
+ strerror(errno));
+ goto sock;
+ }
+
+ err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s",
+ strerror(errno));
+ goto sock;
+ }
+
+ drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname);
+ if (drv->parent_ifi == 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX
+ "couldn't find ifindex for interface %s",
+ drv->common.ifname);
+ goto cache;
+ }
+
+ err = init_genl_ctx(drv);
+ if (err < 0)
+ goto cache;
+
+ return 0;
+
+cache:
+ nl_cache_free(drv->link_cache);
+ drv->link_cache = NULL;
+sock:
+ nl_socket_free(drv->sk);
+ drv->sk = NULL;
+ return -1;
+}
+
+
+static int macsec_drv_macsec_deinit(void *priv)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ if (drv->sk)
+ nl_socket_free(drv->sk);
+ drv->sk = NULL;
+
+ if (drv->link_cache)
+ nl_cache_free(drv->link_cache);
+ drv->link_cache = NULL;
+
+ if (drv->ctx.sk)
+ nl_socket_free(drv->ctx.sk);
+
+ return 0;
+}
+
+
+static int macsec_drv_get_capability(void *priv, enum macsec_cap *cap)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ *cap = MACSEC_CAP_INTEG_AND_CONF;
+
+ return 0;
+}
+
+
+/**
+ * macsec_drv_enable_protect_frames - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = protect frames enabled
+ * FALSE = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_protect_frames(void *priv, Boolean enabled)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+ drv->protect_frames_set = TRUE;
+ drv->protect_frames = enabled;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_enable_encrypt - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = protect frames enabled
+ * FALSE = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_encrypt(void *priv, Boolean enabled)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+ drv->encrypt_set = TRUE;
+ drv->encrypt = enabled;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_set_replay_protect - Set replay protect status and window size
+ * @priv: Private driver interface data
+ * @enabled: TRUE = replay protect enabled
+ * FALSE = replay protect disabled
+ * @window: replay window size, valid only when replay protect enabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_replay_protect(void *priv, Boolean enabled,
+ u32 window)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__,
+ enabled ? "TRUE" : "FALSE", window);
+
+ drv->replay_protect_set = TRUE;
+ drv->replay_protect = enabled;
+ if (enabled)
+ drv->replay_window = window;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_set_current_cipher_suite - Set current cipher suite
+ * @priv: Private driver interface data
+ * @cs: EUI64 identifier
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs)
+{
+ wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
+ return 0;
+}
+
+
+/**
+ * macsec_drv_enable_controlled_port - Set controlled port status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = controlled port enabled
+ * FALSE = controlled port disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_controlled_port(void *priv, Boolean enabled)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+ drv->controlled_port_enabled = enabled;
+ drv->controlled_port_enabled_set = TRUE;
+
+ return try_commit(drv);
+}
+
+
+static struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = {
+ [MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
+ [MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
+ [MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
+ [MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY },
+};
+
+static struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = {
+ [MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
+ [MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
+ [MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = {
+ [MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [MACSEC_ATTR_SECY] = { .type = NLA_NESTED },
+ [MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED },
+ [MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED },
+};
+
+static int dump_callback(struct nl_msg *msg, void *argp)
+{
+ struct nlmsghdr *ret_hdr = nlmsg_hdr(msg);
+ struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1];
+ struct cb_arg *arg = (struct cb_arg *) argp;
+ struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr);
+ int err;
+
+ if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id)
+ return 0;
+
+ err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), main_policy);
+ if (err < 0)
+ return 0;
+
+ if (!tb_msg[MACSEC_ATTR_IFINDEX])
+ return 0;
+
+ if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex)
+ return 0;
+
+ if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) {
+ return 0;
+ } else if (arg->txsa < 4) {
+ struct nlattr *nla;
+ int rem;
+
+ nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) {
+ struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1];
+
+ err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla,
+ sa_policy);
+ if (err < 0)
+ continue;
+ if (!tb[MACSEC_SA_ATTR_AN])
+ continue;
+ if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa)
+ continue;
+ if (!tb[MACSEC_SA_ATTR_PN])
+ return 0;
+ *arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]);
+ return 0;
+ }
+
+ return 0;
+ }
+
+ if (arg->rxsci == UNUSED_SCI)
+ return 0;
+
+ if (tb_msg[MACSEC_ATTR_RXSC_LIST]) {
+ struct nlattr *nla;
+ int rem;
+
+ nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) {
+ struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1];
+
+ err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla,
+ sc_policy);
+ if (err < 0)
+ return 0;
+ if (!tb[MACSEC_RXSC_ATTR_SCI])
+ continue;
+ if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci)
+ continue;
+ if (!tb[MACSEC_RXSC_ATTR_SA_LIST])
+ return 0;
+
+ nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST],
+ rem) {
+ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+ err = nla_parse_nested(tb_sa,
+ MACSEC_SA_ATTR_MAX, nla,
+ sa_policy);
+ if (err < 0)
+ continue;
+ if (!tb_sa[MACSEC_SA_ATTR_AN])
+ continue;
+ if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) !=
+ arg->rxsa)
+ continue;
+ if (!tb_sa[MACSEC_SA_ATTR_PN])
+ return 0;
+ *arg->pn =
+ nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
+
+ return 0;
+ }
+
+ return 0;
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg)
+{
+ int ret;
+
+ ret = nl_send_auto_complete(sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ return ret;
+ }
+
+ ret = nl_recvmsgs_default(sk);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+ return ret;
+}
+
+
+static int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa,
+ u32 *pn)
+{
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ int ret = 1;
+
+ ctx->cb_arg.ifindex = drv->ifi;
+ ctx->cb_arg.rxsci = rxsci;
+ ctx->cb_arg.rxsa = rxsa;
+ ctx->cb_arg.txsa = txsa;
+ ctx->cb_arg.pn = pn;
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message",
+ __func__);
+ return 1;
+ }
+
+ if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0,
+ NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header",
+ __func__);
+ goto out_free_msg;
+ }
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0)
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "failed to communicate: %d (%s)",
+ ret, nl_geterror(-ret));
+
+ ctx->cb_arg.pn = NULL;
+
+out_free_msg:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_get_receive_lowest_pn - Get receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ int err;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__);
+
+ err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an,
+ &sa->lowest_pn);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__,
+ sa->lowest_pn);
+
+ return err;
+}
+
+
+/**
+ * macsec_drv_get_transmit_next_pn - Get transmit next PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ int err;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err,
+ sa->next_pn);
+ return err;
+}
+
+
+/**
+ * macsec_drv_set_transmit_next_pn - Set transmit next pn
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn);
+
+ msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "failed to communicate: %d (%s)",
+ ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+#define SCISTR MACSTR "::%hx"
+#define SCI2STR(addr, port) MAC2STR(addr), htons(port)
+
+/**
+ * macsec_drv_create_receive_sc - Create secure channel for receiving
+ * @priv: Private driver interface data
+ * @sc: secure channel
+ * @sci_addr: secure channel identifier - address
+ * @sci_port: secure channel identifier - port
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+ * 2 = Strict)
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc,
+ unsigned int conf_offset,
+ int validation)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__,
+ SCI2STR(sc->sci.addr, sc->sci.port));
+
+ msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
+ goto nla_put_failure;
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_delete_receive_sc - Delete secure connection for receiving
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__,
+ SCI2STR(sc->sci.addr, sc->sci.port));
+
+ msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
+ goto nla_put_failure;
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_create_receive_sa - Create secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+ goto nla_put_failure;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive);
+ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+ NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
+ &sa->pkey->key_identifier);
+ NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_delete_receive_sa - Delete secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+ goto nla_put_failure;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
+ u64 sci, unsigned char an, Boolean state)
+{
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, sci))
+ goto nla_put_failure;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0)
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_enable_receive_sa - Enable the SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
+ sa->an, TRUE);
+}
+
+
+/**
+ * macsec_drv_disable_receive_sa - Disable SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
+ sa->an, FALSE);
+}
+
+
+static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci)
+{
+ struct rtnl_link *needle;
+ void *match;
+
+ needle = rtnl_link_macsec_alloc();
+ if (!needle)
+ return NULL;
+
+ rtnl_link_set_link(needle, parent);
+ rtnl_link_macsec_set_sci(needle, sci);
+
+ match = nl_cache_find(cache, (struct nl_object *) needle);
+ rtnl_link_put(needle);
+
+ return (struct rtnl_link *) match;
+}
+
+
+/**
+ * macsec_drv_create_transmit_sc - Create secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * @conf_offset: confidentiality offset
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_transmit_sc(
+ void *priv, struct transmit_sc *sc,
+ unsigned int conf_offset)
+{
+ struct macsec_drv_data *drv = priv;
+ struct rtnl_link *link;
+ char *ifname;
+ u64 sci;
+ int err;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ if (!drv->sk) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
+ return -1;
+ }
+
+ link = rtnl_link_macsec_alloc();
+ if (!link) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+ return -1;
+ }
+
+ rtnl_link_set_link(link, drv->parent_ifi);
+
+ sci = mka_sci_u64(&sc->sci);
+ rtnl_link_macsec_set_sci(link, sci);
+
+ drv->created_link = TRUE;
+
+ err = rtnl_link_add(drv->sk, link, NLM_F_CREATE);
+ if (err == -NLE_BUSY) {
+ wpa_printf(MSG_INFO,
+ DRV_PREFIX "link already exists, using it");
+ drv->created_link = FALSE;
+ } else if (err < 0) {
+ rtnl_link_put(link);
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d",
+ err);
+ return err;
+ }
+
+ rtnl_link_put(link);
+
+ nl_cache_refill(drv->sk, drv->link_cache);
+ link = lookup_sc(drv->link_cache, drv->parent_ifi, sci);
+ if (!link) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link");
+ return -1;
+ }
+
+ drv->ifi = rtnl_link_get_ifindex(link);
+ ifname = rtnl_link_get_name(link);
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ rtnl_link_put(link);
+
+ drv->link = rtnl_link_macsec_alloc();
+ if (!drv->link) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+ return -1;
+ }
+
+ rtnl_link_set_name(drv->link, drv->ifname);
+
+ /* In case some settings have already been done but we couldn't apply
+ * them. */
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_delete_transmit_sc - Delete secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+ struct macsec_drv_data *drv = priv;
+ int err;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ if (!drv->sk)
+ return 0;
+
+ if (!drv->created_link) {
+ rtnl_link_put(drv->link);
+ drv->link = NULL;
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "we didn't create the link, leave it alone");
+ return 0;
+ }
+
+ err = rtnl_link_delete(drv->sk, drv->link);
+ if (err < 0)
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link");
+ rtnl_link_put(drv->link);
+ drv->link = NULL;
+
+ return err;
+}
+
+
+/**
+ * macsec_drv_create_transmit_sa - Create secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+
+ msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+ NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
+ &sa->pkey->key_identifier);
+ NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_delete_transmit_sa - Delete secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+
+ msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
+ unsigned char an, Boolean state)
+{
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_enable_transmit_sa - Enable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+
+ ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa");
+ return ret;
+ }
+
+ drv->encoding_sa_set = TRUE;
+ drv->encoding_sa = sa->an;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_disable_transmit_sa - Disable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+
+ return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE);
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
+ .name = "macsec_linux",
+ .desc = "MACsec Ethernet driver for Linux",
+ .get_ssid = driver_wired_get_ssid,
+ .get_bssid = driver_wired_get_bssid,
+ .get_capa = driver_wired_get_capa,
+ .init = macsec_drv_wpa_init,
+ .deinit = macsec_drv_wpa_deinit,
+
+ .macsec_init = macsec_drv_macsec_init,
+ .macsec_deinit = macsec_drv_macsec_deinit,
+ .macsec_get_capability = macsec_drv_get_capability,
+ .enable_protect_frames = macsec_drv_enable_protect_frames,
+ .enable_encrypt = macsec_drv_enable_encrypt,
+ .set_replay_protect = macsec_drv_set_replay_protect,
+ .set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
+ .enable_controlled_port = macsec_drv_enable_controlled_port,
+ .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
+ .get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
+ .set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
+ .create_receive_sc = macsec_drv_create_receive_sc,
+ .delete_receive_sc = macsec_drv_delete_receive_sc,
+ .create_receive_sa = macsec_drv_create_receive_sa,
+ .delete_receive_sa = macsec_drv_delete_receive_sa,
+ .enable_receive_sa = macsec_drv_enable_receive_sa,
+ .disable_receive_sa = macsec_drv_disable_receive_sa,
+ .create_transmit_sc = macsec_drv_create_transmit_sc,
+ .delete_transmit_sc = macsec_drv_delete_transmit_sc,
+ .create_transmit_sa = macsec_drv_create_transmit_sa,
+ .delete_transmit_sa = macsec_drv_delete_transmit_sa,
+ .enable_transmit_sa = macsec_drv_enable_transmit_sa,
+ .disable_transmit_sa = macsec_drv_disable_transmit_sa,
+};
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
index 826d3cc62133..8372393f26c2 100644
--- a/src/drivers/driver_macsec_qca.c
+++ b/src/drivers/driver_macsec_qca.c
@@ -29,7 +29,9 @@
#include "utils/eloop.h"
#include "common/defs.h"
#include "common/ieee802_1x_defs.h"
+#include "pae/ieee802_1x_kay.h"
#include "driver.h"
+#include "driver_wired_common.h"
#include "nss_macsec_secy.h"
#include "nss_macsec_secy_rx.h"
@@ -37,6 +39,9 @@
#define MAXSC 16
+#define SAK_128_LEN 16
+#define SAK_256_LEN 32
+
/* TCI field definition */
#define TCI_ES 0x40
#define TCI_SC 0x20
@@ -52,17 +57,14 @@
#pragma pack(pop)
#endif /* _MSC_VER */
-static const u8 pae_group_addr[ETH_ALEN] =
-{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+struct channel_map {
+ struct ieee802_1x_mka_sci sci;
+};
struct macsec_qca_data {
- char ifname[IFNAMSIZ + 1];
- u32 secy_id;
- void *ctx;
+ struct driver_wired_common_data common;
- int sock; /* raw packet socket for driver access */
- int pf_sock;
- int membership, multi, iff_allmulti, iff_up;
+ u32 secy_id;
/* shadow */
Boolean always_include_sci;
@@ -71,192 +73,10 @@ struct macsec_qca_data {
Boolean protect_frames;
Boolean replay_protect;
u32 replay_window;
-};
-
-
-static int macsec_qca_multicast_membership(int sock, int ifindex,
- const u8 *addr, int add)
-{
-#ifdef __linux__
- struct packet_mreq mreq;
-
- if (sock < 0)
- return -1;
-
- os_memset(&mreq, 0, sizeof(mreq));
- mreq.mr_ifindex = ifindex;
- mreq.mr_type = PACKET_MR_MULTICAST;
- mreq.mr_alen = ETH_ALEN;
- os_memcpy(mreq.mr_address, addr, ETH_ALEN);
-
- if (setsockopt(sock, SOL_PACKET,
- add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
- &mreq, sizeof(mreq)) < 0) {
- wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
- return -1;
- }
- return 0;
-#else /* __linux__ */
- return -1;
-#endif /* __linux__ */
-}
-
-
-static int macsec_qca_get_ssid(void *priv, u8 *ssid)
-{
- ssid[0] = 0;
- return 0;
-}
-
-
-static int macsec_qca_get_bssid(void *priv, u8 *bssid)
-{
- /* Report PAE group address as the "BSSID" for macsec connection. */
- os_memcpy(bssid, pae_group_addr, ETH_ALEN);
- return 0;
-}
-
-
-static int macsec_qca_get_capa(void *priv, struct wpa_driver_capa *capa)
-{
- os_memset(capa, 0, sizeof(*capa));
- capa->flags = WPA_DRIVER_FLAGS_WIRED;
- return 0;
-}
-
-
-static int macsec_qca_get_ifflags(const char *ifname, int *flags)
-{
- struct ifreq ifr;
- int s;
-
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
- return -1;
- }
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
- strerror(errno));
- close(s);
- return -1;
- }
- close(s);
- *flags = ifr.ifr_flags & 0xffff;
- return 0;
-}
-
-
-static int macsec_qca_set_ifflags(const char *ifname, int flags)
-{
- struct ifreq ifr;
- int s;
-
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
- return -1;
- }
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- ifr.ifr_flags = flags & 0xffff;
- if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
- strerror(errno));
- close(s);
- return -1;
- }
- close(s);
- return 0;
-}
-
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
-static int macsec_qca_get_ifstatus(const char *ifname, int *status)
-{
- struct ifmediareq ifmr;
- int s;
-
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- wpa_print(MSG_ERROR, "socket: %s", strerror(errno));
- return -1;
- }
-
- os_memset(&ifmr, 0, sizeof(ifmr));
- os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
- if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
- strerror(errno));
- close(s);
- return -1;
- }
- close(s);
- *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
- (IFM_ACTIVE | IFM_AVALID);
-
- return 0;
-}
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
-
-
-static int macsec_qca_multi(const char *ifname, const u8 *addr, int add)
-{
- struct ifreq ifr;
- int s;
-
-#ifdef __sun__
- return -1;
-#endif /* __sun__ */
-
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
- return -1;
- }
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-#ifdef __linux__
- ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
- os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
-#endif /* __linux__ */
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
- {
- struct sockaddr_dl *dlp;
- dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
- dlp->sdl_len = sizeof(struct sockaddr_dl);
- dlp->sdl_family = AF_LINK;
- dlp->sdl_index = 0;
- dlp->sdl_nlen = 0;
- dlp->sdl_alen = ETH_ALEN;
- dlp->sdl_slen = 0;
- os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
- }
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
-#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
- {
- struct sockaddr *sap;
- sap = (struct sockaddr *) &ifr.ifr_addr;
- sap->sa_len = sizeof(struct sockaddr);
- sap->sa_family = AF_UNSPEC;
- os_memcpy(sap->sa_data, addr, ETH_ALEN);
- }
-#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
-
- if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
- strerror(errno));
- close(s);
- return -1;
- }
- close(s);
- return 0;
-}
+ struct channel_map receive_channel_map[MAXSC];
+ struct channel_map transmit_channel_map[MAXSC];
+};
static void __macsec_drv_init(struct macsec_qca_data *drv)
@@ -309,76 +129,23 @@ static void __macsec_drv_deinit(struct macsec_qca_data *drv)
static void * macsec_qca_init(void *ctx, const char *ifname)
{
struct macsec_qca_data *drv;
- int flags;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
- os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
- drv->ctx = ctx;
/* Board specific settings */
- if (os_memcmp("eth2", drv->ifname, 4) == 0)
+ if (os_memcmp("eth2", ifname, 4) == 0)
drv->secy_id = 1;
- else if (os_memcmp("eth3", drv->ifname, 4) == 0)
+ else if (os_memcmp("eth3", ifname, 4) == 0)
drv->secy_id = 2;
else
drv->secy_id = -1;
-#ifdef __linux__
- drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
- if (drv->pf_sock < 0)
- wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
-#else /* __linux__ */
- drv->pf_sock = -1;
-#endif /* __linux__ */
-
- if (macsec_qca_get_ifflags(ifname, &flags) == 0 &&
- !(flags & IFF_UP) &&
- macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) {
- drv->iff_up = 1;
- }
-
- if (macsec_qca_multicast_membership(drv->pf_sock,
- if_nametoindex(drv->ifname),
- pae_group_addr, 1) == 0) {
- wpa_printf(MSG_DEBUG,
- "%s: Added multicast membership with packet socket",
- __func__);
- drv->membership = 1;
- } else if (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) {
- wpa_printf(MSG_DEBUG,
- "%s: Added multicast membership with SIOCADDMULTI",
- __func__);
- drv->multi = 1;
- } else if (macsec_qca_get_ifflags(ifname, &flags) < 0) {
- wpa_printf(MSG_INFO, "%s: Could not get interface flags",
- __func__);
+ if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
os_free(drv);
return NULL;
- } else if (flags & IFF_ALLMULTI) {
- wpa_printf(MSG_DEBUG,
- "%s: Interface is already configured for multicast",
- __func__);
- } else if (macsec_qca_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) {
- wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
- __func__);
- os_free(drv);
- return NULL;
- } else {
- wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
- drv->iff_allmulti = 1;
}
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
- {
- int status;
- wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
- __func__);
- while (macsec_qca_get_ifstatus(ifname, &status) == 0 &&
- status == 0)
- sleep(1);
- }
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
return drv;
}
@@ -387,42 +154,8 @@ static void * macsec_qca_init(void *ctx, const char *ifname)
static void macsec_qca_deinit(void *priv)
{
struct macsec_qca_data *drv = priv;
- int flags;
-
- if (drv->membership &&
- macsec_qca_multicast_membership(drv->pf_sock,
- if_nametoindex(drv->ifname),
- pae_group_addr, 0) < 0) {
- wpa_printf(MSG_DEBUG,
- "%s: Failed to remove PAE multicast group (PACKET)",
- __func__);
- }
-
- if (drv->multi &&
- macsec_qca_multi(drv->ifname, pae_group_addr, 0) < 0) {
- wpa_printf(MSG_DEBUG,
- "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
- __func__);
- }
-
- if (drv->iff_allmulti &&
- (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 ||
- macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) {
- wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
- __func__);
- }
-
- if (drv->iff_up &&
- macsec_qca_get_ifflags(drv->ifname, &flags) == 0 &&
- (flags & IFF_UP) &&
- macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
- __func__);
- }
-
- if (drv->pf_sock != -1)
- close(drv->pf_sock);
+ driver_wired_deinit_common(&drv->common);
os_free(drv);
}
@@ -457,6 +190,16 @@ static int macsec_qca_macsec_deinit(void *priv)
}
+static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ *cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+
+ return 0;
+}
+
+
static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled)
{
struct macsec_qca_data *drv = priv;
@@ -486,19 +229,32 @@ static int macsec_qca_set_replay_protect(void *priv, Boolean enabled,
}
+static fal_cipher_suite_e macsec_qca_cs_type_get(u64 cs)
+{
+ if (cs == CS_ID_GCM_AES_128)
+ return FAL_CIPHER_SUITE_AES_GCM_128;
+ if (cs == CS_ID_GCM_AES_256)
+ return FAL_CIPHER_SUITE_AES_GCM_256;
+ return FAL_CIPHER_SUITE_MAX;
+}
+
+
static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs)
{
- if (cs != CS_ID_GCM_AES_128) {
+ struct macsec_qca_data *drv = priv;
+ fal_cipher_suite_e cs_type;
+
+ if (cs != CS_ID_GCM_AES_128 && cs != CS_ID_GCM_AES_256) {
wpa_printf(MSG_ERROR,
"%s: NOT supported CipherSuite: %016" PRIx64,
__func__, cs);
return -1;
}
- /* Support default Cipher Suite 0080020001000001 (GCM-AES-128) */
- wpa_printf(MSG_DEBUG, "%s: default support aes-gcm-128", __func__);
+ wpa_printf(MSG_DEBUG, "%s: CipherSuite: %016" PRIx64, __func__, cs);
- return 0;
+ cs_type = macsec_qca_cs_type_get(cs);
+ return nss_macsec_secy_cipher_suite_set(drv->secy_id, cs_type);
}
@@ -515,16 +271,82 @@ static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled)
}
-static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an,
- u32 *lowest_pn)
+static int macsec_qca_lookup_channel(struct channel_map *map,
+ struct ieee802_1x_mka_sci *sci,
+ u32 *channel)
+{
+ u32 i;
+
+ for (i = 0; i < MAXSC; i++) {
+ if (os_memcmp(&map[i].sci, sci,
+ sizeof(struct ieee802_1x_mka_sci)) == 0) {
+ *channel = i;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static void macsec_qca_register_channel(struct channel_map *map,
+ struct ieee802_1x_mka_sci *sci,
+ u32 channel)
+{
+ os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci));
+}
+
+
+static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv,
+ struct receive_sc *sc,
+ u32 *channel)
+{
+ return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci,
+ channel);
+}
+
+
+static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv,
+ struct receive_sc *sc,
+ u32 channel)
+{
+ macsec_qca_register_channel(drv->receive_channel_map, &sc->sci,
+ channel);
+}
+
+
+static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv,
+ struct transmit_sc *sc,
+ u32 *channel)
+{
+ return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci,
+ channel);
+}
+
+
+static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv,
+ struct transmit_sc *sc,
+ u32 channel)
+{
+ macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci,
+ channel);
+}
+
+
+static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
u32 next_pn = 0;
bool enabled = FALSE;
u32 win;
+ u32 channel;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
- ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an,
+ ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an,
&next_pn);
ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
&enabled);
@@ -532,40 +354,49 @@ static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an,
channel, &win);
if (enabled)
- *lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
+ sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
else
- *lowest_pn = next_pn;
+ sa->lowest_pn = next_pn;
- wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn);
+ wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn);
return ret;
}
-static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an,
- u32 *next_pn)
+static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
+ u32 channel;
- ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an,
- next_pn);
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
- wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn);
+ ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an,
+ &sa->next_pn);
+
+ wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn);
return ret;
}
-int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn)
+static int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
+ u32 channel;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
- ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
- next_pn);
+ ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+ sa->next_pn);
- wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn);
+ wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn);
return ret;
}
@@ -598,8 +429,7 @@ static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel)
}
-static int macsec_qca_create_receive_sc(void *priv, u32 channel,
- const u8 *sci_addr, u16 sci_port,
+static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc,
unsigned int conf_offset,
int validation)
{
@@ -608,6 +438,13 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel,
fal_rx_prc_lut_t entry;
fal_rx_sc_validate_frame_e vf;
enum validate_frames validate_frames = validation;
+ u32 channel;
+ const u8 *sci_addr = sc->sci.addr;
+ u16 sci_port = be_to_host16(sc->sci.port);
+
+ ret = macsec_qca_get_available_receive_sc(priv, &channel);
+ if (ret != 0)
+ return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
@@ -615,8 +452,8 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel,
os_memset(&entry, 0, sizeof(entry));
os_memcpy(entry.sci, sci_addr, ETH_ALEN);
- entry.sci[6] = (sci_port >> 8) & 0xf;
- entry.sci[7] = sci_port & 0xf;
+ entry.sci[6] = (sci_port >> 8) & 0xff;
+ entry.sci[7] = sci_port & 0xff;
entry.sci_mask = 0xf;
entry.valid = 1;
@@ -642,15 +479,22 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel,
channel,
drv->replay_window);
+ macsec_qca_register_receive_channel(drv, sc, channel);
+
return ret;
}
-static int macsec_qca_delete_receive_sc(void *priv, u32 channel)
+static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
fal_rx_prc_lut_t entry;
+ u32 channel;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sc, &channel);
+ if (ret != 0)
+ return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
@@ -664,49 +508,91 @@ static int macsec_qca_delete_receive_sc(void *priv, u32 channel)
}
-static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an,
- u32 lowest_pn, const u8 *sak)
+static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
fal_rx_sak_t rx_sak;
int i = 0;
+ u32 channel;
+ fal_rx_prc_lut_t entry;
+ u32 offset;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
- __func__, channel, an, lowest_pn);
+ __func__, channel, sa->an, sa->lowest_pn);
os_memset(&rx_sak, 0, sizeof(rx_sak));
- for (i = 0; i < 16; i++)
- rx_sak.sak[i] = sak[15 - i];
+ rx_sak.sak_len = sa->pkey->key_len;
+ if (sa->pkey->key_len == SAK_128_LEN) {
+ for (i = 0; i < 16; i++)
+ rx_sak.sak[i] = sa->pkey->key[15 - i];
+ } else if (sa->pkey->key_len == SAK_256_LEN) {
+ for (i = 0; i < 16; i++) {
+ rx_sak.sak1[i] = sa->pkey->key[15 - i];
+ rx_sak.sak[i] = sa->pkey->key[31 - i];
+ }
+ } else {
+ return -1;
+ }
- ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an);
- ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak);
+ if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
+ offset = 0;
+ else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
+ offset = 30;
+ else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
+ offset = 50;
+ else
+ return -1;
+ ret += nss_macsec_secy_rx_prc_lut_get(drv->secy_id, channel, &entry);
+ entry.offset = offset;
+ ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+ ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an);
+ ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an,
+ &rx_sak);
return ret;
}
-static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
+ u32 channel;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
- wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+ sa->an);
- ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE);
+ ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+ TRUE);
return ret;
}
-static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
+ u32 channel;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
- wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+ sa->an);
- ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE);
+ ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+ FALSE);
return ret;
}
@@ -715,14 +601,12 @@ static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an)
static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
u32 sc_ch = 0;
bool in_use = FALSE;
for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
- ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
- &in_use);
- if (ret)
+ if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
+ &in_use))
continue;
if (!in_use) {
@@ -739,14 +623,19 @@ static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
}
-static int macsec_qca_create_transmit_sc(void *priv, u32 channel,
- const u8 *sci_addr, u16 sci_port,
+static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc,
unsigned int conf_offset)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
fal_tx_class_lut_t entry;
u8 psci[ETH_ALEN + 2];
+ u32 channel;
+ u16 sci_port = be_to_host16(sc->sci.port);
+
+ ret = macsec_qca_get_available_transmit_sc(priv, &channel);
+ if (ret != 0)
+ return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
@@ -757,9 +646,9 @@ static int macsec_qca_create_transmit_sc(void *priv, u32 channel,
entry.action = FAL_TX_CLASS_ACTION_FORWARD;
entry.channel = channel;
- os_memcpy(psci, sci_addr, ETH_ALEN);
- psci[6] = (sci_port >> 8) & 0xf;
- psci[7] = sci_port & 0xf;
+ os_memcpy(psci, sc->sci.addr, ETH_ALEN);
+ psci[6] = (sci_port >> 8) & 0xff;
+ psci[7] = sci_port & 0xff;
ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
@@ -769,15 +658,22 @@ static int macsec_qca_create_transmit_sc(void *priv, u32 channel,
channel,
conf_offset);
+ macsec_qca_register_transmit_channel(drv, sc, channel);
+
return ret;
}
-static int macsec_qca_delete_transmit_sc(void *priv, u32 channel)
+static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
fal_tx_class_lut_t entry;
+ u32 channel;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel);
+ if (ret != 0)
+ return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
@@ -791,19 +687,23 @@ static int macsec_qca_delete_transmit_sc(void *priv, u32 channel)
}
-static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an,
- u32 next_pn, Boolean confidentiality,
- const u8 *sak)
+static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
u8 tci = 0;
fal_tx_sak_t tx_sak;
int i;
+ u32 channel;
+ u32 offset;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
wpa_printf(MSG_DEBUG,
"%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
- __func__, channel, an, next_pn, confidentiality);
+ __func__, channel, sa->an, sa->next_pn, sa->confidentiality);
if (drv->always_include_sci)
tci |= TCI_SC;
@@ -812,45 +712,81 @@ static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an,
else if (drv->use_scb)
tci |= TCI_SCB;
- if (confidentiality)
+ if (sa->confidentiality)
tci |= TCI_E | TCI_C;
os_memset(&tx_sak, 0, sizeof(tx_sak));
- for (i = 0; i < 16; i++)
- tx_sak.sak[i] = sak[15 - i];
+ tx_sak.sak_len = sa->pkey->key_len;
+ if (sa->pkey->key_len == SAK_128_LEN) {
+ for (i = 0; i < 16; i++)
+ tx_sak.sak[i] = sa->pkey->key[15 - i];
+ } else if (sa->pkey->key_len == SAK_256_LEN) {
+ for (i = 0; i < 16; i++) {
+ tx_sak.sak1[i] = sa->pkey->key[15 - i];
+ tx_sak.sak[i] = sa->pkey->key[31 - i];
+ }
+ } else {
+ return -1;
+ }
- ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
- next_pn);
- ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak);
+ if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
+ offset = 0;
+ else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
+ offset = 30;
+ else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
+ offset = 50;
+ else
+ return -1;
+ ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
+ channel,
+ offset);
+ ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+ sa->next_pn);
+ ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an,
+ &tx_sak);
ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
(tci >> 2));
- ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an);
+ ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an);
return ret;
}
-static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
+ u32 channel;
- wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
- ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE);
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+ sa->an);
+
+ ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+ TRUE);
return ret;
}
-static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
- int ret = 0;
+ int ret;
+ u32 channel;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
- wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+ sa->an);
- ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE);
+ ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+ FALSE);
return ret;
}
@@ -859,14 +795,15 @@ static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an)
const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
.name = "macsec_qca",
.desc = "QCA MACsec Ethernet driver",
- .get_ssid = macsec_qca_get_ssid,
- .get_bssid = macsec_qca_get_bssid,
- .get_capa = macsec_qca_get_capa,
+ .get_ssid = driver_wired_get_ssid,
+ .get_bssid = driver_wired_get_bssid,
+ .get_capa = driver_wired_get_capa,
.init = macsec_qca_init,
.deinit = macsec_qca_deinit,
.macsec_init = macsec_qca_macsec_init,
.macsec_deinit = macsec_qca_macsec_deinit,
+ .macsec_get_capability = macsec_qca_get_capability,
.enable_protect_frames = macsec_qca_enable_protect_frames,
.set_replay_protect = macsec_qca_set_replay_protect,
.set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
@@ -874,13 +811,11 @@ const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
.get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
.get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
.set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
- .get_available_receive_sc = macsec_qca_get_available_receive_sc,
.create_receive_sc = macsec_qca_create_receive_sc,
.delete_receive_sc = macsec_qca_delete_receive_sc,
.create_receive_sa = macsec_qca_create_receive_sa,
.enable_receive_sa = macsec_qca_enable_receive_sa,
.disable_receive_sa = macsec_qca_disable_receive_sa,
- .get_available_transmit_sc = macsec_qca_get_available_transmit_sc,
.create_transmit_sc = macsec_qca_create_transmit_sc,
.delete_transmit_sc = macsec_qca_delete_transmit_sc,
.create_transmit_sa = macsec_qca_create_transmit_sa,
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 9440f0127235..614c4521e6ff 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -1220,12 +1220,16 @@ static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv)
}
-static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
- const u8 *pmkid)
+static int wpa_driver_ndis_add_pmkid(void *priv,
+ struct wpa_pmkid_params *params)
{
struct wpa_driver_ndis_data *drv = priv;
struct ndis_pmkid_entry *entry, *prev;
+ const u8 *bssid = params->bssid;
+ const u8 *pmkid = params->pmkid;
+ if (!bssid || !pmkid)
+ return -1;
if (drv->no_of_pmkid == 0)
return 0;
@@ -1261,12 +1265,16 @@ static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
}
-static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid,
- const u8 *pmkid)
+static int wpa_driver_ndis_remove_pmkid(void *priv,
+ struct wpa_pmkid_params *params)
{
struct wpa_driver_ndis_data *drv = priv;
struct ndis_pmkid_entry *entry, *prev;
+ const u8 *bssid = params->bssid;
+ const u8 *pmkid = params->pmkid;
+ if (!bssid || !pmkid)
+ return -1;
if (drv->no_of_pmkid == 0)
return 0;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 1210d4356087..871a5d0b4a20 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -29,6 +29,7 @@
#include "common/qca-vendor-attr.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
#include "l2_packet/l2_packet.h"
#include "netlink.h"
#include "linux_defines.h"
@@ -39,6 +40,29 @@
#include "driver_nl80211.h"
+/* support for extack if compilation headers are too old */
+#ifndef NETLINK_EXT_ACK
+#define NETLINK_EXT_ACK 11
+enum nlmsgerr_attrs {
+ NLMSGERR_ATTR_UNUSED,
+ NLMSGERR_ATTR_MSG,
+ NLMSGERR_ATTR_OFFS,
+ NLMSGERR_ATTR_COOKIE,
+
+ __NLMSGERR_ATTR_MAX,
+ NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
+};
+#endif
+#ifndef NLM_F_CAPPED
+#define NLM_F_CAPPED 0x100
+#endif
+#ifndef NLM_F_ACK_TLVS
+#define NLM_F_ACK_TLVS 0x200
+#endif
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
#ifndef CONFIG_LIBNL20
/*
* libnl 1.1 has a bug, it tries to allocate socket numbers densely
@@ -129,7 +153,7 @@ static void nl_destroy_handles(struct nl_handle **handle)
static void nl80211_register_eloop_read(struct nl_handle **handle,
eloop_sock_handler handler,
- void *eloop_data)
+ void *eloop_data, int persist)
{
#ifdef CONFIG_LIBNL20
/*
@@ -150,13 +174,17 @@ static void nl80211_register_eloop_read(struct nl_handle **handle,
nl_socket_set_nonblocking(*handle);
eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
eloop_data, *handle);
- *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+ if (!persist)
+ *handle = (void *) (((intptr_t) *handle) ^
+ ELOOP_SOCKET_INVALID);
}
-static void nl80211_destroy_eloop_handle(struct nl_handle **handle)
+static void nl80211_destroy_eloop_handle(struct nl_handle **handle, int persist)
{
- *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+ if (!persist)
+ *handle = (void *) (((intptr_t) *handle) ^
+ ELOOP_SOCKET_INVALID);
eloop_unregister_read_sock(nl_socket_get_fd(*handle));
nl_destroy_handles(handle);
}
@@ -204,6 +232,8 @@ static int nl80211_set_param(void *priv, const char *param);
static int nl80211_put_mesh_config(struct nl_msg *msg,
struct wpa_driver_mesh_bss_params *params);
#endif /* CONFIG_MESH */
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason);
/* Converts nl80211_chan_width to a common format */
@@ -295,8 +325,35 @@ static int finish_handler(struct nl_msg *msg, void *arg)
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
+ struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
+ int len = nlh->nlmsg_len;
+ struct nlattr *attrs;
+ struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
int *ret = arg;
+ int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
+
*ret = err->error;
+
+ if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
+ return NL_SKIP;
+
+ if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
+ ack_len += err->msg.nlmsg_len - sizeof(*nlh);
+
+ if (len <= ack_len)
+ return NL_STOP;
+
+ attrs = (void *) ((unsigned char *) nlh + ack_len);
+ len -= ack_len;
+
+ nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
+ if (tb[NLMSGERR_ATTR_MSG]) {
+ len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]),
+ nla_len(tb[NLMSGERR_ATTR_MSG]));
+ wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
+ len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+ }
+
return NL_SKIP;
}
@@ -335,7 +392,7 @@ static int send_and_recv(struct nl80211_global *global,
void *valid_data)
{
struct nl_cb *cb;
- int err = -ENOMEM;
+ int err = -ENOMEM, opt;
if (!msg)
return -ENOMEM;
@@ -344,6 +401,11 @@ static int send_and_recv(struct nl80211_global *global,
if (!cb)
goto out;
+ /* try to set NETLINK_EXT_ACK to 1, ignoring errors */
+ opt = 1;
+ setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+ NETLINK_EXT_ACK, &opt, sizeof(opt));
+
err = nl_send_auto_complete(nl_handle, msg);
if (err < 0)
goto out;
@@ -673,6 +735,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
struct nl80211_wiphy_data *w;
int wiphy_idx, found = 0;
struct i802_bss *tmp_bss;
+ u8 channel;
if (bss->wiphy_data != NULL)
return bss->wiphy_data;
@@ -692,30 +755,36 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
dl_list_init(&w->bsss);
dl_list_init(&w->drvs);
- w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
- if (!w->nl_cb) {
- os_free(w);
- return NULL;
- }
- nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
- nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event,
- w);
+ /* Beacon frames not supported in IEEE 802.11ad */
+ if (ieee80211_freq_to_chan(bss->freq, &channel) !=
+ HOSTAPD_MODE_IEEE80211AD) {
+ w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!w->nl_cb) {
+ os_free(w);
+ return NULL;
+ }
+ nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+ process_beacon_event, w);
+
+ w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
+ "wiphy beacons");
+ if (w->nl_beacons == NULL) {
+ os_free(w);
+ return NULL;
+ }
- w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
- "wiphy beacons");
- if (w->nl_beacons == NULL) {
- os_free(w);
- return NULL;
- }
+ if (nl80211_register_beacons(bss->drv, w)) {
+ nl_destroy_handles(&w->nl_beacons);
+ os_free(w);
+ return NULL;
+ }
- if (nl80211_register_beacons(bss->drv, w)) {
- nl_destroy_handles(&w->nl_beacons);
- os_free(w);
- return NULL;
+ nl80211_register_eloop_read(&w->nl_beacons,
+ nl80211_recv_beacons, w, 0);
}
- nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w);
-
dl_list_add(&nl80211_wiphys, &w->list);
add:
@@ -761,7 +830,8 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
if (!dl_list_empty(&w->bsss))
return;
- nl80211_destroy_eloop_handle(&w->nl_beacons);
+ if (w->nl_beacons)
+ nl80211_destroy_eloop_handle(&w->nl_beacons, 0);
nl_cb_put(w->nl_cb);
dl_list_del(&w->list);
@@ -900,7 +970,8 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
nl80211_check_global(drv->global);
wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
"interface");
- wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
+ if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+ return -1;
return 1;
}
@@ -909,19 +980,58 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
static struct wpa_driver_nl80211_data *
-nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len)
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
+ int *init_failed)
{
struct wpa_driver_nl80211_data *drv;
+ int res;
+
+ if (init_failed)
+ *init_failed = 0;
dl_list_for_each(drv, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
- if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
- have_ifidx(drv, idx, IFIDX_ANY))
+ res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Found matching own interface, but failed to complete reinitialization");
+ if (init_failed)
+ *init_failed = 1;
+ return drv;
+ }
+ if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY))
return drv;
}
return NULL;
}
+static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int notify)
+{
+ struct i802_bss *bss;
+ u8 addr[ETH_ALEN];
+
+ bss = get_bss_ifindex(drv, ifindex);
+ if (bss &&
+ linux_get_ifhwaddr(drv->global->ioctl_sock,
+ bss->ifname, addr) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: %s: failed to re-read MAC address",
+ bss->ifname);
+ } else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Own MAC address on ifindex %d (%s) changed from "
+ MACSTR " to " MACSTR,
+ ifindex, bss->ifname,
+ MAC2STR(bss->addr), MAC2STR(addr));
+ os_memcpy(bss->addr, addr, ETH_ALEN);
+ if (notify)
+ wpa_supplicant_event(drv->ctx,
+ EVENT_INTERFACE_MAC_CHANGED, NULL);
+ }
+}
+
+
static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
struct ifinfomsg *ifi,
u8 *buf, size_t len)
@@ -934,6 +1044,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
char namebuf[IFNAMSIZ];
char ifname[IFNAMSIZ + 1];
char extra[100], *pos, *end;
+ int init_failed;
extra[0] = '\0';
pos = extra;
@@ -978,9 +1089,11 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
- drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+ drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed);
if (!drv)
goto event_newlink;
+ if (init_failed)
+ return; /* do not update interface state */
if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
namebuf[0] = '\0';
@@ -989,6 +1102,8 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event since interface %s is up", namebuf);
drv->ignore_if_down_event = 0;
+ /* Re-read MAC address as it may have changed */
+ nl80211_refresh_mac(drv, ifi->ifi_index, 1);
return;
}
wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
@@ -1012,18 +1127,27 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
* dynamic interfaces
*/
drv = nl80211_find_drv(global, ifi->ifi_index,
- buf, len);
+ buf, len, NULL);
if (!drv)
return;
}
}
if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ namebuf[0] = '\0';
if (if_indextoname(ifi->ifi_index, namebuf) &&
linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s is down",
namebuf);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
+ namebuf, ifname);
+ if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not the main interface (%s) - do not indicate interface up",
+ drv->first_bss->ifname);
} else if (if_nametoindex(drv->first_bss->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s does not exist",
@@ -1033,29 +1157,9 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
"event since interface %s is marked "
"removed", drv->first_bss->ifname);
} else {
- struct i802_bss *bss;
- u8 addr[ETH_ALEN];
-
/* Re-read MAC address as it may have changed */
- bss = get_bss_ifindex(drv, ifi->ifi_index);
- if (bss &&
- linux_get_ifhwaddr(drv->global->ioctl_sock,
- bss->ifname, addr) < 0) {
- wpa_printf(MSG_DEBUG,
- "nl80211: %s: failed to re-read MAC address",
- bss->ifname);
- } else if (bss &&
- os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Own MAC address on ifindex %d (%s) changed from "
- MACSTR " to " MACSTR,
- ifi->ifi_index, bss->ifname,
- MAC2STR(bss->addr),
- MAC2STR(addr));
- os_memcpy(bss->addr, addr, ETH_ALEN);
- }
+ nl80211_refresh_mac(drv, ifi->ifi_index, 0);
- wpa_printf(MSG_DEBUG, "nl80211: Interface up");
drv->if_disabled = 0;
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
NULL);
@@ -1157,7 +1261,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
- drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+ drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL);
if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
/* device has been removed from bridge */
@@ -1181,16 +1285,108 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
}
+struct nl80211_get_assoc_freq_arg {
+ struct wpa_driver_nl80211_data *drv;
+ unsigned int assoc_freq;
+ unsigned int ibss_freq;
+ u8 assoc_bssid[ETH_ALEN];
+ u8 assoc_ssid[SSID_MAX_LEN];
+ u8 assoc_ssid_len;
+};
+
+static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *bss[NL80211_BSS_MAX + 1];
+ static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+ [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+ };
+ struct nl80211_get_assoc_freq_arg *ctx = arg;
+ enum nl80211_bss_status status;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_BSS] ||
+ nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+ bss_policy) ||
+ !bss[NL80211_BSS_STATUS])
+ return NL_SKIP;
+
+ status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_FREQUENCY]) {
+ ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+ ctx->assoc_freq);
+ }
+ if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+ bss[NL80211_BSS_FREQUENCY]) {
+ ctx->ibss_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+ ctx->ibss_freq);
+ }
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_BSSID]) {
+ os_memcpy(ctx->assoc_bssid,
+ nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+ MACSTR, MAC2STR(ctx->assoc_bssid));
+ }
+
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+ const u8 *ie, *ssid;
+ size_t ie_len;
+
+ ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+ ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+ ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
+ if (ssid && ssid[1] > 0 && ssid[1] <= SSID_MAX_LEN) {
+ ctx->assoc_ssid_len = ssid[1];
+ os_memcpy(ctx->assoc_ssid, ssid + 2, ssid[1]);
+ }
+ }
+
+ return NL_SKIP;
+}
+
+
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid)
+{
+ struct nl_msg *msg;
+ int ret;
+ struct nl80211_get_assoc_freq_arg arg;
+
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+ os_memset(&arg, 0, sizeof(arg));
+ arg.drv = drv;
+ ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+ &arg);
+ if (ret == 0) {
+ os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len);
+ return arg.assoc_ssid_len;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+}
+
+
unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
{
struct nl_msg *msg;
int ret;
- struct nl80211_bss_info_arg arg;
+ struct nl80211_get_assoc_freq_arg arg;
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
os_memset(&arg, 0, sizeof(arg));
arg.drv = drv;
- ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+ ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+ &arg);
if (ret == 0) {
unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
arg.ibss_freq : arg.assoc_freq;
@@ -1273,7 +1469,7 @@ int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
{
struct nl_msg *msg;
- sig->current_signal = -9999;
+ sig->current_signal = -WPA_INVALID_NOISE;
sig->current_txrate = 0;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
@@ -1335,7 +1531,7 @@ int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
{
struct nl_msg *msg;
- sig_change->current_noise = 9999;
+ sig_change->current_noise = WPA_INVALID_NOISE;
sig_change->frequency = drv->assoc_freq;
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
@@ -1505,7 +1701,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
nl80211_register_eloop_read(&global->nl_event,
wpa_driver_nl80211_event_receive,
- global->nl_cb);
+ global->nl_cb, 0);
return 0;
@@ -1871,7 +2067,7 @@ static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss)
{
nl80211_register_eloop_read(&bss->nl_mgmt,
wpa_driver_nl80211_event_receive,
- bss->nl_cb);
+ bss->nl_cb, 0);
}
@@ -1884,6 +2080,25 @@ static int nl80211_register_action_frame(struct i802_bss *bss,
}
+static int nl80211_init_connect_handle(struct i802_bss *bss)
+{
+ if (bss->nl_connect) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Connect handle already created (nl_connect=%p)",
+ bss->nl_connect);
+ return -1;
+ }
+
+ bss->nl_connect = nl_create_handle(bss->nl_cb, "connect");
+ if (!bss->nl_connect)
+ return -1;
+ nl80211_register_eloop_read(&bss->nl_connect,
+ wpa_driver_nl80211_event_receive,
+ bss->nl_cb, 1);
+ return 0;
+}
+
+
static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -1894,7 +2109,9 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
"handle %p", bss->nl_mgmt);
- if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+ if (drv->nlmode == NL80211_IFTYPE_ADHOC ||
+ ((drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_SME))) {
u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
/* register for any AUTH message */
@@ -1906,7 +2123,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
ret = -1;
#endif /* CONFIG_INTERWORKING */
-#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
/* GAS Initial Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
ret = -1;
@@ -1931,7 +2148,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
/* Protected GAS Comeback Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
ret = -1;
-#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */
#ifdef CONFIG_P2P
/* P2P Public Action */
if (nl80211_register_action_frame(bss,
@@ -1944,6 +2161,13 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
5) < 0)
ret = -1;
#endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP
+ /* DPP Public Action */
+ if (nl80211_register_action_frame(bss,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x1a",
+ 6) < 0)
+ ret = -1;
+#endif /* CONFIG_DPP */
#ifdef CONFIG_IEEE80211W
/* SA Query Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
@@ -1976,6 +2200,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
/* WNM-Sleep Mode Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
ret = -1;
+#ifdef CONFIG_WNM
+ /* WNM - Collocated Interference Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x0b", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_WNM */
#ifdef CONFIG_HS20
/* WNM-Notification */
@@ -2070,6 +2299,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
/* RRM Measurement Report */
if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0)
ret = -1;
+ /* RRM Link Measurement Report */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x03", 2) < 0)
+ ret = -1;
/* RRM Neighbor Report Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0)
ret = -1;
@@ -2141,9 +2373,6 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
if (nl80211_register_spurious_class3(bss))
goto out_err;
- if (nl80211_get_wiphy_data_ap(bss) == NULL)
- goto out_err;
-
nl80211_mgmt_handle_register_eloop(bss);
return 0;
@@ -2178,7 +2407,7 @@ static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
return;
wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
"(%s)", bss->nl_mgmt, reason);
- nl80211_destroy_eloop_handle(&bss->nl_mgmt);
+ nl80211_destroy_eloop_handle(&bss->nl_mgmt, 0);
nl80211_put_wiphy_data_ap(bss);
}
@@ -2394,16 +2623,20 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
if (drv->vendor_cmd_test_avail)
qca_vendor_test(drv);
+ nl80211_init_connect_handle(bss);
+
return 0;
}
-static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
+static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss)
{
struct nl_msg *msg;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
drv->ifindex);
+ nl80211_put_wiphy_data_ap(bss);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
return send_and_recv_msgs(drv, msg, NULL, NULL);
}
@@ -2438,9 +2671,11 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
bss->ifname, bss->brname, strerror(errno));
- if (drv->rtnl_sk)
- nl80211_handle_destroy(drv->rtnl_sk);
}
+
+ if (drv->rtnl_sk)
+ nl80211_handle_destroy(drv->rtnl_sk);
+
if (bss->added_bridge) {
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
0) < 0)
@@ -2456,7 +2691,7 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
nl80211_remove_monitor_interface(drv);
if (is_ap_interface(drv->nlmode))
- wpa_driver_nl80211_del_beacon(drv);
+ wpa_driver_nl80211_del_beacon(bss);
if (drv->eapol_sock >= 0) {
eloop_unregister_read_sock(drv->eapol_sock);
@@ -2505,6 +2740,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
nl80211_del_p2pdev(bss);
}
+ if (bss->nl_connect)
+ nl80211_destroy_eloop_handle(&bss->nl_connect, 1);
+
nl80211_destroy_bss(drv->first_bss);
os_free(drv->filter_ssids);
@@ -2530,30 +2768,30 @@ static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len)
switch (alg) {
case WPA_ALG_WEP:
if (key_len == 5)
- return WLAN_CIPHER_SUITE_WEP40;
- return WLAN_CIPHER_SUITE_WEP104;
+ return RSN_CIPHER_SUITE_WEP40;
+ return RSN_CIPHER_SUITE_WEP104;
case WPA_ALG_TKIP:
- return WLAN_CIPHER_SUITE_TKIP;
+ return RSN_CIPHER_SUITE_TKIP;
case WPA_ALG_CCMP:
- return WLAN_CIPHER_SUITE_CCMP;
+ return RSN_CIPHER_SUITE_CCMP;
case WPA_ALG_GCMP:
- return WLAN_CIPHER_SUITE_GCMP;
+ return RSN_CIPHER_SUITE_GCMP;
case WPA_ALG_CCMP_256:
- return WLAN_CIPHER_SUITE_CCMP_256;
+ return RSN_CIPHER_SUITE_CCMP_256;
case WPA_ALG_GCMP_256:
- return WLAN_CIPHER_SUITE_GCMP_256;
+ return RSN_CIPHER_SUITE_GCMP_256;
case WPA_ALG_IGTK:
- return WLAN_CIPHER_SUITE_AES_CMAC;
+ return RSN_CIPHER_SUITE_AES_128_CMAC;
case WPA_ALG_BIP_GMAC_128:
- return WLAN_CIPHER_SUITE_BIP_GMAC_128;
+ return RSN_CIPHER_SUITE_BIP_GMAC_128;
case WPA_ALG_BIP_GMAC_256:
- return WLAN_CIPHER_SUITE_BIP_GMAC_256;
+ return RSN_CIPHER_SUITE_BIP_GMAC_256;
case WPA_ALG_BIP_CMAC_256:
- return WLAN_CIPHER_SUITE_BIP_CMAC_256;
+ return RSN_CIPHER_SUITE_BIP_CMAC_256;
case WPA_ALG_SMS4:
- return WLAN_CIPHER_SUITE_SMS4;
+ return RSN_CIPHER_SUITE_SMS4;
case WPA_ALG_KRK:
- return WLAN_CIPHER_SUITE_KRK;
+ return RSN_CIPHER_SUITE_KRK;
case WPA_ALG_NONE:
case WPA_ALG_PMK:
wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d",
@@ -2571,21 +2809,21 @@ static u32 wpa_cipher_to_cipher_suite(unsigned int cipher)
{
switch (cipher) {
case WPA_CIPHER_CCMP_256:
- return WLAN_CIPHER_SUITE_CCMP_256;
+ return RSN_CIPHER_SUITE_CCMP_256;
case WPA_CIPHER_GCMP_256:
- return WLAN_CIPHER_SUITE_GCMP_256;
+ return RSN_CIPHER_SUITE_GCMP_256;
case WPA_CIPHER_CCMP:
- return WLAN_CIPHER_SUITE_CCMP;
+ return RSN_CIPHER_SUITE_CCMP;
case WPA_CIPHER_GCMP:
- return WLAN_CIPHER_SUITE_GCMP;
+ return RSN_CIPHER_SUITE_GCMP;
case WPA_CIPHER_TKIP:
- return WLAN_CIPHER_SUITE_TKIP;
+ return RSN_CIPHER_SUITE_TKIP;
case WPA_CIPHER_WEP104:
- return WLAN_CIPHER_SUITE_WEP104;
+ return RSN_CIPHER_SUITE_WEP104;
case WPA_CIPHER_WEP40:
- return WLAN_CIPHER_SUITE_WEP40;
+ return RSN_CIPHER_SUITE_WEP40;
case WPA_CIPHER_GTK_NOT_USED:
- return WLAN_CIPHER_SUITE_NO_GROUP_ADDR;
+ return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
}
return 0;
@@ -2598,19 +2836,19 @@ static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[],
int num_suites = 0;
if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256)
- suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256;
+ suites[num_suites++] = RSN_CIPHER_SUITE_CCMP_256;
if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256)
- suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256;
+ suites[num_suites++] = RSN_CIPHER_SUITE_GCMP_256;
if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP)
- suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
+ suites[num_suites++] = RSN_CIPHER_SUITE_CCMP;
if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP)
- suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
+ suites[num_suites++] = RSN_CIPHER_SUITE_GCMP;
if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP)
- suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
+ suites[num_suites++] = RSN_CIPHER_SUITE_TKIP;
if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104)
- suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
+ suites[num_suites++] = RSN_CIPHER_SUITE_WEP104;
if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40)
- suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
+ suites[num_suites++] = RSN_CIPHER_SUITE_WEP40;
return num_suites;
}
@@ -2647,6 +2885,44 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
#endif /* CONFIG_DRIVER_NL80211_QCA */
+static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
+ const u8 *key, size_t key_len,
+ const u8 *addr)
+{
+ struct nl_msg *msg = NULL;
+ int ret;
+
+ /*
+ * If the authenticator address is not set, assume it is
+ * the current BSSID.
+ */
+ if (!addr && drv->associated)
+ addr = drv->bssid;
+ else if (!addr)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR,
+ MAC2STR(addr));
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK);
+ if (!msg ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put(msg, NL80211_ATTR_PMK, key_len, key)) {
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
enum wpa_alg alg, const u8 *addr,
int key_idx, int set_tx,
@@ -2685,6 +2961,10 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
+ if (alg == WPA_ALG_PMK &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+ return nl80211_set_pmk(drv, key, key_len, addr);
+
if (alg == WPA_ALG_NONE) {
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
if (!msg)
@@ -2868,8 +3148,8 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
params->wep_key[i]) ||
nla_put_u32(msg, NL80211_KEY_CIPHER,
params->wep_key_len[i] == 5 ?
- WLAN_CIPHER_SUITE_WEP40 :
- WLAN_CIPHER_SUITE_WEP104) ||
+ RSN_CIPHER_SUITE_WEP40 :
+ RSN_CIPHER_SUITE_WEP104) ||
nla_put_u8(msg, NL80211_KEY_IDX, i) ||
(i == params->wep_tx_keyidx &&
nla_put_flag(msg, NL80211_KEY_DEFAULT)))
@@ -2885,7 +3165,8 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int cmd, u16 reason_code,
- int local_state_change)
+ int local_state_change,
+ struct nl_handle *nl_connect)
{
int ret;
struct nl_msg *msg;
@@ -2899,7 +3180,10 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (nl_connect)
+ ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL);
+ else
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: MLME command failed: reason=%u ret=%d (%s)",
@@ -2910,20 +3194,22 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
- int reason_code)
+ int reason_code,
+ struct nl_handle *nl_connect)
{
int ret;
+ int drv_associated = drv->associated;
wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
nl80211_mark_disconnected(drv);
/* Disconnect command doesn't need BSSID - it uses cached value */
ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
- reason_code, 0);
+ reason_code, 0, nl_connect);
/*
* For locally generated disconnect, supplicant already generates a
* DEAUTH event, so ignore the event from NL80211.
*/
- drv->ignore_next_local_disconnect = ret == 0;
+ drv->ignore_next_local_disconnect = drv_associated && (ret == 0);
return ret;
}
@@ -2934,23 +3220,31 @@ static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret;
+ int drv_associated = drv->associated;
if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
nl80211_mark_disconnected(drv);
return nl80211_leave_ibss(drv, 1);
}
- if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
- return wpa_driver_nl80211_disconnect(drv, reason_code);
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+ struct nl_handle *nl_connect = NULL;
+
+ if (bss->use_nl_connect)
+ nl_connect = bss->nl_connect;
+ return wpa_driver_nl80211_disconnect(drv, reason_code,
+ nl_connect);
+ }
wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
__func__, MAC2STR(addr), reason_code);
nl80211_mark_disconnected(drv);
ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
- reason_code, 0);
+ reason_code, 0, NULL);
/*
* For locally generated deauthenticate, supplicant already generates a
* DEAUTH event, so ignore the event from NL80211.
*/
- drv->ignore_next_local_deauth = ret == 0;
+ drv->ignore_next_local_deauth = drv_associated && (ret == 0);
+
return ret;
}
@@ -3020,6 +3314,27 @@ static void nl80211_unmask_11b_rates(struct i802_bss *bss)
}
+static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg)
+{
+ if (wpa_auth_alg & WPA_AUTH_ALG_OPEN)
+ return NL80211_AUTHTYPE_OPEN_SYSTEM;
+ if (wpa_auth_alg & WPA_AUTH_ALG_SHARED)
+ return NL80211_AUTHTYPE_SHARED_KEY;
+ if (wpa_auth_alg & WPA_AUTH_ALG_LEAP)
+ return NL80211_AUTHTYPE_NETWORK_EAP;
+ if (wpa_auth_alg & WPA_AUTH_ALG_FT)
+ return NL80211_AUTHTYPE_FT;
+ if (wpa_auth_alg & WPA_AUTH_ALG_SAE)
+ return NL80211_AUTHTYPE_SAE;
+ if (wpa_auth_alg & WPA_AUTH_ALG_FILS)
+ return NL80211_AUTHTYPE_FILS_SK;
+ if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS)
+ return NL80211_AUTHTYPE_FILS_SK_PFS;
+
+ return NL80211_AUTHTYPE_MAX;
+}
+
+
static int wpa_driver_nl80211_authenticate(
struct i802_bss *bss, struct wpa_driver_auth_params *params)
{
@@ -3095,27 +3410,17 @@ retry:
if (params->ie &&
nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
goto fail;
- if (params->sae_data) {
- wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data,
- params->sae_data_len);
- if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len,
- params->sae_data))
+ if (params->auth_data) {
+ wpa_hexdump(MSG_DEBUG, " * auth_data", params->auth_data,
+ params->auth_data_len);
+ if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->auth_data_len,
+ params->auth_data))
goto fail;
}
- if (params->auth_alg & WPA_AUTH_ALG_OPEN)
- type = NL80211_AUTHTYPE_OPEN_SYSTEM;
- else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
- type = NL80211_AUTHTYPE_SHARED_KEY;
- else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
- type = NL80211_AUTHTYPE_NETWORK_EAP;
- else if (params->auth_alg & WPA_AUTH_ALG_FT)
- type = NL80211_AUTHTYPE_FT;
- else if (params->auth_alg & WPA_AUTH_ALG_SAE)
- type = NL80211_AUTHTYPE_SAE;
- else
- goto fail;
+ type = get_nl_auth_type(params->auth_alg);
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
- if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+ if (type == NL80211_AUTHTYPE_MAX ||
+ nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
goto fail;
if (params->local_state_change) {
wpa_printf(MSG_DEBUG, " * Local state change only");
@@ -3127,11 +3432,11 @@ retry:
msg = NULL;
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
- "nl80211: MLME command failed (auth): ret=%d (%s)",
- ret, strerror(-ret));
+ "nl80211: MLME command failed (auth): count=%d ret=%d (%s)",
+ count, ret, strerror(-ret));
count++;
- if (ret == -EALREADY && count == 1 && params->bssid &&
- !params->local_state_change) {
+ if ((ret == -EALREADY || ret == -EEXIST) && count == 1 &&
+ params->bssid && !params->local_state_change) {
/*
* mac80211 does not currently accept new
* authentication if we are already authenticated. As a
@@ -3515,6 +3820,147 @@ static int nl80211_set_mesh_config(void *priv,
#endif /* CONFIG_MESH */
+static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags,
+ struct wpa_driver_ap_params *params)
+{
+ struct nlattr *bands, *band;
+ struct nl80211_txrate_vht vht_rate;
+
+ if (!params->freq ||
+ (params->beacon_rate == 0 &&
+ params->rate_type == BEACON_RATE_LEGACY))
+ return 0;
+
+ bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+ if (!bands)
+ return -1;
+
+ switch (params->freq->mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ case HOSTAPD_MODE_IEEE80211G:
+ band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+ break;
+ case HOSTAPD_MODE_IEEE80211A:
+ band = nla_nest_start(msg, NL80211_BAND_5GHZ);
+ break;
+ case HOSTAPD_MODE_IEEE80211AD:
+ band = nla_nest_start(msg, NL80211_BAND_60GHZ);
+ break;
+ default:
+ return 0;
+ }
+
+ if (!band)
+ return -1;
+
+ os_memset(&vht_rate, 0, sizeof(vht_rate));
+
+ switch (params->rate_type) {
+ case BEACON_RATE_LEGACY:
+ if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support setting Beacon frame rate (legacy)");
+ return -1;
+ }
+
+ if (nla_put_u8(msg, NL80211_TXRATE_LEGACY,
+ (u8) params->beacon_rate / 5) ||
+ nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
+ (params->freq->vht_enabled &&
+ nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+ &vht_rate)))
+ return -1;
+
+ wpa_printf(MSG_DEBUG, " * beacon_rate = legacy:%u (* 100 kbps)",
+ params->beacon_rate);
+ break;
+ case BEACON_RATE_HT:
+ if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_HT)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support setting Beacon frame rate (HT)");
+ return -1;
+ }
+ if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) ||
+ nla_put_u8(msg, NL80211_TXRATE_HT, params->beacon_rate) ||
+ (params->freq->vht_enabled &&
+ nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+ &vht_rate)))
+ return -1;
+ wpa_printf(MSG_DEBUG, " * beacon_rate = HT-MCS %u",
+ params->beacon_rate);
+ break;
+ case BEACON_RATE_VHT:
+ if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_VHT)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support setting Beacon frame rate (VHT)");
+ return -1;
+ }
+ vht_rate.mcs[0] = BIT(params->beacon_rate);
+ if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL))
+ return -1;
+ if (nla_put(msg, NL80211_TXRATE_HT, 0, NULL))
+ return -1;
+ if (nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+ &vht_rate))
+ return -1;
+ wpa_printf(MSG_DEBUG, " * beacon_rate = VHT-MCS %u",
+ params->beacon_rate);
+ break;
+ }
+
+ nla_nest_end(msg, band);
+ nla_nest_end(msg, bands);
+
+ return 0;
+}
+
+
+static int nl80211_set_multicast_to_unicast(struct i802_bss *bss,
+ int multicast_to_unicast)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MULTICAST_TO_UNICAST);
+ if (!msg ||
+ (multicast_to_unicast &&
+ nla_put_flag(msg, NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED))) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to build NL80211_CMD_SET_MULTICAST_TO_UNICAST msg for %s",
+ bss->ifname);
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
+ switch (ret) {
+ case 0:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: multicast to unicast %s on interface %s",
+ multicast_to_unicast ? "enabled" : "disabled",
+ bss->ifname);
+ break;
+ case -EOPNOTSUPP:
+ if (!multicast_to_unicast)
+ break;
+ wpa_printf(MSG_INFO,
+ "nl80211: multicast to unicast not supported on interface %s",
+ bss->ifname);
+ break;
+ default:
+ wpa_printf(MSG_ERROR,
+ "nl80211: %s multicast to unicast failed with %d (%s) on interface %s",
+ multicast_to_unicast ? "enabling" : "disabling",
+ ret, strerror(-ret), bss->ifname);
+ break;
+ }
+
+ return ret;
+}
+
+
static int wpa_driver_nl80211_set_ap(void *priv,
struct wpa_driver_ap_params *params)
{
@@ -3538,6 +3984,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
beacon_set);
if (beacon_set)
cmd = NL80211_CMD_SET_BEACON;
+ else if (!drv->device_ap_sme && !drv->use_monitor &&
+ !nl80211_get_wiphy_data_ap(bss))
+ return -ENOBUFS;
wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
params->head, params->head_len);
@@ -3545,6 +3994,8 @@ static int wpa_driver_nl80211_set_ap(void *priv,
params->tail, params->tail_len);
wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex);
wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
+ wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate);
+ wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type);
wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid",
params->ssid, params->ssid_len);
@@ -3554,6 +4005,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
params->tail) ||
nl80211_put_beacon_int(msg, params->beacon_int) ||
+ nl80211_put_beacon_rate(msg, drv->capa.flags, params) ||
nl80211_put_dtim_period(msg, params->dtim_period) ||
nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
goto fail;
@@ -3616,9 +4068,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
params->key_mgmt_suites);
num_suites = 0;
if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X)
- suites[num_suites++] = WLAN_AKM_SUITE_8021X;
+ suites[num_suites++] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
- suites[num_suites++] = WLAN_AKM_SUITE_PSK;
+ suites[num_suites++] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
if (num_suites &&
nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
suites))
@@ -3664,7 +4116,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
smps_mode = NL80211_SMPS_OFF;
break;
}
- if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
+ if (nla_put_u8(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
goto fail;
}
@@ -3731,6 +4183,8 @@ static int wpa_driver_nl80211_set_ap(void *priv,
nl80211_set_bss(bss, params->cts_protect, params->preamble,
params->short_slot_time, params->ht_opmode,
params->isolate, params->basic_rates);
+ nl80211_set_multicast_to_unicast(bss,
+ params->multicast_to_unicast);
if (beacon_set && params->freq &&
params->freq->bandwidth != bss->bandwidth) {
wpa_printf(MSG_DEBUG,
@@ -4464,6 +4918,7 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
else
nl80211_mgmt_unsubscribe(bss, "AP teardown");
+ nl80211_put_wiphy_data_ap(bss);
bss->beacon_set = 0;
}
@@ -4808,10 +5263,54 @@ fail:
}
+static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params,
+ struct nl_msg *msg)
+{
+ if (params->fils_erp_username_len) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP EMSKname/username",
+ params->fils_erp_username,
+ params->fils_erp_username_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME,
+ params->fils_erp_username_len,
+ params->fils_erp_username))
+ return -1;
+ }
+
+ if (params->fils_erp_realm_len) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP Realm",
+ params->fils_erp_realm,
+ params->fils_erp_realm_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM,
+ params->fils_erp_realm_len, params->fils_erp_realm))
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, " * FILS ERP next seq %u",
+ params->fils_erp_next_seq_num);
+ if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ params->fils_erp_next_seq_num))
+ return -1;
+
+ if (params->fils_erp_rrk_len) {
+ wpa_printf(MSG_DEBUG, " * FILS ERP rRK (len=%lu)",
+ (unsigned long) params->fils_erp_rrk_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
+ params->fils_erp_rrk_len, params->fils_erp_rrk))
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params,
struct nl_msg *msg)
{
+ if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+ return -1;
+
if (params->bssid) {
wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
MAC2STR(params->bssid));
@@ -4912,40 +5411,64 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
- params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
- int mgmt = WLAN_AKM_SUITE_PSK;
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
+ int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
switch (params->key_mgmt_suite) {
case WPA_KEY_MGMT_CCKM:
- mgmt = WLAN_AKM_SUITE_CCKM;
+ mgmt = RSN_AUTH_KEY_MGMT_CCKM;
break;
case WPA_KEY_MGMT_IEEE8021X:
- mgmt = WLAN_AKM_SUITE_8021X;
+ mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
break;
case WPA_KEY_MGMT_FT_IEEE8021X:
- mgmt = WLAN_AKM_SUITE_FT_8021X;
+ mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X;
break;
case WPA_KEY_MGMT_FT_PSK:
- mgmt = WLAN_AKM_SUITE_FT_PSK;
+ mgmt = RSN_AUTH_KEY_MGMT_FT_PSK;
break;
case WPA_KEY_MGMT_IEEE8021X_SHA256:
- mgmt = WLAN_AKM_SUITE_8021X_SHA256;
+ mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
break;
case WPA_KEY_MGMT_PSK_SHA256:
- mgmt = WLAN_AKM_SUITE_PSK_SHA256;
+ mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256;
break;
case WPA_KEY_MGMT_OSEN:
- mgmt = WLAN_AKM_SUITE_OSEN;
+ mgmt = RSN_AUTH_KEY_MGMT_OSEN;
break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
- mgmt = WLAN_AKM_SUITE_8021X_SUITE_B;
+ mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
- mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192;
+ mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ break;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+ break;
+ case WPA_KEY_MGMT_FILS_SHA384:
+ mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+ break;
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+ break;
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+ break;
+ case WPA_KEY_MGMT_OWE:
+ mgmt = RSN_AUTH_KEY_MGMT_OWE;
+ break;
+ case WPA_KEY_MGMT_DPP:
+ mgmt = RSN_AUTH_KEY_MGMT_DPP;
break;
case WPA_KEY_MGMT_PSK:
default:
- mgmt = WLAN_AKM_SUITE_PSK;
+ mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
break;
}
wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt);
@@ -4953,6 +5476,14 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
return -1;
}
+ /* Add PSK in case of 4-way handshake offload */
+ if (params->psk &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) {
+ wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32);
+ if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk))
+ return -1;
+ }
+
if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
return -1;
@@ -4964,10 +5495,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
return -1;
- if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
- nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
- return -1;
-
if (params->rrm_used) {
u32 drv_rrm_flags = drv->capa.rrm_flags;
if ((!((drv_rrm_flags &
@@ -5000,13 +5527,23 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
drv->connect_reassoc = 1;
}
+ if ((params->auth_alg & WPA_AUTH_ALG_FILS) &&
+ nl80211_put_fils_connect_params(drv, params, msg) != 0)
+ return -1;
+
+ if ((params->auth_alg & WPA_AUTH_ALG_SAE) &&
+ (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
+ nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+ return -1;
+
return 0;
}
static int wpa_driver_nl80211_try_connect(
struct wpa_driver_nl80211_data *drv,
- struct wpa_driver_associate_params *params)
+ struct wpa_driver_associate_params *params,
+ struct nl_handle *nl_connect)
{
struct nl_msg *msg;
enum nl80211_auth_type type;
@@ -5034,6 +5571,15 @@ static int wpa_driver_nl80211_try_connect(
if (ret)
goto fail;
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+ goto fail;
+
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_MFP_OPTIONAL) &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL))
+ goto fail;
+
algs = 0;
if (params->auth_alg & WPA_AUTH_ALG_OPEN)
algs++;
@@ -5041,25 +5587,20 @@ static int wpa_driver_nl80211_try_connect(
algs++;
if (params->auth_alg & WPA_AUTH_ALG_LEAP)
algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_FILS)
+ algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_FT)
+ algs++;
if (algs > 1) {
wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
"selection");
goto skip_auth_type;
}
- if (params->auth_alg & WPA_AUTH_ALG_OPEN)
- type = NL80211_AUTHTYPE_OPEN_SYSTEM;
- else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
- type = NL80211_AUTHTYPE_SHARED_KEY;
- else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
- type = NL80211_AUTHTYPE_NETWORK_EAP;
- else if (params->auth_alg & WPA_AUTH_ALG_FT)
- type = NL80211_AUTHTYPE_FT;
- else
- goto fail;
-
+ type = get_nl_auth_type(params->auth_alg);
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
- if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+ if (type == NL80211_AUTHTYPE_MAX ||
+ nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
goto fail;
skip_auth_type:
@@ -5067,7 +5608,11 @@ skip_auth_type:
if (ret)
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (nl_connect)
+ ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL);
+ else
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
@@ -5086,7 +5631,8 @@ fail:
static int wpa_driver_nl80211_connect(
struct wpa_driver_nl80211_data *drv,
- struct wpa_driver_associate_params *params)
+ struct wpa_driver_associate_params *params,
+ struct nl_handle *nl_connect)
{
int ret;
@@ -5096,7 +5642,7 @@ static int wpa_driver_nl80211_connect(
else
os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
- ret = wpa_driver_nl80211_try_connect(drv, params);
+ ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect);
if (ret == -EALREADY) {
/*
* cfg80211 does not currently accept new connections if
@@ -5107,9 +5653,9 @@ static int wpa_driver_nl80211_connect(
"disconnecting before reassociation "
"attempt");
if (wpa_driver_nl80211_disconnect(
- drv, WLAN_REASON_PREV_AUTH_NOT_VALID))
+ drv, WLAN_REASON_PREV_AUTH_NOT_VALID, nl_connect))
return -1;
- ret = wpa_driver_nl80211_try_connect(drv, params);
+ ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect);
}
return ret;
}
@@ -5134,10 +5680,18 @@ static int wpa_driver_nl80211_associate(
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
enum nl80211_iftype nlmode = params->p2p ?
NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+ struct nl_handle *nl_connect = NULL;
if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
return -1;
- return wpa_driver_nl80211_connect(drv, params);
+ if (params->auth_alg & WPA_AUTH_ALG_SAE) {
+ nl_connect = bss->nl_connect;
+ bss->use_nl_connect = 1;
+ } else {
+ bss->use_nl_connect = 0;
+ }
+
+ return wpa_driver_nl80211_connect(drv, params, nl_connect);
}
nl80211_mark_disconnected(drv);
@@ -5152,6 +5706,26 @@ static int wpa_driver_nl80211_associate(
if (ret)
goto fail;
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+ goto fail;
+
+ if (params->fils_kek) {
+ wpa_printf(MSG_DEBUG, " * FILS KEK (len=%u)",
+ (unsigned int) params->fils_kek_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len,
+ params->fils_kek))
+ goto fail;
+ }
+ if (params->fils_nonces) {
+ wpa_hexdump(MSG_DEBUG, " * FILS nonces (for AAD)",
+ params->fils_nonces,
+ params->fils_nonces_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_NONCES,
+ params->fils_nonces_len, params->fils_nonces))
+ goto fail;
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
if (ret) {
@@ -5568,6 +6142,17 @@ static int get_sta_handler(struct nl_msg *msg, void *arg)
[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+ };
+ struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
+ static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+ [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+ [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+ [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+ [NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
};
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -5619,6 +6204,72 @@ static int get_sta_handler(struct nl_msg *msg, void *arg)
if (stats[NL80211_STA_INFO_TX_FAILED])
data->tx_retry_failed =
nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
+ if (stats[NL80211_STA_INFO_SIGNAL])
+ data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
+ if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
+ data->last_ack_rssi =
+ nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
+ data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
+ }
+
+ if (stats[NL80211_STA_INFO_TX_BITRATE] &&
+ nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+ stats[NL80211_STA_INFO_TX_BITRATE],
+ rate_policy) == 0) {
+ if (rate[NL80211_RATE_INFO_BITRATE32])
+ data->current_tx_rate =
+ nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+ else if (rate[NL80211_RATE_INFO_BITRATE])
+ data->current_tx_rate =
+ nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+ if (rate[NL80211_RATE_INFO_MCS]) {
+ data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+ data->flags |= STA_DRV_DATA_TX_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+ data->tx_vhtmcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+ data->flags |= STA_DRV_DATA_TX_VHT_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_SHORT_GI])
+ data->flags |= STA_DRV_DATA_TX_SHORT_GI;
+ if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+ data->tx_vht_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+ data->flags |= STA_DRV_DATA_TX_VHT_NSS;
+ }
+ }
+
+ if (stats[NL80211_STA_INFO_RX_BITRATE] &&
+ nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+ stats[NL80211_STA_INFO_RX_BITRATE],
+ rate_policy) == 0) {
+ if (rate[NL80211_RATE_INFO_BITRATE32])
+ data->current_rx_rate =
+ nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+ else if (rate[NL80211_RATE_INFO_BITRATE])
+ data->current_rx_rate =
+ nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+ if (rate[NL80211_RATE_INFO_MCS]) {
+ data->rx_mcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+ data->flags |= STA_DRV_DATA_RX_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+ data->rx_vhtmcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+ data->flags |= STA_DRV_DATA_RX_VHT_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_SHORT_GI])
+ data->flags |= STA_DRV_DATA_RX_SHORT_GI;
+ if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+ data->rx_vht_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+ data->flags |= STA_DRV_DATA_RX_VHT_NSS;
+ }
+ }
return NL_SKIP;
}
@@ -5629,8 +6280,6 @@ static int i802_read_sta_data(struct i802_bss *bss,
{
struct nl_msg *msg;
- os_memset(data, 0, sizeof(*data));
-
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
nlmsg_free(msg);
@@ -5648,6 +6297,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *txq, *params;
+ int res;
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY);
if (!msg)
@@ -5693,7 +6343,11 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
nla_nest_end(msg, txq);
- if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ res = send_and_recv_msgs(drv, msg, NULL, NULL);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
+ queue, aifs, cw_min, cw_max, burst_time, res);
+ if (res == 0)
return 0;
msg = NULL;
fail:
@@ -5736,6 +6390,7 @@ static int i802_get_inact_sec(void *priv, const u8 *addr)
struct hostap_sta_driver_data data;
int ret;
+ os_memset(&data, 0, sizeof(data));
data.inactive_msec = (unsigned long) -1;
ret = i802_read_sta_data(priv, &data, addr);
if (ret == -ENOENT)
@@ -5761,6 +6416,14 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt mgmt;
+ u8 channel;
+
+ if (ieee80211_freq_to_chan(bss->freq, &channel) ==
+ HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ return i802_sta_disassoc(priv, own_addr, addr, reason);
+ }
if (is_mesh_interface(drv->nlmode))
return -1;
@@ -5942,8 +6605,16 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
char name[IFNAMSIZ + 1];
+ union wpa_event_data event;
+ int ret;
+
+ ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+ if (ret >= (int) sizeof(name))
+ wpa_printf(MSG_WARNING,
+ "nl80211: WDS interface name was truncated");
+ else if (ret < 0)
+ return ret;
- os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
if (ifname_wds)
os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
@@ -5960,6 +6631,14 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
linux_br_add_if(drv->global->ioctl_sock,
bridge_ifname, name) < 0)
return -1;
+
+ os_memset(&event, 0, sizeof(event));
+ event.wds_sta_interface.sta_addr = addr;
+ event.wds_sta_interface.ifname = name;
+ event.wds_sta_interface.istatus = INTERFACE_ADDED;
+ wpa_supplicant_event(bss->ctx,
+ EVENT_WDS_STA_INTERFACE_STATUS,
+ &event);
}
if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
@@ -5973,6 +6652,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
i802_set_sta_vlan(priv, addr, bss->ifname, 0);
nl80211_remove_iface(drv, if_nametoindex(name));
+ os_memset(&event, 0, sizeof(event));
+ event.wds_sta_interface.sta_addr = addr;
+ event.wds_sta_interface.ifname = name;
+ event.wds_sta_interface.istatus = INTERFACE_REMOVED;
+ wpa_supplicant_event(bss->ctx, EVENT_WDS_STA_INTERFACE_STATUS,
+ &event);
return 0;
}
}
@@ -6026,8 +6711,10 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
bss->br_ifindex = br_ifindex;
if (linux_br_get(in_br, ifname) == 0) {
- if (os_strcmp(in_br, brname) == 0)
+ if (os_strcmp(in_br, brname) == 0) {
+ bss->already_in_bridge = 1;
return 0; /* already in the bridge */
+ }
wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
"bridge %s", ifname, in_br);
@@ -6036,7 +6723,7 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
wpa_printf(MSG_ERROR, "nl80211: Failed to "
"remove interface %s from bridge "
"%s: %s",
- ifname, brname, strerror(errno));
+ ifname, in_br, strerror(errno));
return -1;
}
}
@@ -6124,7 +6811,7 @@ static void *i802_init(struct hostapd_data *hapd,
add_ifidx(drv, br_ifindex, drv->ifindex);
#ifdef CONFIG_LIBNL3_ROUTE
- if (bss->added_if_into_bridge) {
+ if (bss->added_if_into_bridge || bss->already_in_bridge) {
drv->rtnl_sk = nl_socket_alloc();
if (drv->rtnl_sk == NULL) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
@@ -6474,7 +7161,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
nl80211_teardown_ap(bss);
if (!bss->added_if && !drv->first_bss->next)
- wpa_driver_nl80211_del_beacon(drv);
+ wpa_driver_nl80211_del_beacon(bss);
nl80211_destroy_bss(bss);
if (!bss->added_if)
i802_set_iface_flags(bss, 0);
@@ -6598,6 +7285,14 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
os_memcpy(hdr->addr2, src, ETH_ALEN);
os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+ if (os_memcmp(bss->addr, src, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR,
+ MAC2STR(src));
+ os_memcpy(bss->rand_addr, src, ETH_ALEN);
+ } else {
+ os_memset(bss->rand_addr, 0, ETH_ALEN);
+ }
+
if (is_ap_interface(drv->nlmode) &&
(!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
(int) freq == bss->freq || drv->device_ap_sme ||
@@ -6745,7 +7440,7 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
} else if (bss->nl_preq) {
wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
"reporting nl_preq=%p", bss->nl_preq);
- nl80211_destroy_eloop_handle(&bss->nl_preq);
+ nl80211_destroy_eloop_handle(&bss->nl_preq, 0);
}
return 0;
}
@@ -6770,7 +7465,7 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
nl80211_register_eloop_read(&bss->nl_preq,
wpa_driver_nl80211_event_receive,
- bss->nl_cb);
+ bss->nl_cb, 0);
return 0;
@@ -6836,7 +7531,7 @@ static int wpa_driver_nl80211_deinit_ap(void *priv)
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!is_ap_interface(drv->nlmode))
return -1;
- wpa_driver_nl80211_del_beacon(drv);
+ wpa_driver_nl80211_del_beacon(bss);
bss->beacon_set = 0;
/*
@@ -6856,7 +7551,7 @@ static int wpa_driver_nl80211_stop_ap(void *priv)
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!is_ap_interface(drv->nlmode))
return -1;
- wpa_driver_nl80211_del_beacon(drv);
+ wpa_driver_nl80211_del_beacon(bss);
bss->beacon_set = 0;
return 0;
}
@@ -7085,7 +7780,7 @@ static void nl80211_global_deinit(void *priv)
nl_destroy_handles(&global->nl);
if (global->nl_event)
- nl80211_destroy_eloop_handle(&global->nl_event);
+ nl80211_destroy_eloop_handle(&global->nl_event, 0);
nl_cb_put(global->nl_cb);
@@ -7104,14 +7799,24 @@ static const char * nl80211_get_radio_name(void *priv)
}
-static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
- const u8 *pmkid)
+static int nl80211_pmkid(struct i802_bss *bss, int cmd,
+ struct wpa_pmkid_params *params)
{
struct nl_msg *msg;
+ const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */
if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
- (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) ||
- (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) {
+ (params->pmkid &&
+ nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) ||
+ (params->bssid &&
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) ||
+ (params->ssid_len &&
+ nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+ (params->fils_cache_id &&
+ nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
+ params->fils_cache_id)) ||
+ (params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
+ nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -7120,28 +7825,68 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
}
-static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
{
struct i802_bss *bss = priv;
- wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
- return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+ int ret;
+
+ if (params->bssid)
+ wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR,
+ MAC2STR(params->bssid));
+ else if (params->fils_cache_id && params->ssid_len) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add PMKSA for cache id %02x%02x SSID %s",
+ params->fils_cache_id[0], params->fils_cache_id[1],
+ wpa_ssid_txt(params->ssid, params->ssid_len));
+ }
+
+ ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
}
-static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params)
{
struct i802_bss *bss = priv;
- wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
- MAC2STR(bssid));
- return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+ int ret;
+
+ if (params->bssid)
+ wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+ MAC2STR(params->bssid));
+ else if (params->fils_cache_id && params->ssid_len) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Delete PMKSA for cache id %02x%02x SSID %s",
+ params->fils_cache_id[0], params->fils_cache_id[1],
+ wpa_ssid_txt(params->ssid, params->ssid_len));
+ }
+
+ ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
}
static int nl80211_flush_pmkid(void *priv)
{
struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+
wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
- return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
+ if (!msg)
+ return -ENOBUFS;
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
}
@@ -7336,7 +8081,7 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
!(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
- nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
+ (kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) ||
nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
replay_ctr)) {
nl80211_nlmsg_clear(msg);
@@ -7424,6 +8169,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
{
struct nl_msg *msg;
+ int ret;
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
nla_put_u32(msg, NL80211_ATTR_PS_STATE,
@@ -7431,7 +8177,15 @@ static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
nlmsg_free(msg);
return -ENOBUFS;
}
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Setting PS state %s failed: %d (%s)",
+ enabled ? "enabled" : "disabled",
+ ret, strerror(-ret));
+ }
+ return ret;
}
@@ -7545,6 +8299,7 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
enum nl80211_tdls_operation nl80211_oper;
+ int res;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
return -EOPNOTSUPP;
@@ -7580,7 +8335,11 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
return -ENOBUFS;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ res = send_and_recv_msgs(drv, msg, NULL, NULL);
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
+ " --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
+ strerror(-res));
+ return res;
}
@@ -7738,6 +8497,8 @@ static int driver_nl80211_read_sta_data(void *priv,
const u8 *addr)
{
struct i802_bss *bss = priv;
+
+ os_memset(data, 0, sizeof(*data));
return i802_read_sta_data(bss, data, addr);
}
@@ -7842,7 +8603,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
"brname=%s\n"
"addr=" MACSTR "\n"
"freq=%d\n"
- "%s%s%s%s%s",
+ "%s%s%s%s%s%s",
bss->ifindex,
bss->ifname,
bss->brname,
@@ -7851,6 +8612,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
bss->beacon_set ? "beacon_set=1\n" : "",
bss->added_if_into_bridge ?
"added_if_into_bridge=1\n" : "",
+ bss->already_in_bridge ? "already_in_bridge=1\n" : "",
bss->added_bridge ? "added_bridge=1\n" : "",
bss->in_deinit ? "in_deinit=1\n" : "",
bss->if_dynamic ? "if_dynamic=1\n" : "");
@@ -8026,8 +8788,9 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
return -EOPNOTSUPP;
}
- if ((drv->nlmode != NL80211_IFTYPE_AP) &&
- (drv->nlmode != NL80211_IFTYPE_P2P_GO))
+ if (drv->nlmode != NL80211_IFTYPE_AP &&
+ drv->nlmode != NL80211_IFTYPE_P2P_GO &&
+ drv->nlmode != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
/*
@@ -8388,6 +9151,95 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
return send_and_recv_msgs(drv, msg, NULL, NULL);
}
+
+
+static int nl80211_disable_fils(void *priv, int disable)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Disable FILS=%d", disable);
+
+ if (!drv->set_wifi_conf_vendor_cmd_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS,
+ disable)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
+#define WPA_SUPPLICANT_CLIENT_ID 1
+
+static int nl80211_set_bssid_blacklist(void *priv, unsigned int num_bssid,
+ const u8 *bssid)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params, *nlbssids, *attr;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set blacklist BSSID (num=%u)",
+ num_bssid);
+
+ if (!drv->roam_vendor_cmd_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID,
+ WPA_SUPPLICANT_CLIENT_ID) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID,
+ num_bssid))
+ goto fail;
+
+ nlbssids = nla_nest_start(
+ msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS);
+ if (!nlbssids)
+ goto fail;
+
+ for (i = 0; i < num_bssid; i++) {
+ attr = nla_nest_start(msg, i);
+ if (!attr)
+ goto fail;
+ if (nla_put(msg,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID,
+ ETH_ALEN, &bssid[i * ETH_ALEN]))
+ goto fail;
+ wpa_printf(MSG_DEBUG, "nl80211: BSSID[%u]: " MACSTR, i,
+ MAC2STR(&bssid[i * ETH_ALEN]));
+ nla_nest_end(msg, attr);
+ }
+ nla_nest_end(msg, nlbssids);
+ nla_nest_end(msg, params);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
#endif /* CONFIG_DRIVER_NL80211_QCA */
@@ -8470,11 +9322,14 @@ static int nl80211_put_mesh_config(struct nl_msg *msg,
return -1;
if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
- nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
- params->auto_plinks)) ||
+ nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ params->auto_plinks)) ||
((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
- params->max_peer_links)))
+ params->max_peer_links)) ||
+ ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) &&
+ nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
+ params->rssi_threshold)))
return -1;
/*
@@ -9307,6 +10162,304 @@ static int nl80211_p2p_lo_stop(void *priv)
return send_and_recv_msgs(drv, msg, NULL, NULL);
}
+
+static int nl80211_set_tdls_mode(void *priv, int tdls_external_control)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params;
+ int ret;
+ u32 tdls_mode;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set TDKS mode: tdls_external_control=%d",
+ tdls_external_control);
+
+ if (tdls_external_control == 1)
+ tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT |
+ QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL;
+ else
+ tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
+ goto fail;
+
+ params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!params)
+ goto fail;
+
+ if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE,
+ tdls_mode))
+ goto fail;
+
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Set TDLS mode failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+ return 0;
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+#ifdef CONFIG_MBO
+
+static enum mbo_transition_reject_reason
+nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status)
+{
+ switch (status) {
+ case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED:
+ return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+ case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED:
+ return MBO_TRANSITION_REJECT_REASON_DELAY;
+ case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY:
+ return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY;
+ case QCA_STATUS_REJECT_LOW_RSSI:
+ return MBO_TRANSITION_REJECT_REASON_RSSI;
+ case QCA_STATUS_REJECT_HIGH_INTERFERENCE:
+ return MBO_TRANSITION_REJECT_REASON_INTERFERENCE;
+ case QCA_STATUS_REJECT_UNKNOWN:
+ default:
+ return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+ }
+}
+
+
+static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+ struct nlattr *tb[], int num)
+{
+ enum qca_wlan_btm_candidate_status status;
+ char buf[50];
+
+ os_memcpy(candidate->bssid,
+ nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+ ETH_ALEN);
+
+ status = nla_get_u32(
+ tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]);
+ candidate->is_accept = status == QCA_STATUS_ACCEPT;
+ candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status);
+
+ if (candidate->is_accept)
+ os_snprintf(buf, sizeof(buf), "Accepted");
+ else
+ os_snprintf(buf, sizeof(buf),
+ "Rejected, Reject_reason: %d",
+ candidate->reject_reason);
+ wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR " %s",
+ num, MAC2STR(candidate->bssid), buf);
+}
+
+
+static int
+nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+{
+ struct wpa_bss_candidate_info *info = arg;
+ struct candidate_list *candidate = info->candidates;
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+ static struct nla_policy policy[
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = {
+ [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+ .minlen = ETH_ALEN
+ },
+ [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+ .type = NLA_U32,
+ },
+ };
+ struct nlattr *attr;
+ int rem;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ u8 num;
+
+ num = info->num; /* number of candidates sent to driver */
+ info->num = 0;
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb_msg[NL80211_ATTR_VENDOR_DATA] ||
+ nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+ tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) ||
+ !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO])
+ return NL_SKIP;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: WNM Candidate list received from driver");
+ nla_for_each_nested(attr,
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+ rem) {
+ if (info->num >= num ||
+ nla_parse_nested(
+ tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+ attr, policy) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS])
+ break;
+
+ nl80211_parse_btm_candidate_info(candidate, tb, info->num);
+
+ candidate++;
+ info->num++;
+ }
+
+ return NL_SKIP;
+}
+
+
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *attr, *attr1, *attr2;
+ struct wpa_bss_candidate_info *info;
+ u8 i;
+ int ret;
+ u8 *pos;
+
+ if (!drv->fetch_bss_trans_status)
+ return NULL;
+
+ info = os_zalloc(sizeof(*info));
+ if (!info)
+ return NULL;
+ /* Allocate memory for number of candidates sent to driver */
+ info->candidates = os_calloc(params->n_candidates,
+ sizeof(*info->candidates));
+ if (!info->candidates) {
+ os_free(info);
+ return NULL;
+ }
+
+ /* Copy the number of candidates being sent to driver. This is used in
+ * nl80211_get_bss_transition_status_handler() to limit the number of
+ * candidates that can be populated in info->candidates and will be
+ * later overwritten with the actual number of candidates received from
+ * the driver.
+ */
+ info->num = params->n_candidates;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON,
+ params->mbo_transition_reason))
+ goto fail;
+
+ attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+ if (!attr1)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d",
+ params->mbo_transition_reason, params->n_candidates);
+ pos = params->bssid;
+ for (i = 0; i < params->n_candidates; i++) {
+ wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR, i,
+ MAC2STR(pos));
+ attr2 = nla_nest_start(msg, i);
+ if (!attr2 ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+ ETH_ALEN, pos))
+ goto fail;
+ pos += ETH_ALEN;
+ nla_nest_end(msg, attr2);
+ }
+
+ nla_nest_end(msg, attr1);
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg,
+ nl80211_get_bss_transition_status_handler,
+ info);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: WNM Get BSS transition status failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+ return info;
+
+fail:
+ nlmsg_free(msg);
+ os_free(info->candidates);
+ os_free(info);
+ return NULL;
+}
+
+
+/**
+ * nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ignore_assoc_disallow: 0 to not ignore, 1 to ignore
+ * Returns: 0 on success, -1 on failure
+ */
+static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *attr;
+ int ret = -1;
+
+ if (!drv->set_wifi_conf_vendor_cmd_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d",
+ ignore_disallow);
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
+ ignore_disallow))
+ goto fail;
+
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+#endif /* CONFIG_MBO */
+
#endif /* CONFIG_DRIVER_NL80211_QCA */
@@ -9434,6 +10587,88 @@ static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type,
}
+static int nl80211_update_connection_params(
+ void *priv, struct wpa_driver_associate_params *params,
+ enum wpa_drv_update_connect_params_mask mask)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -1;
+ enum nl80211_auth_type type;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+ if (!msg)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)",
+ drv->ifindex);
+
+ if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) {
+ if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+ params->wpa_ie))
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie,
+ params->wpa_ie_len);
+ }
+
+ if (mask & WPA_DRV_UPDATE_AUTH_TYPE) {
+ type = get_nl_auth_type(params->auth_alg);
+ if (type == NL80211_AUTHTYPE_MAX ||
+ nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+ goto fail;
+ wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
+ }
+
+ if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) &&
+ nl80211_put_fils_connect_params(drv, params, msg))
+ goto fail;
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret)
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: Update connect params command failed: ret=%d (%s)",
+ ret, strerror(-ret));
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_send_external_auth_status(void *priv,
+ struct external_auth *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg = NULL;
+ int ret = -1;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: External auth status: %u", params->status);
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
+ if (!msg ||
+ nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
+ nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid) ||
+ nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))
+ goto fail;
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: External Auth status update failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -9543,6 +10778,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.get_ifindex = nl80211_get_ifindex,
#ifdef CONFIG_DRIVER_NL80211_QCA
.roaming = nl80211_roaming,
+ .disable_fils = nl80211_disable_fils,
.do_acs = wpa_driver_do_acs,
.set_band = nl80211_set_band,
.get_pref_freq_list = nl80211_get_pref_freq_list,
@@ -9550,7 +10786,15 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.p2p_lo_start = nl80211_p2p_lo_start,
.p2p_lo_stop = nl80211_p2p_lo_stop,
.set_default_scan_ies = nl80211_set_default_scan_ies,
+ .set_tdls_mode = nl80211_set_tdls_mode,
+#ifdef CONFIG_MBO
+ .get_bss_transition_status = nl80211_get_bss_transition_status,
+ .ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
+#endif /* CONFIG_MBO */
+ .set_bssid_blacklist = nl80211_set_bssid_blacklist,
#endif /* CONFIG_DRIVER_NL80211_QCA */
.configure_data_frame_filters = nl80211_configure_data_frame_filters,
.get_ext_capab = nl80211_get_ext_capab,
+ .update_connect_params = nl80211_update_connection_params,
+ .send_external_auth_status = nl80211_send_external_auth_status,
};
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index d0ec48c9f973..bc562ba7a8cc 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -60,11 +60,13 @@ struct i802_bss {
char brname[IFNAMSIZ];
unsigned int beacon_set:1;
unsigned int added_if_into_bridge:1;
+ unsigned int already_in_bridge:1;
unsigned int added_bridge:1;
unsigned int in_deinit:1;
unsigned int wdev_id_set:1;
unsigned int added_if:1;
unsigned int static_ap:1;
+ unsigned int use_nl_connect:1;
u8 addr[ETH_ALEN];
@@ -73,11 +75,12 @@ struct i802_bss {
int if_dynamic;
void *ctx;
- struct nl_handle *nl_preq, *nl_mgmt;
+ struct nl_handle *nl_preq, *nl_mgmt, *nl_connect;
struct nl_cb *nl_cb;
struct nl80211_wiphy_data *wiphy_data;
struct dl_list wiphy_list;
+ u8 rand_addr[ETH_ALEN];
};
struct wpa_driver_nl80211_data {
@@ -160,6 +163,9 @@ struct wpa_driver_nl80211_data {
unsigned int scan_vendor_cmd_avail:1;
unsigned int connect_reassoc:1;
unsigned int set_wifi_conf_vendor_cmd_avail:1;
+ unsigned int he_capab_vendor_cmd_avail:1;
+ unsigned int fetch_bss_trans_status:1;
+ unsigned int roam_vendor_cmd_avail:1;
u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
@@ -208,6 +214,8 @@ struct wpa_driver_nl80211_data {
* (NL80211_CMD_VENDOR). 0 if no pending scan request.
*/
int last_scan_cmd;
+
+ struct he_capabilities he_capab;
};
struct nl_msg;
@@ -228,6 +236,7 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
void *arg, int use_existing);
void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid);
enum chan_width convert2width(int width);
void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
@@ -244,7 +253,8 @@ int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
enum nl80211_iftype nlmode);
int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int cmd, u16 reason_code,
- int local_state_change);
+ int local_state_change,
+ struct nl_handle *nl_connect);
int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
@@ -254,7 +264,8 @@ int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
struct hostapd_hw_modes *
-nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags);
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+ u8 *dfs_domain);
int process_global_event(struct nl_msg *msg, void *arg);
int process_bss_event(struct nl_msg *msg, void *arg);
@@ -282,15 +293,6 @@ int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
/* driver_nl80211_scan.c */
-struct nl80211_bss_info_arg {
- struct wpa_driver_nl80211_data *drv;
- struct wpa_scan_results *res;
- unsigned int assoc_freq;
- unsigned int ibss_freq;
- u8 assoc_bssid[ETH_ALEN];
-};
-
-int bss_info_handler(struct nl_msg *msg, void *arg);
void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
int wpa_driver_nl80211_scan(struct i802_bss *bss,
struct wpa_driver_scan_params *params);
@@ -299,7 +301,7 @@ int wpa_driver_nl80211_sched_scan(void *priv,
int wpa_driver_nl80211_stop_sched_scan(void *priv);
struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
-int wpa_driver_nl80211_abort_scan(void *priv);
+int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
struct wpa_driver_scan_params *params);
int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 6adc3f6d33dc..7b360d209aba 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -12,8 +12,8 @@
#include <netlink/genl/genl.h>
#include "utils/common.h"
-#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
#include "common/qca-vendor.h"
#include "common/qca-vendor-attr.h"
#include "driver_nl80211.h"
@@ -266,40 +266,40 @@ static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
c >> 24, (c >> 16) & 0xff,
(c >> 8) & 0xff, c & 0xff);
switch (c) {
- case WLAN_CIPHER_SUITE_CCMP_256:
+ case RSN_CIPHER_SUITE_CCMP_256:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
break;
- case WLAN_CIPHER_SUITE_GCMP_256:
+ case RSN_CIPHER_SUITE_GCMP_256:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
break;
- case WLAN_CIPHER_SUITE_CCMP:
+ case RSN_CIPHER_SUITE_CCMP:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
break;
- case WLAN_CIPHER_SUITE_GCMP:
+ case RSN_CIPHER_SUITE_GCMP:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
break;
- case WLAN_CIPHER_SUITE_TKIP:
+ case RSN_CIPHER_SUITE_TKIP:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
break;
- case WLAN_CIPHER_SUITE_WEP104:
+ case RSN_CIPHER_SUITE_WEP104:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
break;
- case WLAN_CIPHER_SUITE_WEP40:
+ case RSN_CIPHER_SUITE_WEP40:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
+ case RSN_CIPHER_SUITE_AES_128_CMAC:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
break;
- case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case RSN_CIPHER_SUITE_BIP_GMAC_128:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
break;
- case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ case RSN_CIPHER_SUITE_BIP_GMAC_256:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
break;
- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case RSN_CIPHER_SUITE_BIP_CMAC_256:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
break;
- case WLAN_CIPHER_SUITE_NO_GROUP_ADDR:
+ case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
break;
}
@@ -362,6 +362,72 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
+
+ if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA))
+ capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
+ capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_RATE_HT))
+ capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_HT;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_RATE_VHT))
+ capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL))
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SCAN_START_TIME) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BSS_PARENT_TSF) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL))
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
+ capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
+ capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
+ capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD))
+ capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
+ capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL))
+ capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_DFS_OFFLOAD))
+ capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+
+#ifdef CONFIG_MBO
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) &&
+ ext_feature_isset(
+ ext_features, len,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION))
+ capa->flags |= WPA_DRIVER_FLAGS_OCE_STA;
+#endif /* CONFIG_MBO */
}
@@ -510,23 +576,22 @@ static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv,
nl80211_iftype_str(capa->iftype));
len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
- capa->ext_capa = os_malloc(len);
+ capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
+ len);
if (!capa->ext_capa)
goto err;
- os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
- len);
capa->ext_capa_len = len;
wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
capa->ext_capa, capa->ext_capa_len);
len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
- capa->ext_capa_mask = os_malloc(len);
+ capa->ext_capa_mask =
+ os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]),
+ len);
if (!capa->ext_capa_mask)
goto err;
- os_memcpy(capa->ext_capa_mask,
- nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len);
wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
capa->ext_capa_mask, capa->ext_capa_len);
@@ -708,6 +773,15 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION:
drv->set_wifi_conf_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES:
+ drv->he_capab_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
+ drv->fetch_bss_trans_status = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_ROAM:
+ drv->roam_vendor_cmd_avail = 1;
+ break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
}
}
@@ -744,6 +818,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
capa->max_csa_counters =
nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
+ if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
+ capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
+
return NL_SKIP;
}
@@ -877,6 +954,100 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
}
+static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct he_capabilities *he_capab = arg;
+ struct nlattr *nl_vend;
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX + 1];
+ size_t len;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_VENDOR_DATA])
+ return NL_SKIP;
+
+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX,
+ nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]) {
+ u8 he_supported;
+
+ he_supported = nla_get_u8(
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]);
+ wpa_printf(MSG_DEBUG, "nl80211: HE capabilities supported: %u",
+ he_supported);
+ he_capab->he_supported = he_supported;
+ if (!he_supported)
+ return NL_SKIP;
+ }
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]) {
+ len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]);
+
+ if (len > sizeof(he_capab->phy_cap))
+ len = sizeof(he_capab->phy_cap);
+ os_memcpy(he_capab->phy_cap,
+ nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]),
+ len);
+ }
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB])
+ he_capab->mac_cap =
+ nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS])
+ he_capab->mcs =
+ nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS])
+ he_capab->ppet.numss_m1 =
+ nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK])
+ he_capab->ppet.ru_count =
+ nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]) {
+ len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]);
+
+ if (len > sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0))
+ len = sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0);
+ os_memcpy(he_capab->ppet.ppet16_ppet8_ru3_ru0,
+ nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]),
+ len);
+ }
+
+ return NL_SKIP;
+}
+
+
+static void qca_nl80211_check_he_capab(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ if (!drv->he_capab_vendor_cmd_avail)
+ return;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES)) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, qca_nl80211_he_capab_handler,
+ &drv->he_capab);
+ if (!ret && drv->he_capab.he_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_HE_CAPABILITIES;
+}
+
+
struct features_info {
u8 *flags;
size_t flags_len;
@@ -973,6 +1144,12 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
os_free(info.flags);
}
@@ -994,7 +1171,19 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
- WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 |
+ WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+ WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+ else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+
drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
@@ -1046,8 +1235,10 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
#ifdef CONFIG_DRIVER_NL80211_QCA
- qca_nl80211_check_dfs_capa(drv);
+ if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD))
+ qca_nl80211_check_dfs_capa(drv);
qca_nl80211_get_features(drv);
+ qca_nl80211_check_he_capab(drv);
/*
* To enable offchannel simultaneous support in wpa_supplicant, the
@@ -1069,6 +1260,7 @@ struct phy_info_arg {
struct hostapd_hw_modes *modes;
int last_mode, last_chan_idx;
int failed;
+ u8 dfs_domain;
};
static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
@@ -1388,14 +1580,13 @@ wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
mode11g = &modes[mode11g_idx];
mode->num_channels = mode11g->num_channels;
- mode->channels = os_malloc(mode11g->num_channels *
+ mode->channels = os_memdup(mode11g->channels,
+ mode11g->num_channels *
sizeof(struct hostapd_channel_data));
if (mode->channels == NULL) {
(*num_modes)--;
return modes; /* Could not add 802.11b mode */
}
- os_memcpy(mode->channels, mode11g->channels,
- mode11g->num_channels * sizeof(struct hostapd_channel_data));
mode->num_rates = 0;
mode->rates = os_malloc(4 * sizeof(int));
@@ -1598,6 +1789,20 @@ static void nl80211_reg_rule_vht(struct nlattr *tb[],
}
+static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
+ u8 *dfs_domain)
+{
+ if (region == NL80211_DFS_FCC)
+ *dfs_domain = HOSTAPD_DFS_REGION_FCC;
+ else if (region == NL80211_DFS_ETSI)
+ *dfs_domain = HOSTAPD_DFS_REGION_ETSI;
+ else if (region == NL80211_DFS_JP)
+ *dfs_domain = HOSTAPD_DFS_REGION_JP;
+ else
+ *dfs_domain = 0;
+}
+
+
static const char * dfs_domain_name(enum nl80211_dfs_regions region)
{
switch (region) {
@@ -1644,6 +1849,7 @@ static int nl80211_get_reg(struct nl_msg *msg, void *arg)
if (tb_msg[NL80211_ATTR_DFS_REGION]) {
enum nl80211_dfs_regions dfs_domain;
dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+ nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain);
wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
(char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
dfs_domain_name(dfs_domain));
@@ -1715,12 +1921,20 @@ static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
return -ENOMEM;
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
}
struct hostapd_hw_modes *
-nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+ u8 *dfs_domain)
{
u32 feat;
struct i802_bss *bss = priv;
@@ -1732,10 +1946,12 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
.modes = NULL,
.last_mode = -1,
.failed = 0,
+ .dfs_domain = 0,
};
*num_modes = 0;
*flags = 0;
+ *dfs_domain = 0;
feat = get_nl80211_protocol_features(drv);
if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
@@ -1756,8 +1972,12 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
os_free(result.modes[i].rates);
}
os_free(result.modes);
+ *num_modes = 0;
return NULL;
}
+
+ *dfs_domain = result.dfs_domain;
+
return wpa_driver_nl80211_postprocess_modes(result.modes,
num_modes);
}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 762e3acc2807..205b4cd4b082 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -1,6 +1,6 @@
/*
* Driver interaction with Linux nl80211/cfg80211 - Event processing
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
* Copyright (c) 2009-2010, Atheros Communications
*
@@ -131,6 +131,11 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
C2S(NL80211_CMD_SET_QOS_MAP)
C2S(NL80211_CMD_ADD_TX_TS)
C2S(NL80211_CMD_DEL_TX_TS)
+ C2S(NL80211_CMD_WIPHY_REG_CHANGE)
+ C2S(NL80211_CMD_PORT_AUTHORIZED)
+ C2S(NL80211_CMD_EXTERNAL_AUTH)
+ C2S(NL80211_CMD_STA_OPMODE_CHANGED)
+ C2S(NL80211_CMD_CONTROL_PORT_FRAME)
default:
return "NL80211_CMD_UNKNOWN";
}
@@ -206,6 +211,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
const struct ieee80211_mgmt *mgmt;
union wpa_event_data event;
u16 status;
+ int ssid_len;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
drv->force_connect_cmd) {
@@ -247,6 +253,8 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
os_memset(&event, 0, sizeof(event));
+ event.assoc_info.resp_frame = frame;
+ event.assoc_info.resp_frame_len = len;
if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
event.assoc_info.resp_ies_len =
@@ -255,6 +263,16 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
event.assoc_info.freq = drv->assoc_freq;
+ /* When this association was initiated outside of wpa_supplicant,
+ * drv->ssid needs to be set here to satisfy later checking. */
+ ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid);
+ if (ssid_len > 0) {
+ drv->ssid_len = ssid_len;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set drv->ssid based on scan res info to '%s'",
+ wpa_ssid_txt(drv->ssid, drv->ssid_len));
+ }
+
nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
@@ -266,15 +284,20 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
struct nlattr *addr, struct nlattr *req_ie,
struct nlattr *resp_ie,
struct nlattr *timed_out,
+ struct nlattr *timeout_reason,
struct nlattr *authorized,
struct nlattr *key_replay_ctr,
struct nlattr *ptk_kck,
struct nlattr *ptk_kek,
- struct nlattr *subnet_status)
+ struct nlattr *subnet_status,
+ struct nlattr *fils_erp_next_seq_num,
+ struct nlattr *fils_pmk,
+ struct nlattr *fils_pmkid)
{
union wpa_event_data event;
- const u8 *ssid;
+ const u8 *ssid = NULL;
u16 status_code;
+ int ssid_len;
if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
/*
@@ -324,6 +347,27 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
}
event.assoc_reject.status_code = status_code;
event.assoc_reject.timed_out = timed_out != NULL;
+ if (timed_out && timeout_reason) {
+ enum nl80211_timeout_reason reason;
+
+ reason = nla_get_u32(timeout_reason);
+ switch (reason) {
+ case NL80211_TIMEOUT_SCAN:
+ event.assoc_reject.timeout_reason = "scan";
+ break;
+ case NL80211_TIMEOUT_AUTH:
+ event.assoc_reject.timeout_reason = "auth";
+ break;
+ case NL80211_TIMEOUT_ASSOC:
+ event.assoc_reject.timeout_reason = "assoc";
+ break;
+ default:
+ break;
+ }
+ }
+ if (fils_erp_next_seq_num)
+ event.assoc_reject.fils_erp_next_seq_num =
+ nla_get_u16(fils_erp_next_seq_num);
wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
return;
}
@@ -345,6 +389,10 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
drv->ssid_len = ssid[1];
os_memcpy(drv->ssid, ssid + 2, ssid[1]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set drv->ssid based on req_ie to '%s'",
+ wpa_ssid_txt(drv->ssid,
+ drv->ssid_len));
}
}
}
@@ -355,6 +403,16 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+ if ((!ssid || ssid[1] == 0 || ssid[1] > 32) &&
+ (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) {
+ /* When this connection was initiated outside of wpa_supplicant,
+ * drv->ssid needs to be set here to satisfy later checking. */
+ drv->ssid_len = ssid_len;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set drv->ssid based on scan res info to '%s'",
+ wpa_ssid_txt(drv->ssid, drv->ssid_len));
+ }
+
if (authorized && nla_get_u8(authorized)) {
event.assoc_info.authorized = 1;
wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
@@ -383,6 +441,18 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
event.assoc_info.subnet_status = nla_get_u8(subnet_status);
}
+ if (fils_erp_next_seq_num)
+ event.assoc_info.fils_erp_next_seq_num =
+ nla_get_u16(fils_erp_next_seq_num);
+
+ if (fils_pmk) {
+ event.assoc_info.fils_pmk = nla_data(fils_pmk);
+ event.assoc_info.fils_pmk_len = nla_len(fils_pmk);
+ }
+
+ if (fils_pmkid)
+ event.assoc_info.fils_pmkid = nla_data(fils_pmkid);
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
@@ -516,6 +586,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
data.ch_switch.cf2 = nla_get_u32(cf2);
bss->freq = data.ch_switch.freq;
+ drv->assoc_freq = data.ch_switch.freq;
wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
}
@@ -607,7 +678,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
cookie_val = nla_get_u64(cookie);
wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
- " cookie=0%llx%s (ack=%d)",
+ " cookie=0x%llx%s (ack=%d)",
(long long unsigned int) cookie_val,
cookie_val == drv->send_action_cookie ?
" (match)" : " (unknown)", ack != NULL);
@@ -683,12 +754,12 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
* disconnection event for the old AP may show up after
* we have started connection with the new AP.
*/
- wpa_printf(MSG_DEBUG,
- "nl80211: Ignore deauth/disassoc event from old AP "
- MACSTR
- " when already connecting with " MACSTR,
- MAC2STR(bssid),
- MAC2STR(drv->auth_attempt_bssid));
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore deauth/disassoc event from old AP "
+ MACSTR
+ " when already connecting with " MACSTR,
+ MAC2STR(bssid),
+ MAC2STR(drv->auth_attempt_bssid));
return;
}
@@ -831,6 +902,8 @@ static void mlme_event(struct i802_bss *bss,
MAC2STR(data + 4 + ETH_ALEN));
if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+ (is_zero_ether_addr(bss->rand_addr) ||
+ os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
"for foreign address", bss->ifname);
@@ -1081,6 +1154,16 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
msg);
}
+
+ if (tb[NL80211_ATTR_SCAN_START_TIME_TSF] &&
+ tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]) {
+ info->scan_start_tsf =
+ nla_get_u64(tb[NL80211_ATTR_SCAN_START_TIME_TSF]);
+ os_memcpy(info->scan_start_tsf_bssid,
+ nla_data(tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]),
+ ETH_ALEN);
+ }
+
wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
}
@@ -1093,6 +1176,10 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_BEACON_LOSS_EVENT] = { .type = NLA_FLAG },
};
struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
enum nl80211_cqm_rssi_threshold_event event;
@@ -1114,12 +1201,39 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
return;
os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
ETH_ALEN);
+ ed.low_ack.num_packets =
+ nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]);
+ wpa_printf(MSG_DEBUG, "nl80211: Packet loss event for " MACSTR
+ " (num_packets %u)",
+ MAC2STR(ed.low_ack.addr), ed.low_ack.num_packets);
wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
return;
}
- if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+ if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Beacon loss event");
+ wpa_supplicant_event(drv->ctx, EVENT_BEACON_LOSS, NULL);
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_TXE_RATE] &&
+ cqm[NL80211_ATTR_CQM_TXE_PKTS] &&
+ cqm[NL80211_ATTR_CQM_TXE_INTVL] &&
+ cqm[NL80211_ATTR_MAC]) {
+ wpa_printf(MSG_DEBUG, "nl80211: CQM TXE event for " MACSTR
+ " (rate: %u pkts: %u interval: %u)",
+ MAC2STR((u8 *) nla_data(cqm[NL80211_ATTR_MAC])),
+ nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_RATE]),
+ nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_PKTS]),
+ nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_INTVL]));
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not a CQM RSSI threshold event");
return;
+ }
event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
@@ -1130,8 +1244,12 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
"event: RSSI low");
ed.signal_change.above_threshold = 0;
- } else
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Unknown CQM RSSI threshold event: %d",
+ event);
return;
+ }
res = nl80211_get_link_signal(drv, &sig);
if (res == 0) {
@@ -1473,6 +1591,13 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
case NL80211_RADAR_NOP_FINISHED:
wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
break;
+ case NL80211_RADAR_PRE_CAC_EXPIRED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+ &data);
+ break;
+ case NL80211_RADAR_CAC_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
"received", event_type);
@@ -1650,12 +1775,15 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
- NULL,
+ NULL, NULL,
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
- tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]);
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]);
}
@@ -2055,6 +2183,146 @@ static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
}
+static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data event;
+ enum nl80211_external_auth_action act;
+
+ if (!tb[NL80211_ATTR_AKM_SUITES] ||
+ !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] ||
+ !tb[NL80211_ATTR_BSSID] ||
+ !tb[NL80211_ATTR_SSID])
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]);
+ switch (act) {
+ case NL80211_EXTERNAL_AUTH_START:
+ event.external_auth.action = EXT_AUTH_START;
+ break;
+ case NL80211_EXTERNAL_AUTH_ABORT:
+ event.external_auth.action = EXT_AUTH_ABORT;
+ break;
+ default:
+ return;
+ }
+
+ event.external_auth.key_mgmt_suite =
+ nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]);
+
+ event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]);
+ if (event.external_auth.ssid_len > SSID_MAX_LEN)
+ return;
+ os_memcpy(event.external_auth.ssid, nla_data(tb[NL80211_ATTR_SSID]),
+ event.external_auth.ssid_len);
+
+ os_memcpy(event.external_auth.bssid, nla_data(tb[NL80211_ATTR_BSSID]),
+ ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: External auth action: %u, AKM: 0x%x",
+ event.external_auth.action,
+ event.external_auth.key_mgmt_suite);
+ wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event);
+}
+
+
+static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ const u8 *addr;
+
+ if (!tb[NL80211_ATTR_MAC] ||
+ nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore port authorized event without BSSID");
+ return;
+ }
+
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore port authorized event for " MACSTR
+ " (not the currently connected BSSID " MACSTR ")",
+ MAC2STR(addr), MAC2STR(drv->bssid));
+ return;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, NULL);
+}
+
+
+static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data ed;
+ u8 smps_mode, max_bw;
+
+ if (!tb[NL80211_ATTR_MAC] ||
+ (!tb[NL80211_ATTR_CHANNEL_WIDTH] &&
+ !tb[NL80211_ATTR_SMPS_MODE] &&
+ !tb[NL80211_ATTR_NSS]))
+ return;
+
+ ed.sta_opmode.smps_mode = SMPS_INVALID;
+ ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
+ ed.sta_opmode.rx_nss = 0xff;
+ ed.sta_opmode.addr = nla_data(tb[NL80211_ATTR_MAC]);
+
+ if (tb[NL80211_ATTR_SMPS_MODE]) {
+ smps_mode = nla_get_u8(tb[NL80211_ATTR_SMPS_MODE]);
+ switch (smps_mode) {
+ case NL80211_SMPS_OFF:
+ ed.sta_opmode.smps_mode = SMPS_OFF;
+ break;
+ case NL80211_SMPS_STATIC:
+ ed.sta_opmode.smps_mode = SMPS_STATIC;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ ed.sta_opmode.smps_mode = SMPS_DYNAMIC;
+ break;
+ default:
+ ed.sta_opmode.smps_mode = SMPS_INVALID;
+ break;
+ }
+ }
+
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+ max_bw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]);
+ switch (max_bw) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_20_NOHT;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_40;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_80;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_80P80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_160;
+ break;
+ default:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
+ break;
+
+ }
+ }
+
+ if (tb[NL80211_ATTR_NSS])
+ ed.sta_opmode.rx_nss = nla_get_u8(tb[NL80211_ATTR_NSS]);
+
+ wpa_supplicant_event(drv->ctx, EVENT_STATION_OPMODE_CHANGED, &ed);
+}
+
+
static void do_process_drv_event(struct i802_bss *bss, int cmd,
struct nlattr **tb)
{
@@ -2113,9 +2381,10 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New scan results available");
+ if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
+ drv->scan_state = SCAN_COMPLETED;
drv->scan_complete_events = 1;
if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
- drv->scan_state = SCAN_COMPLETED;
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
drv, drv->ctx);
drv->last_scan_cmd = 0;
@@ -2132,8 +2401,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
break;
case NL80211_CMD_SCAN_ABORTED:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
- if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
drv->scan_state = SCAN_ABORTED;
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
/*
* Need to indicate that scan results are available in
* order not to make wpa_supplicant stop its scanning.
@@ -2168,7 +2438,13 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
tb[NL80211_ATTR_REQ_IE],
tb[NL80211_ATTR_RESP_IE],
tb[NL80211_ATTR_TIMED_OUT],
- NULL, NULL, NULL, NULL, NULL);
+ tb[NL80211_ATTR_TIMEOUT_REASON],
+ NULL, NULL, NULL,
+ tb[NL80211_ATTR_FILS_KEK],
+ NULL,
+ tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
+ tb[NL80211_ATTR_PMK],
+ tb[NL80211_ATTR_PMKID]);
break;
case NL80211_CMD_CH_SWITCH_NOTIFY:
mlme_event_ch_switch(drv,
@@ -2200,6 +2476,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
nl80211_cqm_event(drv, tb);
break;
case NL80211_CMD_REG_CHANGE:
+ case NL80211_CMD_WIPHY_REG_CHANGE:
nl80211_reg_change_event(drv, tb);
break;
case NL80211_CMD_REG_BEACON_HINT:
@@ -2245,6 +2522,12 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
case NL80211_CMD_NEW_PEER_CANDIDATE:
nl80211_new_peer_candidate(drv, tb);
break;
+ case NL80211_CMD_PORT_AUTHORIZED:
+ nl80211_port_authorized(drv, tb);
+ break;
+ case NL80211_CMD_STA_OPMODE_CHANGED:
+ nl80211_sta_opmode_change_event(drv, tb);
+ break;
default:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
@@ -2259,10 +2542,11 @@ int process_global_event(struct nl_msg *msg, void *arg)
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct wpa_driver_nl80211_data *drv, *tmp;
- int ifidx = -1;
+ int ifidx = -1, wiphy_idx = -1, wiphy_idx_rx = -1;
struct i802_bss *bss;
u64 wdev_id = 0;
int wdev_id_set = 0;
+ int wiphy_idx_set = 0;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
@@ -2272,13 +2556,19 @@ int process_global_event(struct nl_msg *msg, void *arg)
else if (tb[NL80211_ATTR_WDEV]) {
wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
wdev_id_set = 1;
+ } else if (tb[NL80211_ATTR_WIPHY]) {
+ wiphy_idx_rx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+ wiphy_idx_set = 1;
}
dl_list_for_each_safe(drv, tmp, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
for (bss = drv->first_bss; bss; bss = bss->next) {
- if ((ifidx == -1 && !wdev_id_set) ||
+ if (wiphy_idx_set)
+ wiphy_idx = nl80211_get_wiphy_index(bss);
+ if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) ||
ifidx == bss->ifindex ||
+ (wiphy_idx_set && wiphy_idx == wiphy_idx_rx) ||
(wdev_id_set && bss->wdev_id_set &&
wdev_id == bss->wdev_id)) {
do_process_drv_event(bss, gnlh->cmd, tb);
@@ -2323,6 +2613,9 @@ int process_bss_event(struct nl_msg *msg, void *arg)
case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
nl80211_spurious_frame(bss, tb, 1);
break;
+ case NL80211_CMD_EXTERNAL_AUTH:
+ nl80211_external_auth(bss->drv, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c
index 9376d1143800..f25cd792464a 100644
--- a/src/drivers/driver_nl80211_monitor.c
+++ b/src/drivers/driver_nl80211_monitor.c
@@ -361,8 +361,17 @@ int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
*/
snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
} else {
+ int ret;
+
/* Non-P2P interface with AP functionality. */
- snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname);
+ ret = os_snprintf(buf, IFNAMSIZ, "mon.%s",
+ drv->first_bss->ifname);
+ if (ret >= (int) sizeof(buf))
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Monitor interface name has been truncated to %s",
+ buf);
+ else if (ret < 0)
+ return ret;
}
buf[IFNAMSIZ - 1] = '\0';
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index c115b6b31b7d..33a8d359848f 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -10,6 +10,7 @@
*/
#include "includes.h"
+#include <time.h>
#include <netlink/genl/genl.h>
#include "utils/common.h"
@@ -20,6 +21,14 @@
#include "driver_nl80211.h"
+#define MAX_NL80211_NOISE_FREQS 50
+
+struct nl80211_noise_info {
+ u32 freq[MAX_NL80211_NOISE_FREQS];
+ s8 noise[MAX_NL80211_NOISE_FREQS];
+ unsigned int count;
+};
+
static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -29,9 +38,10 @@ static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
};
- struct wpa_scan_results *scan_results = arg;
- struct wpa_scan_res *scan_res;
- size_t i;
+ struct nl80211_noise_info *info = arg;
+
+ if (info->count >= MAX_NL80211_NOISE_FREQS)
+ return NL_STOP;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
@@ -55,34 +65,81 @@ static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
return NL_SKIP;
- for (i = 0; i < scan_results->num; ++i) {
- scan_res = scan_results->res[i];
- if (!scan_res)
- continue;
- if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
- scan_res->freq)
- continue;
- if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
- continue;
- scan_res->noise = (s8)
- nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
- scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
- }
+ info->freq[info->count] =
+ nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+ info->noise[info->count] =
+ (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ info->count++;
return NL_SKIP;
}
static int nl80211_get_noise_for_scan_results(
- struct wpa_driver_nl80211_data *drv,
- struct wpa_scan_results *scan_res)
+ struct wpa_driver_nl80211_data *drv, struct nl80211_noise_info *info)
{
struct nl_msg *msg;
+ os_memset(info, 0, sizeof(*info));
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
- return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
- scan_res);
+ return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info);
+}
+
+
+static int nl80211_abort_scan(struct i802_bss *bss)
+{
+ int ret;
+ struct nl_msg *msg;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+ return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_abort_vendor_scan(struct wpa_driver_nl80211_data *drv,
+ u64 scan_cookie)
+{
+ struct nl_msg *msg;
+ struct nlattr *params;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Abort vendor scan with cookie 0x%llx",
+ (long long unsigned int) scan_cookie);
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, scan_cookie))
+ goto fail;
+
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Aborting vendor scan with cookie 0x%llx failed: ret=%d (%s)",
+ (long long unsigned int) scan_cookie, ret,
+ strerror(-ret));
+ goto fail;
+ }
+ return 0;
+fail:
+ nlmsg_free(msg);
+ return -1;
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
/**
@@ -98,7 +155,13 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
struct wpa_driver_nl80211_data *drv = eloop_ctx;
wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it");
- if (!wpa_driver_nl80211_abort_scan(drv->first_bss))
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (drv->vendor_scan_cookie &&
+ nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0)
+ return;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ if (!drv->vendor_scan_cookie &&
+ nl80211_abort_scan(drv->first_bss) == 0)
return;
wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
@@ -206,6 +269,34 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
}
}
+ if (params->duration) {
+ if (!(drv->capa.rrm_flags &
+ WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) ||
+ nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION,
+ params->duration))
+ goto fail;
+
+ if (params->duration_mandatory &&
+ nla_put_flag(msg,
+ NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY))
+ goto fail;
+ }
+
+ if (params->oce_scan) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME");
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP");
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_MIN_TX_RATE");
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION");
+ scan_flags |= NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME |
+ NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP |
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE |
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION;
+ }
+
if (scan_flags &&
nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
goto fail;
@@ -487,6 +578,44 @@ int wpa_driver_nl80211_sched_scan(void *priv,
nla_nest_end(msg, match_sets);
}
+ if (params->relative_rssi_set) {
+ struct nl80211_bss_select_rssi_adjust rssi_adjust;
+
+ os_memset(&rssi_adjust, 0, sizeof(rssi_adjust));
+ wpa_printf(MSG_DEBUG, "nl80211: Relative RSSI: %d",
+ params->relative_rssi);
+ if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+ params->relative_rssi))
+ goto fail;
+
+ if (params->relative_adjust_rssi) {
+ int pref_band_set = 1;
+
+ switch (params->relative_adjust_band) {
+ case WPA_SETBAND_5G:
+ rssi_adjust.band = NL80211_BAND_5GHZ;
+ break;
+ case WPA_SETBAND_2G:
+ rssi_adjust.band = NL80211_BAND_2GHZ;
+ break;
+ default:
+ pref_band_set = 0;
+ break;
+ }
+ rssi_adjust.delta = params->relative_adjust_rssi;
+
+ if (pref_band_set &&
+ nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+ sizeof(rssi_adjust), &rssi_adjust))
+ goto fail;
+ }
+ }
+
+ if (params->sched_scan_start_delay &&
+ nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY,
+ params->sched_scan_start_delay))
+ goto fail;
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
/* TODO: if we get an error here, we should fall back to normal scan */
@@ -562,7 +691,9 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
}
-int bss_info_handler(struct nl_msg *msg, void *arg)
+static struct wpa_scan_res *
+nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
@@ -579,50 +710,22 @@ int bss_info_handler(struct nl_msg *msg, void *arg)
[NL80211_BSS_STATUS] = { .type = NLA_U32 },
[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 },
};
- struct nl80211_bss_info_arg *_arg = arg;
- struct wpa_scan_results *res = _arg->res;
- struct wpa_scan_res **tmp;
struct wpa_scan_res *r;
const u8 *ie, *beacon_ie;
size_t ie_len, beacon_ie_len;
u8 *pos;
- size_t i;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_BSS])
- return NL_SKIP;
+ return NULL;
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
bss_policy))
- return NL_SKIP;
- if (bss[NL80211_BSS_STATUS]) {
- enum nl80211_bss_status status;
- status = nla_get_u32(bss[NL80211_BSS_STATUS]);
- if (status == NL80211_BSS_STATUS_ASSOCIATED &&
- bss[NL80211_BSS_FREQUENCY]) {
- _arg->assoc_freq =
- nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
- wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
- _arg->assoc_freq);
- }
- if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
- bss[NL80211_BSS_FREQUENCY]) {
- _arg->ibss_freq =
- nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
- wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
- _arg->ibss_freq);
- }
- if (status == NL80211_BSS_STATUS_ASSOCIATED &&
- bss[NL80211_BSS_BSSID]) {
- os_memcpy(_arg->assoc_bssid,
- nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
- wpa_printf(MSG_DEBUG, "nl80211: Associated with "
- MACSTR, MAC2STR(_arg->assoc_bssid));
- }
- }
- if (!res)
- return NL_SKIP;
+ return NULL;
if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@@ -638,13 +741,13 @@ int bss_info_handler(struct nl_msg *msg, void *arg)
beacon_ie_len = 0;
}
- if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
+ if (nl80211_scan_filtered(drv, ie ? ie : beacon_ie,
ie ? ie_len : beacon_ie_len))
- return NL_SKIP;
+ return NULL;
r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
if (r == NULL)
- return NL_SKIP;
+ return NULL;
if (bss[NL80211_BSS_BSSID])
os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
ETH_ALEN);
@@ -673,6 +776,23 @@ int bss_info_handler(struct nl_msg *msg, void *arg)
}
if (bss[NL80211_BSS_SEEN_MS_AGO])
r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+ if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
+ u64 boottime;
+ struct timespec ts;
+
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+ if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) {
+ /* Use more accurate boottime information to update the
+ * scan result age since the driver reports this and
+ * CLOCK_BOOTTIME is available. */
+ boottime = nla_get_u64(
+ bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
+ r->age = ((u64) ts.tv_sec * 1000000000 +
+ ts.tv_nsec - boottime) / 1000000;
+ }
+ }
r->ie_len = ie_len;
pos = (u8 *) (r + 1);
if (ie) {
@@ -695,40 +815,36 @@ int bss_info_handler(struct nl_msg *msg, void *arg)
}
}
- /*
- * cfg80211 maintains separate BSS table entries for APs if the same
- * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
- * not use frequency as a separate key in the BSS table, so filter out
- * duplicated entries. Prefer associated BSS entry in such a case in
- * order to get the correct frequency into the BSS table. Similarly,
- * prefer newer entries over older.
- */
- for (i = 0; i < res->num; i++) {
- const u8 *s1, *s2;
- if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
- continue;
+ if (bss[NL80211_BSS_PARENT_TSF] && bss[NL80211_BSS_PARENT_BSSID]) {
+ r->parent_tsf = nla_get_u64(bss[NL80211_BSS_PARENT_TSF]);
+ os_memcpy(r->tsf_bssid, nla_data(bss[NL80211_BSS_PARENT_BSSID]),
+ ETH_ALEN);
+ }
- s1 = get_ie((u8 *) (res->res[i] + 1),
- res->res[i]->ie_len, WLAN_EID_SSID);
- s2 = get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
- if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
- os_memcmp(s1, s2, 2 + s1[1]) != 0)
- continue;
+ return r;
+}
- /* Same BSSID,SSID was already included in scan results */
- wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
- "for " MACSTR, MAC2STR(r->bssid));
- if (((r->flags & WPA_SCAN_ASSOCIATED) &&
- !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) ||
- r->age < res->res[i]->age) {
- os_free(res->res[i]);
- res->res[i] = r;
- } else
- os_free(r);
+struct nl80211_bss_info_arg {
+ struct wpa_driver_nl80211_data *drv;
+ struct wpa_scan_results *res;
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_bss_info_arg *_arg = arg;
+ struct wpa_scan_results *res = _arg->res;
+ struct wpa_scan_res **tmp;
+ struct wpa_scan_res *r;
+
+ r = nl80211_parse_bss_info(_arg->drv, msg);
+ if (!r)
return NL_SKIP;
- }
+ if (!res) {
+ os_free(r);
+ return NL_SKIP;
+ }
tmp = os_realloc_array(res->res, res->num + 1,
sizeof(struct wpa_scan_res *));
if (tmp == NULL) {
@@ -750,7 +866,32 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
"mismatch (" MACSTR ")", MAC2STR(addr));
wpa_driver_nl80211_mlme(drv, addr,
NL80211_CMD_DEAUTHENTICATE,
- WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
+ WLAN_REASON_PREV_AUTH_NOT_VALID, 1,
+ NULL);
+ }
+}
+
+
+static void nl80211_check_bss_status(struct wpa_driver_nl80211_data *drv,
+ struct wpa_scan_res *r)
+{
+ if (!(r->flags & WPA_SCAN_ASSOCIATED))
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Scan results indicate BSS status with "
+ MACSTR " as associated", MAC2STR(r->bssid));
+ if (is_sta_interface(drv->nlmode) && !drv->associated) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Local state (not associated) does not match with BSS state");
+ clear_state_mismatch(drv, r->bssid);
+ } else if (is_sta_interface(drv->nlmode) &&
+ os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Local state (associated with " MACSTR
+ ") does not match with BSS state",
+ MAC2STR(drv->bssid));
+ clear_state_mismatch(drv, r->bssid);
+ clear_state_mismatch(drv, drv->bssid);
}
}
@@ -760,31 +901,22 @@ static void wpa_driver_nl80211_check_bss_status(
{
size_t i;
- for (i = 0; i < res->num; i++) {
- struct wpa_scan_res *r = res->res[i];
-
- if (r->flags & WPA_SCAN_ASSOCIATED) {
- wpa_printf(MSG_DEBUG, "nl80211: Scan results "
- "indicate BSS status with " MACSTR
- " as associated",
- MAC2STR(r->bssid));
- if (is_sta_interface(drv->nlmode) &&
- !drv->associated) {
- wpa_printf(MSG_DEBUG, "nl80211: Local state "
- "(not associated) does not match "
- "with BSS state");
- clear_state_mismatch(drv, r->bssid);
- } else if (is_sta_interface(drv->nlmode) &&
- os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
- 0) {
- wpa_printf(MSG_DEBUG, "nl80211: Local state "
- "(associated with " MACSTR ") does "
- "not match with BSS state",
- MAC2STR(drv->bssid));
- clear_state_mismatch(drv, r->bssid);
- clear_state_mismatch(drv, drv->bssid);
- }
- }
+ for (i = 0; i < res->num; i++)
+ nl80211_check_bss_status(drv, res->res[i]);
+}
+
+
+static void nl80211_update_scan_res_noise(struct wpa_scan_res *res,
+ struct nl80211_noise_info *info)
+{
+ unsigned int i;
+
+ for (i = 0; res && i < info->count; i++) {
+ if ((int) info->freq[i] != res->freq ||
+ !(res->flags & WPA_SCAN_NOISE_INVALID))
+ continue;
+ res->noise = info->noise[i];
+ res->flags &= ~WPA_SCAN_NOISE_INVALID;
}
}
@@ -810,9 +942,17 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
arg.res = res;
ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
if (ret == 0) {
+ struct nl80211_noise_info info;
+
wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
"BSSes)", (unsigned long) res->num);
- nl80211_get_noise_for_scan_results(drv, res);
+ if (nl80211_get_noise_for_scan_results(drv, &info) == 0) {
+ size_t i;
+
+ for (i = 0; i < res->num; ++i)
+ nl80211_update_scan_res_noise(res->res[i],
+ &info);
+ }
return res;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
@@ -840,45 +980,57 @@ struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
}
-void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+struct nl80211_dump_scan_ctx {
+ struct wpa_driver_nl80211_data *drv;
+ int idx;
+};
+
+static int nl80211_dump_scan_handler(struct nl_msg *msg, void *arg)
{
- struct wpa_scan_results *res;
- size_t i;
+ struct nl80211_dump_scan_ctx *ctx = arg;
+ struct wpa_scan_res *r;
- res = nl80211_get_scan_results(drv);
- if (res == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
- return;
- }
+ r = nl80211_parse_bss_info(ctx->drv, msg);
+ if (!r)
+ return NL_SKIP;
+ wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s",
+ ctx->idx, MAC2STR(r->bssid), r->freq,
+ r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+ ctx->idx++;
+ os_free(r);
+ return NL_SKIP;
+}
- wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
- for (i = 0; i < res->num; i++) {
- struct wpa_scan_res *r = res->res[i];
- wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s",
- (int) i, (int) res->num, MAC2STR(r->bssid),
- r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
- }
- wpa_scan_results_free(res);
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ struct nl80211_dump_scan_ctx ctx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+ ctx.drv = drv;
+ ctx.idx = 0;
+ msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+ if (msg)
+ send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx);
}
-int wpa_driver_nl80211_abort_scan(void *priv)
+int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie)
{
struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
struct wpa_driver_nl80211_data *drv = bss->drv;
- int ret;
- struct nl_msg *msg;
-
- wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
- msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
- if (ret) {
- wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
- ret, strerror(-ret));
- }
- return ret;
+ /*
+ * If scan_cookie is zero, a normal scan through kernel (cfg80211)
+ * was triggered, hence abort the cfg80211 scan instead of the vendor
+ * scan.
+ */
+ if (drv->scan_vendor_cmd_avail && scan_cookie)
+ return nl80211_abort_vendor_scan(drv, scan_cookie);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ return nl80211_abort_scan(bss);
}
@@ -1015,7 +1167,7 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
}
if (scan_flags &&
- nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, scan_flags))
goto fail;
if (params->p2p_probe) {
@@ -1043,6 +1195,14 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
goto fail;
}
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
+ MACSTR, MAC2STR(params->bssid));
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_BSSID, ETH_ALEN,
+ params->bssid))
+ goto fail;
+ }
+
nla_nest_end(msg, attr);
ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
@@ -1056,6 +1216,8 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
drv->vendor_scan_cookie = cookie;
drv->scan_state = SCAN_REQUESTED;
+ /* Pass the cookie to the caller to help distinguish the scans. */
+ params->scan_cookie = cookie;
wpa_printf(MSG_DEBUG,
"nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 43d41937d474..a3f0837e1569 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -97,15 +97,31 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
return 0;
}
-
+
static int wpa_driver_privsep_scan(void *priv,
struct wpa_driver_scan_params *params)
{
struct wpa_driver_privsep_data *drv = priv;
- const u8 *ssid = params->ssids[0].ssid;
- size_t ssid_len = params->ssids[0].ssid_len;
+ struct privsep_cmd_scan scan;
+ size_t i;
+
wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
- return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len,
+ os_memset(&scan, 0, sizeof(scan));
+ scan.num_ssids = params->num_ssids;
+ for (i = 0; i < params->num_ssids; i++) {
+ if (!params->ssids[i].ssid)
+ continue;
+ scan.ssid_lens[i] = params->ssids[i].ssid_len;
+ os_memcpy(scan.ssids[i], params->ssids[i].ssid,
+ scan.ssid_lens[i]);
+ }
+
+ for (i = 0; i < PRIVSEP_MAX_SCAN_FREQS &&
+ params->freqs && params->freqs[i]; i++)
+ scan.freqs[i] = params->freqs[i];
+ scan.num_freqs = i;
+
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, &scan, sizeof(scan),
NULL, NULL);
}
@@ -168,12 +184,15 @@ wpa_driver_privsep_get_scan_results2(void *priv)
if (len < 0 || len > 10000 || len > end - pos)
break;
- r = os_malloc(len);
+ r = os_memdup(pos, len);
if (r == NULL)
break;
- os_memcpy(r, pos, len);
pos += len;
- if (sizeof(*r) + r->ie_len > (size_t) len) {
+ if (sizeof(*r) + r->ie_len + r->beacon_ie_len > (size_t) len) {
+ wpa_printf(MSG_ERROR,
+ "privsep: Invalid scan result len (%d + %d + %d > %d)",
+ (int) sizeof(*r), (int) r->ie_len,
+ (int) r->beacon_ie_len, len);
os_free(r);
break;
}
@@ -234,7 +253,7 @@ static int wpa_driver_privsep_authenticate(
__func__, priv, params->freq, MAC2STR(params->bssid),
params->auth_alg, params->local_state_change, params->p2p);
- buflen = sizeof(*data) + params->ie_len + params->sae_data_len;
+ buflen = sizeof(*data) + params->ie_len + params->auth_data_len;
data = os_zalloc(buflen);
if (data == NULL)
return -1;
@@ -259,8 +278,8 @@ static int wpa_driver_privsep_authenticate(
os_memcpy(pos, params->ie, params->ie_len);
pos += params->ie_len;
}
- if (params->sae_data_len)
- os_memcpy(pos, params->sae_data, params->sae_data_len);
+ if (params->auth_data_len)
+ os_memcpy(pos, params->auth_data, params->auth_data_len);
res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
NULL, NULL);
@@ -464,19 +483,6 @@ static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf,
}
-static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
-{
- union wpa_event_data data;
-
- if (len != ETH_ALEN)
- return;
-
- os_memset(&data, 0, sizeof(data));
- os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
- wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
-}
-
-
static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
size_t len)
{
@@ -570,10 +576,6 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
event_len);
break;
- case PRIVSEP_EVENT_STKSTART:
- wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
- event_len);
- break;
case PRIVSEP_EVENT_FT_RESPONSE:
wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
event_len);
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 791cd5d435d0..20abaab4cd84 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -290,15 +290,6 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
done:
os_free(resp_ies);
os_free(req_ies);
-#ifdef CONFIG_PEERKEY
- } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) {
- if (hwaddr_aton(custom + 17, data.stkstart.peer)) {
- wpa_printf(MSG_DEBUG, "WEXT: unrecognized "
- "STKSTART.request '%s'", custom + 17);
- return;
- }
- wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
-#endif /* CONFIG_PEERKEY */
}
}
@@ -362,12 +353,11 @@ static int wpa_driver_wext_event_wireless_assocreqie(
wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
len);
os_free(drv->assoc_req_ies);
- drv->assoc_req_ies = os_malloc(len);
+ drv->assoc_req_ies = os_memdup(ev, len);
if (drv->assoc_req_ies == NULL) {
drv->assoc_req_ies_len = 0;
return -1;
}
- os_memcpy(drv->assoc_req_ies, ev, len);
drv->assoc_req_ies_len = len;
return 0;
@@ -383,12 +373,11 @@ static int wpa_driver_wext_event_wireless_assocrespie(
wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
len);
os_free(drv->assoc_resp_ies);
- drv->assoc_resp_ies = os_malloc(len);
+ drv->assoc_resp_ies = os_memdup(ev, len);
if (drv->assoc_resp_ies == NULL) {
drv->assoc_resp_ies_len = 0;
return -1;
}
- os_memcpy(drv->assoc_resp_ies, ev, len);
drv->assoc_resp_ies_len = len;
return 0;
@@ -472,7 +461,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
drv->assoc_resp_ies = NULL;
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
NULL);
-
+
} else {
wpa_driver_wext_event_assoc_ies(drv);
wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
@@ -933,7 +922,7 @@ static int wext_add_hostap(struct wpa_driver_wext_data *drv)
static void wext_check_hostap(struct wpa_driver_wext_data *drv)
{
- char buf[200], *pos;
+ char path[200], buf[200], *pos;
ssize_t res;
/*
@@ -948,9 +937,9 @@ static void wext_check_hostap(struct wpa_driver_wext_data *drv)
*/
/* First, try to see if driver information is available from sysfs */
- snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/driver",
+ snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver",
drv->ifname);
- res = readlink(buf, buf, sizeof(buf) - 1);
+ res = readlink(path, buf, sizeof(buf) - 1);
if (res > 0) {
buf[res] = '\0';
pos = strrchr(buf, '/');
@@ -1042,6 +1031,7 @@ void wpa_driver_wext_deinit(void *priv)
wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0);
eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+ eloop_cancel_timeout(wpa_driver_wext_send_rfkill, drv, drv->ctx);
/*
* Clear possibly configured driver parameters in order to make it
@@ -2352,19 +2342,21 @@ static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
}
-static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid,
- const u8 *pmkid)
+static int wpa_driver_wext_add_pmkid(void *priv,
+ struct wpa_pmkid_params *params)
{
struct wpa_driver_wext_data *drv = priv;
- return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid);
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, params->bssid,
+ params->pmkid);
}
-static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid,
- const u8 *pmkid)
+static int wpa_driver_wext_remove_pmkid(void *priv,
+ struct wpa_pmkid_params *params)
{
struct wpa_driver_wext_data *drv = priv;
- return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid);
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, params->bssid,
+ params->pmkid);
}
@@ -2436,8 +2428,8 @@ static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si)
struct iwreq iwr;
os_memset(si, 0, sizeof(*si));
- si->current_signal = -9999;
- si->current_noise = 9999;
+ si->current_signal = -WPA_INVALID_NOISE;
+ si->current_noise = WPA_INVALID_NOISE;
si->chanwidth = CHAN_WIDTH_UNKNOWN;
os_memset(&iwr, 0, sizeof(iwr));
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
index 422a22064ebe..c7537b7c35eb 100644
--- a/src/drivers/driver_wired.c
+++ b/src/drivers/driver_wired.c
@@ -12,6 +12,7 @@
#include "common.h"
#include "eloop.h"
#include "driver.h"
+#include "driver_wired_common.h"
#include <sys/ioctl.h>
#undef IFNAMSIZ
@@ -42,20 +43,12 @@ struct ieee8023_hdr {
#pragma pack(pop)
#endif /* _MSC_VER */
-static const u8 pae_group_addr[ETH_ALEN] =
-{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
-
struct wpa_driver_wired_data {
- char ifname[IFNAMSIZ + 1];
- void *ctx;
+ struct driver_wired_common_data common;
- int sock; /* raw packet socket for driver access */
int dhcp_sock; /* socket for dhcp packets */
int use_pae_group_addr;
-
- int pf_sock;
- int membership, multi, iff_allmulti, iff_up;
};
@@ -83,34 +76,6 @@ struct dhcp_message {
};
-static int wired_multicast_membership(int sock, int ifindex,
- const u8 *addr, int add)
-{
-#ifdef __linux__
- struct packet_mreq mreq;
-
- if (sock < 0)
- return -1;
-
- os_memset(&mreq, 0, sizeof(mreq));
- mreq.mr_ifindex = ifindex;
- mreq.mr_type = PACKET_MR_MULTICAST;
- mreq.mr_alen = ETH_ALEN;
- os_memcpy(mreq.mr_address, addr, ETH_ALEN);
-
- if (setsockopt(sock, SOL_PACKET,
- add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
- &mreq, sizeof(mreq)) < 0) {
- wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
- return -1;
- }
- return 0;
-#else /* __linux__ */
- return -1;
-#endif /* __linux__ */
-}
-
-
#ifdef __linux__
static void handle_data(void *ctx, unsigned char *buf, size_t len)
{
@@ -131,16 +96,16 @@ static void handle_data(void *ctx, unsigned char *buf, size_t len)
hdr = (struct ieee8023_hdr *) buf;
switch (ntohs(hdr->ethertype)) {
- case ETH_P_PAE:
- wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
- sa = hdr->src;
- os_memset(&event, 0, sizeof(event));
- event.new_sta.addr = sa;
- wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
-
- pos = (u8 *) (hdr + 1);
- left = len - sizeof(*hdr);
- drv_event_eapol_rx(ctx, sa, pos, left);
+ case ETH_P_PAE:
+ wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+ sa = hdr->src;
+ os_memset(&event, 0, sizeof(event));
+ event.new_sta.addr = sa;
+ wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+ pos = (u8 *) (hdr + 1);
+ left = len - sizeof(*hdr);
+ drv_event_eapol_rx(ctx, sa, pos, left);
break;
default:
@@ -208,21 +173,22 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
struct sockaddr_in addr2;
int n = 1;
- drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
- if (drv->sock < 0) {
+ drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+ if (drv->common.sock < 0) {
wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
strerror(errno));
return -1;
}
- if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) {
+ if (eloop_register_read_sock(drv->common.sock, handle_read,
+ drv->common.ctx, NULL)) {
wpa_printf(MSG_INFO, "Could not register read socket");
return -1;
}
os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
- if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+ os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
strerror(errno));
return -1;
@@ -234,13 +200,14 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
addr.sll_ifindex);
- if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ {
wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
return -1;
}
/* filter multicast address */
- if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex,
+ if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
pae_group_addr, 1) < 0) {
wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
"membership");
@@ -248,8 +215,8 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
}
os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
- if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
+ os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
strerror(errno));
return -1;
@@ -269,8 +236,8 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
return -1;
}
- if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx,
- NULL)) {
+ if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp,
+ drv->common.ctx, NULL)) {
wpa_printf(MSG_INFO, "Could not register read socket");
return -1;
}
@@ -294,7 +261,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
}
os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ);
+ os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->common.ifname, IFNAMSIZ);
if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
(char *) &ifr, sizeof(ifr)) < 0) {
wpa_printf(MSG_ERROR,
@@ -343,7 +310,7 @@ static int wired_send_eapol(void *priv, const u8 *addr,
pos = (u8 *) (hdr + 1);
os_memcpy(pos, data, data_len);
- res = send(drv->sock, (u8 *) hdr, len, 0);
+ res = send(drv->common.sock, (u8 *) hdr, len, 0);
os_free(hdr);
if (res < 0) {
@@ -368,8 +335,9 @@ static void * wired_driver_hapd_init(struct hostapd_data *hapd,
return NULL;
}
- drv->ctx = hapd;
- os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+ drv->common.ctx = hapd;
+ os_strlcpy(drv->common.ifname, params->ifname,
+ sizeof(drv->common.ifname));
drv->use_pae_group_addr = params->use_pae_group_addr;
if (wired_init_sockets(drv, params->own_addr)) {
@@ -385,9 +353,9 @@ static void wired_driver_hapd_deinit(void *priv)
{
struct wpa_driver_wired_data *drv = priv;
- if (drv->sock >= 0) {
- eloop_unregister_read_sock(drv->sock);
- close(drv->sock);
+ if (drv->common.sock >= 0) {
+ eloop_unregister_read_sock(drv->common.sock);
+ close(drv->common.sock);
}
if (drv->dhcp_sock >= 0) {
@@ -399,227 +367,18 @@ static void wired_driver_hapd_deinit(void *priv)
}
-static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid)
-{
- ssid[0] = 0;
- return 0;
-}
-
-
-static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
-{
- /* Report PAE group address as the "BSSID" for wired connection. */
- os_memcpy(bssid, pae_group_addr, ETH_ALEN);
- return 0;
-}
-
-
-static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
-{
- os_memset(capa, 0, sizeof(*capa));
- capa->flags = WPA_DRIVER_FLAGS_WIRED;
- return 0;
-}
-
-
-static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags)
-{
- struct ifreq ifr;
- int s;
-
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
- return -1;
- }
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
- strerror(errno));
- close(s);
- return -1;
- }
- close(s);
- *flags = ifr.ifr_flags & 0xffff;
- return 0;
-}
-
-
-static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
-{
- struct ifreq ifr;
- int s;
-
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
- return -1;
- }
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- ifr.ifr_flags = flags & 0xffff;
- if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
- strerror(errno));
- close(s);
- return -1;
- }
- close(s);
- return 0;
-}
-
-
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
-static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status)
-{
- struct ifmediareq ifmr;
- int s;
-
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
- return -1;
- }
-
- os_memset(&ifmr, 0, sizeof(ifmr));
- os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
- if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
- strerror(errno));
- close(s);
- return -1;
- }
- close(s);
- *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
- (IFM_ACTIVE | IFM_AVALID);
-
- return 0;
-}
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
-
-
-static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
-{
- struct ifreq ifr;
- int s;
-
-#ifdef __sun__
- return -1;
-#endif /* __sun__ */
-
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
- return -1;
- }
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-#ifdef __linux__
- ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
- os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
-#endif /* __linux__ */
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
- {
- struct sockaddr_dl *dlp;
- dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
- dlp->sdl_len = sizeof(struct sockaddr_dl);
- dlp->sdl_family = AF_LINK;
- dlp->sdl_index = 0;
- dlp->sdl_nlen = 0;
- dlp->sdl_alen = ETH_ALEN;
- dlp->sdl_slen = 0;
- os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
- }
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
-#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
- {
- struct sockaddr *sap;
- sap = (struct sockaddr *) &ifr.ifr_addr;
- sap->sa_len = sizeof(struct sockaddr);
- sap->sa_family = AF_UNSPEC;
- os_memcpy(sap->sa_data, addr, ETH_ALEN);
- }
-#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
-
- if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
- strerror(errno));
- close(s);
- return -1;
- }
- close(s);
- return 0;
-}
-
-
static void * wpa_driver_wired_init(void *ctx, const char *ifname)
{
struct wpa_driver_wired_data *drv;
- int flags;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
- os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
- drv->ctx = ctx;
-
-#ifdef __linux__
- drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
- if (drv->pf_sock < 0)
- wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
-#else /* __linux__ */
- drv->pf_sock = -1;
-#endif /* __linux__ */
- if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 &&
- !(flags & IFF_UP) &&
- wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) {
- drv->iff_up = 1;
- }
-
- if (wired_multicast_membership(drv->pf_sock,
- if_nametoindex(drv->ifname),
- pae_group_addr, 1) == 0) {
- wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
- "packet socket", __func__);
- drv->membership = 1;
- } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
- wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
- "SIOCADDMULTI", __func__);
- drv->multi = 1;
- } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) {
- wpa_printf(MSG_INFO, "%s: Could not get interface "
- "flags", __func__);
- os_free(drv);
- return NULL;
- } else if (flags & IFF_ALLMULTI) {
- wpa_printf(MSG_DEBUG, "%s: Interface is already configured "
- "for multicast", __func__);
- } else if (wpa_driver_wired_set_ifflags(ifname,
- flags | IFF_ALLMULTI) < 0) {
- wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
- __func__);
+ if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
os_free(drv);
return NULL;
- } else {
- wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode",
- __func__);
- drv->iff_allmulti = 1;
}
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
- {
- int status;
- wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
- __func__);
- while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 &&
- status == 0)
- sleep(1);
- }
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
return drv;
}
@@ -628,41 +387,8 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname)
static void wpa_driver_wired_deinit(void *priv)
{
struct wpa_driver_wired_data *drv = priv;
- int flags;
-
- if (drv->membership &&
- wired_multicast_membership(drv->pf_sock,
- if_nametoindex(drv->ifname),
- pae_group_addr, 0) < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
- "group (PACKET)", __func__);
- }
-
- if (drv->multi &&
- wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
- "group (SIOCDELMULTI)", __func__);
- }
-
- if (drv->iff_allmulti &&
- (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
- wpa_driver_wired_set_ifflags(drv->ifname,
- flags & ~IFF_ALLMULTI) < 0)) {
- wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
- __func__);
- }
-
- if (drv->iff_up &&
- wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
- (flags & IFF_UP) &&
- wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
- __func__);
- }
-
- if (drv->pf_sock != -1)
- close(drv->pf_sock);
+ driver_wired_deinit_common(&drv->common);
os_free(drv);
}
@@ -673,9 +399,9 @@ const struct wpa_driver_ops wpa_driver_wired_ops = {
.hapd_init = wired_driver_hapd_init,
.hapd_deinit = wired_driver_hapd_deinit,
.hapd_send_eapol = wired_send_eapol,
- .get_ssid = wpa_driver_wired_get_ssid,
- .get_bssid = wpa_driver_wired_get_bssid,
- .get_capa = wpa_driver_wired_get_capa,
+ .get_ssid = driver_wired_get_ssid,
+ .get_bssid = driver_wired_get_bssid,
+ .get_capa = driver_wired_get_capa,
.init = wpa_driver_wired_init,
.deinit = wpa_driver_wired_deinit,
};
diff --git a/src/drivers/driver_wired_common.c b/src/drivers/driver_wired_common.c
new file mode 100644
index 000000000000..a860b1c7db35
--- /dev/null
+++ b/src/drivers/driver_wired_common.c
@@ -0,0 +1,322 @@
+/*
+ * Common functions for Wired Ethernet driver interfaces
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+
+static int driver_wired_get_ifflags(const char *ifname, int *flags)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ *flags = ifr.ifr_flags & 0xffff;
+ return 0;
+}
+
+
+static int driver_wired_set_ifflags(const char *ifname, int flags)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ ifr.ifr_flags = flags & 0xffff;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+static int driver_wired_multi(const char *ifname, const u8 *addr, int add)
+{
+ struct ifreq ifr;
+ int s;
+
+#ifdef __sun__
+ return -1;
+#endif /* __sun__ */
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+#ifdef __linux__
+ ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
+ os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ {
+ struct sockaddr_dl *dlp;
+
+ dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+ dlp->sdl_len = sizeof(struct sockaddr_dl);
+ dlp->sdl_family = AF_LINK;
+ dlp->sdl_index = 0;
+ dlp->sdl_nlen = 0;
+ dlp->sdl_alen = ETH_ALEN;
+ dlp->sdl_slen = 0;
+ os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+ }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+ {
+ struct sockaddr *sap;
+
+ sap = (struct sockaddr *) &ifr.ifr_addr;
+ sap->sa_len = sizeof(struct sockaddr);
+ sap->sa_family = AF_UNSPEC;
+ os_memcpy(sap->sa_data, addr, ETH_ALEN);
+ }
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
+
+ if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add)
+{
+#ifdef __linux__
+ struct packet_mreq mreq;
+
+ if (sock < 0)
+ return -1;
+
+ os_memset(&mreq, 0, sizeof(mreq));
+ mreq.mr_ifindex = ifindex;
+ mreq.mr_type = PACKET_MR_MULTICAST;
+ mreq.mr_alen = ETH_ALEN;
+ os_memcpy(mreq.mr_address, addr, ETH_ALEN);
+
+ if (setsockopt(sock, SOL_PACKET,
+ add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+#else /* __linux__ */
+ return -1;
+#endif /* __linux__ */
+}
+
+
+int driver_wired_get_ssid(void *priv, u8 *ssid)
+{
+ ssid[0] = 0;
+ return 0;
+}
+
+
+int driver_wired_get_bssid(void *priv, u8 *bssid)
+{
+ /* Report PAE group address as the "BSSID" for wired connection. */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+ capa->flags = WPA_DRIVER_FLAGS_WIRED;
+ return 0;
+}
+
+
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int driver_wired_get_ifstatus(const char *ifname, int *status)
+{
+ struct ifmediareq ifmr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifmr, 0, sizeof(ifmr));
+ os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
+ (IFM_ACTIVE | IFM_AVALID);
+
+ return 0;
+}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+
+int driver_wired_init_common(struct driver_wired_common_data *common,
+ const char *ifname, void *ctx)
+{
+ int flags;
+
+ os_strlcpy(common->ifname, ifname, sizeof(common->ifname));
+ common->ctx = ctx;
+
+#ifdef __linux__
+ common->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (common->pf_sock < 0)
+ wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
+#else /* __linux__ */
+ common->pf_sock = -1;
+#endif /* __linux__ */
+
+ if (driver_wired_get_ifflags(ifname, &flags) == 0 &&
+ !(flags & IFF_UP) &&
+ driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0)
+ common->iff_up = 1;
+
+ if (wired_multicast_membership(common->pf_sock,
+ if_nametoindex(common->ifname),
+ pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Added multicast membership with packet socket",
+ __func__);
+ common->membership = 1;
+ } else if (driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Added multicast membership with SIOCADDMULTI",
+ __func__);
+ common->multi = 1;
+ } else if (driver_wired_get_ifflags(ifname, &flags) < 0) {
+ wpa_printf(MSG_INFO, "%s: Could not get interface flags",
+ __func__);
+ return -1;
+ } else if (flags & IFF_ALLMULTI) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Interface is already configured for multicast",
+ __func__);
+ } else if (driver_wired_set_ifflags(ifname,
+ flags | IFF_ALLMULTI) < 0) {
+ wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", __func__);
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
+ common->iff_allmulti = 1;
+ }
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ {
+ int status;
+
+ wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+ __func__);
+ while (driver_wired_get_ifstatus(ifname, &status) == 0 &&
+ status == 0)
+ sleep(1);
+ }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+ return 0;
+}
+
+
+void driver_wired_deinit_common(struct driver_wired_common_data *common)
+{
+ int flags;
+
+ if (common->membership &&
+ wired_multicast_membership(common->pf_sock,
+ if_nametoindex(common->ifname),
+ pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to remove PAE multicast group (PACKET)",
+ __func__);
+ }
+
+ if (common->multi &&
+ driver_wired_multi(common->ifname, pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
+ __func__);
+ }
+
+ if (common->iff_allmulti &&
+ (driver_wired_get_ifflags(common->ifname, &flags) < 0 ||
+ driver_wired_set_ifflags(common->ifname,
+ flags & ~IFF_ALLMULTI) < 0)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+ __func__);
+ }
+
+ if (common->iff_up &&
+ driver_wired_get_ifflags(common->ifname, &flags) == 0 &&
+ (flags & IFF_UP) &&
+ driver_wired_set_ifflags(common->ifname, flags & ~IFF_UP) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+ __func__);
+ }
+
+ if (common->pf_sock != -1)
+ close(common->pf_sock);
+}
diff --git a/src/drivers/driver_wired_common.h b/src/drivers/driver_wired_common.h
new file mode 100644
index 000000000000..2bb0710bb275
--- /dev/null
+++ b/src/drivers/driver_wired_common.h
@@ -0,0 +1,34 @@
+/*
+ * Common definitions for Wired Ethernet driver interfaces
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_WIRED_COMMON_H
+#define DRIVER_WIRED_COMMON_H
+
+struct driver_wired_common_data {
+ char ifname[IFNAMSIZ + 1];
+ void *ctx;
+
+ int sock; /* raw packet socket for driver access */
+ int pf_sock;
+ int membership, multi, iff_allmulti, iff_up;
+};
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add);
+int driver_wired_get_ssid(void *priv, u8 *ssid);
+int driver_wired_get_bssid(void *priv, u8 *bssid);
+int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa);
+
+int driver_wired_init_common(struct driver_wired_common_data *common,
+ const char *ifname, void *ctx);
+void driver_wired_deinit_common(struct driver_wired_common_data *common);
+
+#endif /* DRIVER_WIRED_COMMON_H */
diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
index 00773a7113f6..e95df6ddb20a 100644
--- a/src/drivers/drivers.c
+++ b/src/drivers/drivers.c
@@ -34,6 +34,9 @@ const struct wpa_driver_ops *const wpa_drivers[] =
#ifdef CONFIG_DRIVER_WIRED
&wpa_driver_wired_ops,
#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
+ &wpa_driver_macsec_linux_ops,
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
#ifdef CONFIG_DRIVER_MACSEC_QCA
&wpa_driver_macsec_qca_ops,
#endif /* CONFIG_DRIVER_MACSEC_QCA */
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index c6d3f8181808..1496b47d0ad5 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -15,11 +15,24 @@ DRV_AP_LIBS =
ifdef CONFIG_DRIVER_WIRED
DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
DRV_OBJS += ../src/drivers/driver_wired.o
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_LINUX
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
+DRV_OBJS += ../src/drivers/driver_macsec_linux.o
+NEED_DRV_WIRED_COMMON=1
+CONFIG_LIBNL3_ROUTE=y
endif
ifdef CONFIG_DRIVER_MACSEC_QCA
DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
DRV_OBJS += ../src/drivers/driver_macsec_qca.o
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef NEED_DRV_WIRED_COMMON
+DRV_OBJS += ../src/drivers/driver_wired_common.o
endif
ifdef CONFIG_DRIVER_NL80211
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index c6fe4c20c561..cd25133af808 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -15,6 +15,18 @@ DRV_AP_LIBS =
ifdef CONFIG_DRIVER_WIRED
DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
DRV_OBJS += src/drivers/driver_wired.c
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_LINUX
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
+DRV_OBJS += src/drivers/driver_macsec_linux.c
+NEED_DRV_WIRED_COMMON=1
+CONFIG_LIBNL3_ROUTE=y
+endif
+
+ifdef NEED_DRV_WIRED_COMMON
+DRV_OBJS += src/drivers/driver_wired_common.c
endif
ifdef CONFIG_DRIVER_NL80211
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 220694151434..1766a12b231c 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -10,7 +10,8 @@
* Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
- * Copyright 2015 Intel Deutschland GmbH
+ * Copyright 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -48,6 +49,7 @@
#define NL80211_MULTICAST_GROUP_REG "regulatory"
#define NL80211_MULTICAST_GROUP_MLME "mlme"
#define NL80211_MULTICAST_GROUP_VENDOR "vendor"
+#define NL80211_MULTICAST_GROUP_NAN "nan"
#define NL80211_MULTICAST_GROUP_TESTMODE "testmode"
/**
@@ -172,6 +174,67 @@
*/
/**
+ * DOC: WPA/WPA2 EAPOL handshake offload
+ *
+ * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK flag drivers
+ * can indicate they support offloading EAPOL handshakes for WPA/WPA2
+ * preshared key authentication. In %NL80211_CMD_CONNECT the preshared
+ * key should be specified using %NL80211_ATTR_PMK. Drivers supporting
+ * this offload may reject the %NL80211_CMD_CONNECT when no preshared
+ * key material is provided, for example when that driver does not
+ * support setting the temporal keys through %CMD_NEW_KEY.
+ *
+ * Similarly @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X flag can be
+ * set by drivers indicating offload support of the PTK/GTK EAPOL
+ * handshakes during 802.1X authentication. In order to use the offload
+ * the %NL80211_CMD_CONNECT should have %NL80211_ATTR_WANT_1X_4WAY_HS
+ * attribute flag. Drivers supporting this offload may reject the
+ * %NL80211_CMD_CONNECT when the attribute flag is not present.
+ *
+ * For 802.1X the PMK or PMK-R0 are set by providing %NL80211_ATTR_PMK
+ * using %NL80211_CMD_SET_PMK. For offloaded FT support also
+ * %NL80211_ATTR_PMKR0_NAME must be provided.
+ */
+
+/**
+ * DOC: FILS shared key authentication offload
+ *
+ * FILS shared key authentication offload can be advertized by drivers by
+ * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+ * FILS shared key authentication offload should be able to construct the
+ * authentication and association frames for FILS shared key authentication and
+ * eventually do a key derivation as per IEEE 802.11ai. The below additional
+ * parameters should be given to driver in %NL80211_CMD_CONNECT and/or in
+ * %NL80211_CMD_UPDATE_CONNECT_PARAMS.
+ * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai
+ * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai
+ * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message
+ * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK
+ * rIK should be used to generate an authentication tag on the ERP message and
+ * rMSK should be used to derive a PMKSA.
+ * rIK, rMSK should be generated and keyname_nai, sequence number should be used
+ * as specified in IETF RFC 6696.
+ *
+ * When FILS shared key authentication is completed, driver needs to provide the
+ * below additional parameters to userspace, which can be either after setting
+ * up a connection or after roaming.
+ * %NL80211_ATTR_FILS_KEK - used for key renewal
+ * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges
+ * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated
+ * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace
+ * The PMKSA can be maintained in userspace persistently so that it can be used
+ * later after reboots or wifi turn off/on also.
+ *
+ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
+ * capable AP supporting PMK caching. It specifies the scope within which the
+ * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+ * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+ * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with
+ * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to
+ * use in a FILS shared key connection with PMKSA caching.
+ */
+
+/**
* enum nl80211_commands - supported nl80211 commands
*
* @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -322,7 +385,7 @@
* @NL80211_CMD_GET_SCAN: get scan results
* @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
* %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
- * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to
+ * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to
* specify a BSSID to scan for; if not included, the wildcard BSSID will
* be used.
* @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
@@ -350,7 +413,9 @@
* are used. Extra IEs can also be passed from the userspace by
* using the %NL80211_ATTR_IE attribute. The first cycle of the
* scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
- * is supplied.
+ * is supplied. If the device supports multiple concurrent scheduled
+ * scans, it will allow such when the caller provides the flag attribute
+ * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it.
* @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
* scheduled scan is not running. The caller may assume that as soon
* as the call returns, it is safe to start a new scheduled scan again.
@@ -369,10 +434,18 @@
* @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
* NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
*
- * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC
- * (for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC
+ * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK
+ * (PMK is used for PTKSA derivation in case of FILS shared key offload) or
+ * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+ * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+ * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+ * advertized by a FILS capable AP identifying the scope of PMKSA in an
+ * ESS.
* @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
- * (for the BSSID) and %NL80211_ATTR_PMKID.
+ * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+ * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+ * authentication.
* @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
*
* @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
@@ -472,7 +545,8 @@
* IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
* %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
* %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
- * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+ * %NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and
* %NL80211_ATTR_WIPHY_FREQ_HINT.
* If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
* restrictions on BSS selection, i.e., they effectively prevent roaming
@@ -499,8 +573,14 @@
* authentication/association or not receiving a response from the AP.
* Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
* well to remain backwards compatible.
- * @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
- * sent as an event when the card/driver roamed by itself.
+ * When establishing a security association, drivers that support 4 way
+ * handshake offload should send %NL80211_CMD_PORT_AUTHORIZED event when
+ * the 4 way handshake is completed successfully.
+ * @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself.
+ * When a security association was established with the new AP (e.g. if
+ * the FT protocol was used for roaming or the driver completed the 4 way
+ * handshake), this event should be followed by an
+ * %NL80211_CMD_PORT_AUTHORIZED event.
* @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
* userspace that a connection was dropped by the AP or due to other
* reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
@@ -599,6 +679,20 @@
*
* @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
*
+ * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform
+ * multicast to unicast conversion. When enabled, all multicast packets
+ * with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header)
+ * will be sent out to each station once with the destination (multicast)
+ * MAC address replaced by the station's MAC address. Note that this may
+ * break certain expectations of the receiver, e.g. the ability to drop
+ * unicast IP packets encapsulated in multicast L2 frames, or the ability
+ * to not send destination unreachable messages in such cases.
+ * This can only be toggled per BSS. Configure this on an interface of
+ * type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces
+ * (%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode.
+ * If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this
+ * command, the feature is disabled.
+ *
* @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
* mesh config parameters may be given.
* @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
@@ -838,6 +932,107 @@
* not running. The driver indicates the status of the scan through
* cfg80211_scan_done().
*
+ * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
+ * %NL80211_ATTR_WDEV interface. This interface must have been
+ * previously created with %NL80211_CMD_NEW_INTERFACE. After it
+ * has been started, the NAN interface will create or join a
+ * cluster. This command must have a valid
+ * %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ * %NL80211_ATTR_BANDS attributes. If %NL80211_ATTR_BANDS is
+ * omitted or set to 0, it means don't-care and the device will
+ * decide what to use. After this command NAN functions can be
+ * added.
+ * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
+ * its %NL80211_ATTR_WDEV interface.
+ * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
+ * with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this
+ * operation returns the strictly positive and unique instance id
+ * (%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE)
+ * of the function upon success.
+ * Since instance ID's can be re-used, this cookie is the right
+ * way to identify the function. This will avoid races when a termination
+ * event is handled by the user space after it has already added a new
+ * function that got the same instance id from the kernel as the one
+ * which just terminated.
+ * This cookie may be used in NAN events even before the command
+ * returns, so userspace shouldn't process NAN events until it processes
+ * the response to this command.
+ * Look at %NL80211_ATTR_SOCKET_OWNER as well.
+ * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie.
+ * This command is also used as a notification sent when a NAN function is
+ * terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
+ * and %NL80211_ATTR_COOKIE attributes.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN
+ * configuration. NAN must be operational (%NL80211_CMD_START_NAN
+ * was executed). It must contain at least one of the following
+ * attributes: %NL80211_ATTR_NAN_MASTER_PREF,
+ * %NL80211_ATTR_BANDS. If %NL80211_ATTR_BANDS is omitted, the
+ * current configuration is not changed. If it is present but
+ * set to zero, the configuration is changed to don't-care
+ * (i.e. the device can decide what to do).
+ * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
+ * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+ * %NL80211_ATTR_COOKIE.
+ *
+ * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters
+ * for subsequent roaming cases if the driver or firmware uses internal
+ * BSS selection. This command can be issued only while connected and it
+ * does not result in a change for the current association. Currently,
+ * only the %NL80211_ATTR_IE data is used and updated with this command.
+ *
+ * @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0
+ * for the given authenticator address (specified with %NL80211_ATTR_MAC).
+ * When %NL80211_ATTR_PMKR0_NAME is set, %NL80211_ATTR_PMK specifies the
+ * PMK-R0, otherwise it specifies the PMK.
+ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
+ * configured PMK for the authenticator address identified by
+ * %NL80211_ATTR_MAC.
+ * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates that the 4 way
+ * handshake was completed successfully by the driver. The BSSID is
+ * specified with %NL80211_ATTR_MAC. Drivers that support 4 way handshake
+ * offload should send this event after indicating 802.11 association with
+ * %NL80211_CMD_CONNECT or %NL80211_CMD_ROAM. If the 4 way handshake failed
+ * %NL80211_CMD_DISCONNECT should be indicated instead.
+ *
+ * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
+ * and RX notification. This command is used both as a request to transmit
+ * a control port frame and as a notification that a control port frame
+ * has been received. %NL80211_ATTR_FRAME is used to specify the
+ * frame contents. The frame is the raw EAPoL data, without ethernet or
+ * 802.11 headers.
+ * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
+ * indicating the protocol type of the received frame; whether the frame
+ * was received unencrypted and the MAC address of the peer respectively.
+ *
+ * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded.
+ *
+ * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host
+ * drivers that do not define separate commands for authentication and
+ * association, but rely on user space for the authentication to happen.
+ * This interface acts both as the event request (driver to user space)
+ * to trigger the authentication and command response (userspace to
+ * driver) to indicate the authentication status.
+ *
+ * User space uses the %NL80211_CMD_CONNECT command to the host driver to
+ * trigger a connection. The host driver selects a BSS and further uses
+ * this interface to offload only the authentication part to the user
+ * space. Authentication frames are passed between the driver and user
+ * space through the %NL80211_CMD_FRAME interface. Host driver proceeds
+ * further with the association after getting successful authentication
+ * status. User space indicates the authentication status through
+ * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH
+ * command interface.
+ *
+ * Host driver reports this status on an authentication failure to the
+ * user space through the connect result as the user space would have
+ * initiated the connection through the connect request.
+ *
+ * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's
+ * ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE,
+ * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its
+ * address(specified in %NL80211_ATTR_MAC).
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1026,6 +1221,30 @@ enum nl80211_commands {
NL80211_CMD_ABORT_SCAN,
+ NL80211_CMD_START_NAN,
+ NL80211_CMD_STOP_NAN,
+ NL80211_CMD_ADD_NAN_FUNCTION,
+ NL80211_CMD_DEL_NAN_FUNCTION,
+ NL80211_CMD_CHANGE_NAN_CONFIG,
+ NL80211_CMD_NAN_MATCH,
+
+ NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+
+ NL80211_CMD_UPDATE_CONNECT_PARAMS,
+
+ NL80211_CMD_SET_PMK,
+ NL80211_CMD_DEL_PMK,
+
+ NL80211_CMD_PORT_AUTHORIZED,
+
+ NL80211_CMD_RELOAD_REGDB,
+
+ NL80211_CMD_EXTERNAL_AUTH,
+
+ NL80211_CMD_STA_OPMODE_CHANGED,
+
+ NL80211_CMD_CONTROL_PORT_FRAME,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1248,8 +1467,12 @@ enum nl80211_commands {
*
* @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
* used for the association (&enum nl80211_mfp, represented as a u32);
- * this attribute can be used
- * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
+ * this attribute can be used with %NL80211_CMD_ASSOCIATE and
+ * %NL80211_CMD_CONNECT requests. %NL80211_MFP_OPTIONAL is not allowed for
+ * %NL80211_CMD_ASSOCIATE since user space SME is expected and hence, it
+ * must have decided whether to use management frame protection or not.
+ * Setting %NL80211_MFP_OPTIONAL with a %NL80211_CMD_CONNECT request will
+ * let the driver (or the firmware) decide whether to use MFP or not.
*
* @NL80211_ATTR_STA_FLAGS2: Attribute containing a
* &struct nl80211_sta_flag_update.
@@ -1269,6 +1492,15 @@ enum nl80211_commands {
* @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with
* %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom
* ethertype frames used for key negotiation must not be encrypted.
+ * @NL80211_ATTR_CONTROL_PORT_OVER_NL80211: A flag indicating whether control
+ * port frames (e.g. of type given in %NL80211_ATTR_CONTROL_PORT_ETHERTYPE)
+ * will be sent directly to the network interface or sent via the NL80211
+ * socket. If this attribute is missing, then legacy behavior of sending
+ * control port frames directly to the network interface is used. If the
+ * flag is included, then control port frames are sent over NL80211 instead
+ * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is
+ * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER
+ * flag.
*
* @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
* We recommend using nested, driver-specific attributes within this.
@@ -1343,7 +1575,13 @@ enum nl80211_commands {
* enum nl80211_band value is used as the index (nla_type() of the nested
* data. If a band is not included, it will be configured to allow all
* rates based on negotiated supported rates information. This attribute
- * is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
+ * is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP,
+ * and joining mesh networks (not IBSS yet). In the later case, it must
+ * specify just a single bitrate, which is to be used for the beacon.
+ * The driver must also specify support for this with the extended
+ * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ * NL80211_EXT_FEATURE_BEACON_RATE_HT and
+ * NL80211_EXT_FEATURE_BEACON_RATE_VHT.
*
* @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
* at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
@@ -1589,8 +1827,16 @@ enum nl80211_commands {
* the connection request from a station. nl80211_connect_failed_reason
* enum has different reasons of connection failure.
*
- * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts
- * with the Authentication transaction sequence number field.
+ * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames.
+ * This contains the authentication frame body (non-IE and IE data),
+ * excluding the Authentication algorithm number, i.e., starting at the
+ * Authentication transaction sequence number field. It is used with
+ * authentication algorithms that need special fields to be added into
+ * the frames (SAE and FILS). Currently, only the SAE cases use the
+ * initial two fields (Authentication transaction sequence number and
+ * Status code). However, those fields are included in the attribute data
+ * for all authentication algorithms to keep the attribute definition
+ * consistent.
*
* @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from
* association request when used with NL80211_CMD_NEW_STATION)
@@ -1691,7 +1937,9 @@ enum nl80211_commands {
*
* @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode
* Notification Element based on association request when used with
- * %NL80211_CMD_NEW_STATION; u8 attribute.
+ * %NL80211_CMD_NEW_STATION or %NL80211_CMD_SET_STATION (only when
+ * %NL80211_FEATURE_FULL_AP_CLIENT_STATE is supported, or with TDLS);
+ * u8 attribute.
*
* @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
* %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
@@ -1733,6 +1981,19 @@ enum nl80211_commands {
* regulatory indoor configuration would be owned by the netlink socket
* that configured the indoor setting, and the indoor operation would be
* cleared when the socket is closed.
+ * If set during NAN interface creation, the interface will be destroyed
+ * if the socket is closed just like any other interface. Moreover, NAN
+ * notifications will be sent in unicast to that socket. Without this
+ * attribute, the notifications will be sent to the %NL80211_MCGRP_NAN
+ * multicast group.
+ * If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
+ * station will deauthenticate when the socket is closed.
+ * If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically
+ * torn down when the socket is closed.
+ * If set during %NL80211_CMD_JOIN_MESH the mesh setup will be
+ * automatically torn down when the socket is closed.
+ * If set during %NL80211_CMD_START_AP the AP will be automatically
+ * disabled when the socket is closed.
*
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
* the TDLS link initiator.
@@ -1867,6 +2128,119 @@ enum nl80211_commands {
* @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is
* used to pull the stored data for mesh peer in power save state.
*
+ * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by
+ * %NL80211_CMD_START_NAN and optionally with
+ * %NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
+ * Also, values 1 and 255 are reserved for certification purposes and
+ * should not be used during a normal device operation.
+ * @NL80211_ATTR_BANDS: operating bands configuration. This is a u32
+ * bitmask of BIT(NL80211_BAND_*) as described in %enum
+ * nl80211_band. For instance, for NL80211_BAND_2GHZ, bit 0
+ * would be set. This attribute is used with
+ * %NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and
+ * it is optional. If no bands are set, it means don't-care and
+ * the device will decide what to use.
+ * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
+ * &enum nl80211_nan_func_attributes for description of this nested
+ * attribute.
+ * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
+ * See &enum nl80211_nan_match_attributes.
+ * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame
+ * protection.
+ * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association
+ * Request/Response frame protection. This attribute contains the 16 octet
+ * STA Nonce followed by 16 octets of AP Nonce.
+ *
+ * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast
+ * packets should be send out as unicast to all stations (flag attribute).
+ *
+ * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
+ * used in various commands/events for specifying the BSSID.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which
+ * other BSSs has to be better or slightly worse than the current
+ * connected BSS so that they get reported to user space.
+ * This will give an opportunity to userspace to consider connecting to
+ * other matching BSSs which have better or slightly worse RSSI than
+ * the current connected BSS by using an offloaded operation to avoid
+ * unnecessary wakeups.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
+ * the specified band is to be adjusted before doing
+ * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
+ * better BSSs. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ *
+ * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out.
+ * u32 attribute with an &enum nl80211_timeout_reason value. This is used,
+ * e.g., with %NL80211_CMD_CONNECT event.
+ *
+ * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP)
+ * username part of NAI used to refer keys rRK and rIK. This is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part
+ * of NAI specifying the domain name of the ER server. This is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number
+ * to use in ERP messages. This is used in generating the FILS wrapped data
+ * for FILS authentication and is used with %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the
+ * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and
+ * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK
+ * from successful FILS authentication and is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
+ * identifying the scope of PMKSAs. This is used with
+ * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+ *
+ * @NL80211_ATTR_PMK: attribute for passing PMK key material. Used with
+ * %NL80211_CMD_SET_PMKSA for the PMKSA identified by %NL80211_ATTR_PMKID.
+ * For %NL80211_CMD_CONNECT it is used to provide PSK for offloading 4-way
+ * handshake for WPA/WPA2-PSK networks. For 802.1X authentication it is
+ * used with %NL80211_CMD_SET_PMK. For offloaded FT support this attribute
+ * specifies the PMK-R0 if NL80211_ATTR_PMKR0_NAME is included as well.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to
+ * indicate that it supports multiple active scheduled scan requests.
+ * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled
+ * scan request that may be active for the device (u32).
+ *
+ * @NL80211_ATTR_WANT_1X_4WAY_HS: flag attribute which user-space can include
+ * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it
+ * wants to use the supported offload of the 4-way handshake.
+ * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT.
+ * @NL80211_ATTR_PORT_AUTHORIZED: (reserved)
+ *
+ * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external
+ * authentication operation (u32 attribute with an
+ * &enum nl80211_external_auth_action value). This is used with the
+ * %NL80211_CMD_EXTERNAL_AUTH request event.
+ * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
+ * space supports external authentication. This attribute shall be used
+ * only with %NL80211_CMD_CONNECT request. The driver may offload
+ * authentication processing to user space if this capability is indicated
+ * in NL80211_CMD_CONNECT requests from the user space.
+ *
+ * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this
+ * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
+ *
+ * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum
+ * nl80211_txq_stats)
+ * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy.
+ * The smaller of this and the memory limit is enforced.
+ * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the
+ * TXQ queues for this phy. The smaller of this and the packet limit is
+ * enforced.
+ * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes
+ * a flow is assigned on each round of the DRR scheduler.
+ * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION). Can be set
+ * only if %NL80211_STA_FLAG_WME is set.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2125,7 +2499,7 @@ enum nl80211_attrs {
NL80211_ATTR_CONN_FAILED_REASON,
- NL80211_ATTR_SAE_DATA,
+ NL80211_ATTR_AUTH_DATA,
NL80211_ATTR_VHT_CAPABILITY,
@@ -2261,6 +2635,53 @@ enum nl80211_attrs {
NL80211_ATTR_MESH_PEER_AID,
+ NL80211_ATTR_NAN_MASTER_PREF,
+ NL80211_ATTR_BANDS,
+ NL80211_ATTR_NAN_FUNC,
+ NL80211_ATTR_NAN_MATCH,
+
+ NL80211_ATTR_FILS_KEK,
+ NL80211_ATTR_FILS_NONCES,
+
+ NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED,
+
+ NL80211_ATTR_BSSID,
+
+ NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+ NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+
+ NL80211_ATTR_TIMEOUT_REASON,
+
+ NL80211_ATTR_FILS_ERP_USERNAME,
+ NL80211_ATTR_FILS_ERP_REALM,
+ NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ NL80211_ATTR_FILS_ERP_RRK,
+ NL80211_ATTR_FILS_CACHE_ID,
+
+ NL80211_ATTR_PMK,
+
+ NL80211_ATTR_SCHED_SCAN_MULTI,
+ NL80211_ATTR_SCHED_SCAN_MAX_REQS,
+
+ NL80211_ATTR_WANT_1X_4WAY_HS,
+ NL80211_ATTR_PMKR0_NAME,
+ NL80211_ATTR_PORT_AUTHORIZED,
+
+ NL80211_ATTR_EXTERNAL_AUTH_ACTION,
+ NL80211_ATTR_EXTERNAL_AUTH_SUPPORT,
+
+ NL80211_ATTR_NSS,
+ NL80211_ATTR_ACK_SIGNAL,
+
+ NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
+
+ NL80211_ATTR_TXQ_STATS,
+ NL80211_ATTR_TXQ_LIMIT,
+ NL80211_ATTR_TXQ_MEMORY_LIMIT,
+ NL80211_ATTR_TXQ_QUANTUM,
+
+ NL80211_ATTR_HE_CAPABILITY,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2272,6 +2693,7 @@ enum nl80211_attrs {
#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
+#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
/*
* Allow user space programs to use #ifdef on new attributes by defining them
@@ -2299,6 +2721,8 @@ enum nl80211_attrs {
#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+#define NL80211_WIPHY_NAME_MAXLEN 64
+
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_HT_RATES 77
#define NL80211_MAX_SUPP_REG_RULES 64
@@ -2307,7 +2731,8 @@ enum nl80211_attrs {
#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24
#define NL80211_HT_CAPABILITY_LEN 26
#define NL80211_VHT_CAPABILITY_LEN 12
-
+#define NL80211_HE_MIN_CAPABILITY_LEN 16
+#define NL80211_HE_MAX_CAPABILITY_LEN 51
#define NL80211_MAX_NR_CIPHER_SUITES 5
#define NL80211_MAX_NR_AKM_SUITES 2
@@ -2339,6 +2764,7 @@ enum nl80211_attrs {
* commands to create and destroy one
* @NL80211_IF_TYPE_OCB: Outside Context of a BSS
* This mode corresponds to the MIB variable dot11OCBActivated=true
+ * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
* @NUM_NL80211_IFTYPES: number of defined interface types
*
@@ -2359,6 +2785,7 @@ enum nl80211_iftype {
NL80211_IFTYPE_P2P_GO,
NL80211_IFTYPE_P2P_DEVICE,
NL80211_IFTYPE_OCB,
+ NL80211_IFTYPE_NAN,
/* keep last */
NUM_NL80211_IFTYPES,
@@ -2433,6 +2860,38 @@ struct nl80211_sta_flag_update {
} __attribute__((packed));
/**
+ * enum nl80211_he_gi - HE guard interval
+ * @NL80211_RATE_INFO_HE_GI_0_8: 0.8 usec
+ * @NL80211_RATE_INFO_HE_GI_1_6: 1.6 usec
+ * @NL80211_RATE_INFO_HE_GI_3_2: 3.2 usec
+ */
+enum nl80211_he_gi {
+ NL80211_RATE_INFO_HE_GI_0_8,
+ NL80211_RATE_INFO_HE_GI_1_6,
+ NL80211_RATE_INFO_HE_GI_3_2,
+};
+
+/**
+ * enum nl80211_he_ru_alloc - HE RU allocation values
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_26: 26-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_52: 52-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_106: 106-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_242: 242-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_484: 484-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_996: 996-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_2x996: 2x996-tone RU allocation
+ */
+enum nl80211_he_ru_alloc {
+ NL80211_RATE_INFO_HE_RU_ALLOC_26,
+ NL80211_RATE_INFO_HE_RU_ALLOC_52,
+ NL80211_RATE_INFO_HE_RU_ALLOC_106,
+ NL80211_RATE_INFO_HE_RU_ALLOC_242,
+ NL80211_RATE_INFO_HE_RU_ALLOC_484,
+ NL80211_RATE_INFO_HE_RU_ALLOC_996,
+ NL80211_RATE_INFO_HE_RU_ALLOC_2x996,
+};
+
+/**
* enum nl80211_rate_info - bitrate information
*
* These attribute types are used with %NL80211_STA_INFO_TXRATE
@@ -2464,6 +2923,13 @@ struct nl80211_sta_flag_update {
* @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is
* a legacy rate and will be reported as the actual bitrate, i.e.
* a quarter of the base (20 MHz) rate
+ * @NL80211_RATE_INFO_HE_MCS: HE MCS index (u8, 0-11)
+ * @NL80211_RATE_INFO_HE_NSS: HE NSS value (u8, 1-8)
+ * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier
+ * (u8, see &enum nl80211_he_gi)
+ * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1)
+ * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then
+ * non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc)
* @__NL80211_RATE_INFO_AFTER_LAST: internal use
*/
enum nl80211_rate_info {
@@ -2480,6 +2946,11 @@ enum nl80211_rate_info {
NL80211_RATE_INFO_160_MHZ_WIDTH,
NL80211_RATE_INFO_10_MHZ_WIDTH,
NL80211_RATE_INFO_5_MHZ_WIDTH,
+ NL80211_RATE_INFO_HE_MCS,
+ NL80211_RATE_INFO_HE_NSS,
+ NL80211_RATE_INFO_HE_GI,
+ NL80211_RATE_INFO_HE_DCM,
+ NL80211_RATE_INFO_HE_RU_ALLOC,
/* keep last */
__NL80211_RATE_INFO_AFTER_LAST,
@@ -2578,6 +3049,8 @@ enum nl80211_sta_bss_param {
* @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames
* received from the station (u64, usec)
* @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm)
+ * @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm)
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
@@ -2616,12 +3089,18 @@ enum nl80211_sta_info {
NL80211_STA_INFO_TID_STATS,
NL80211_STA_INFO_RX_DURATION,
NL80211_STA_INFO_PAD,
+ NL80211_STA_INFO_ACK_SIGNAL,
+ NL80211_STA_INFO_ACK_SIGNAL_AVG,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
};
+/* we renamed this - stay compatible */
+#define NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG NL80211_STA_INFO_ACK_SIGNAL_AVG
+
+
/**
* enum nl80211_tid_stats - per TID statistics attributes
* @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved
@@ -2633,6 +3112,7 @@ enum nl80211_sta_info {
* @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
* MSDUs (u64)
* @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute)
* @NUM_NL80211_TID_STATS: number of attributes here
* @NL80211_TID_STATS_MAX: highest numbered attribute here
*/
@@ -2643,6 +3123,7 @@ enum nl80211_tid_stats {
NL80211_TID_STATS_TX_MSDU_RETRIES,
NL80211_TID_STATS_TX_MSDU_FAILED,
NL80211_TID_STATS_PAD,
+ NL80211_TID_STATS_TXQ_STATS,
/* keep last */
NUM_NL80211_TID_STATS,
@@ -2650,6 +3131,44 @@ enum nl80211_tid_stats {
};
/**
+ * enum nl80211_txq_stats - per TXQ statistics attributes
+ * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved
+ * @NUM_NL80211_TXQ_STATS: number of attributes here
+ * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged
+ * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently
+ * backlogged
+ * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen
+ * @NL80211_TXQ_STATS_DROPS: total number of packet drops
+ * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks
+ * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow
+ * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow
+ * (only for per-phy stats)
+ * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions
+ * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ
+ * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ
+ * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY
+ * @NL80211_TXQ_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_txq_stats {
+ __NL80211_TXQ_STATS_INVALID,
+ NL80211_TXQ_STATS_BACKLOG_BYTES,
+ NL80211_TXQ_STATS_BACKLOG_PACKETS,
+ NL80211_TXQ_STATS_FLOWS,
+ NL80211_TXQ_STATS_DROPS,
+ NL80211_TXQ_STATS_ECN_MARKS,
+ NL80211_TXQ_STATS_OVERLIMIT,
+ NL80211_TXQ_STATS_OVERMEMORY,
+ NL80211_TXQ_STATS_COLLISIONS,
+ NL80211_TXQ_STATS_TX_BYTES,
+ NL80211_TXQ_STATS_TX_PACKETS,
+ NL80211_TXQ_STATS_MAX_FLOWS,
+
+ /* keep last */
+ NUM_NL80211_TXQ_STATS,
+ NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1
+};
+
+/**
* enum nl80211_mpath_flags - nl80211 mesh path flags
*
* @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
@@ -2701,6 +3220,38 @@ enum nl80211_mpath_info {
};
/**
+ * enum nl80211_band_iftype_attr - Interface type data attributes
+ *
+ * @__NL80211_BAND_IFTYPE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BAND_IFTYPE_ATTR_IFTYPES: nested attribute containing a flag attribute
+ * for each interface type that supports the band data
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC: HE MAC capabilities as in HE
+ * capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY: HE PHY capabilities as in HE
+ * capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET: HE supported NSS/MCS as in HE
+ * capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as
+ * defined in HE capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band HE capability attribute currently
+ * defined
+ * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_band_iftype_attr {
+ __NL80211_BAND_IFTYPE_ATTR_INVALID,
+
+ NL80211_BAND_IFTYPE_ATTR_IFTYPES,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
+
+ /* keep last */
+ __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
+ NL80211_BAND_IFTYPE_ATTR_MAX = __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_band_attr - band attributes
* @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_BAND_ATTR_FREQS: supported frequencies in this band,
@@ -2715,6 +3266,8 @@ enum nl80211_mpath_info {
* @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as
* defined in 802.11ac
* @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using
+ * attributes from &enum nl80211_band_iftype_attr
* @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
* @__NL80211_BAND_ATTR_AFTER_LAST: internal use
*/
@@ -2730,6 +3283,7 @@ enum nl80211_band_attr {
NL80211_BAND_ATTR_VHT_MCS_SET,
NL80211_BAND_ATTR_VHT_CAPA,
+ NL80211_BAND_ATTR_IFTYPE_DATA,
/* keep last */
__NL80211_BAND_ATTR_AFTER_LAST,
@@ -2739,6 +3293,29 @@ enum nl80211_band_attr {
#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
/**
+ * enum nl80211_wmm_rule - regulatory wmm rule
+ *
+ * @__NL80211_WMMR_INVALID: attribute number 0 is reserved
+ * @NL80211_WMMR_CW_MIN: Minimum contention window slot.
+ * @NL80211_WMMR_CW_MAX: Maximum contention window slot.
+ * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space.
+ * @NL80211_WMMR_TXOP: Maximum allowed tx operation time.
+ * @nl80211_WMMR_MAX: highest possible wmm rule.
+ * @__NL80211_WMMR_LAST: Internal use.
+ */
+enum nl80211_wmm_rule {
+ __NL80211_WMMR_INVALID,
+ NL80211_WMMR_CW_MIN,
+ NL80211_WMMR_CW_MAX,
+ NL80211_WMMR_AIFSN,
+ NL80211_WMMR_TXOP,
+
+ /* keep last */
+ __NL80211_WMMR_LAST,
+ NL80211_WMMR_MAX = __NL80211_WMMR_LAST - 1
+};
+
+/**
* enum nl80211_frequency_attr - frequency attributes
* @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
@@ -2787,6 +3364,9 @@ enum nl80211_band_attr {
* on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
* on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations.
+ * This is a nested attribute that contains the wmm limitation per AC.
+ * (see &enum nl80211_wmm_rule)
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -2815,6 +3395,7 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
NL80211_FREQUENCY_ATTR_NO_20MHZ,
NL80211_FREQUENCY_ATTR_NO_10MHZ,
+ NL80211_FREQUENCY_ATTR_WMM,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2942,6 +3523,7 @@ enum nl80211_reg_rule_attr {
* @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
* only report BSS with matching SSID.
+ * (This cannot be used together with BSSID.)
* @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
* BSS in scan results. Filtering is turned off if not specified. Note that
* if this attribute is in a match set of its own, then it is treated as
@@ -2950,6 +3532,15 @@ enum nl80211_reg_rule_attr {
* how this API was implemented in the past. Also, due to the same problem,
* the only way to create a matchset with only an RSSI filter (with this
* attribute) is if there's only a single matchset with the RSSI attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether
+ * %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or
+ * relative to current bss's RSSI.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for
+ * BSS-es in the specified band is to be adjusted before doing
+ * RSSI-based BSS selection. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
+ * (this cannot be used together with SSID).
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -2959,6 +3550,9 @@ enum nl80211_sched_scan_match_attr {
NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
+ NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
/* keep last */
__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -2985,7 +3579,7 @@ enum nl80211_sched_scan_match_attr {
* @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
* base on contiguous rules and wider channels will be allowed to cross
* multiple contiguous/overlapping frequency ranges.
- * @NL80211_RRF_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+ * @NL80211_RRF_IR_CONCURRENT: See %NL80211_FREQUENCY_ATTR_IR_CONCURRENT
* @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
* @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
* @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
@@ -3528,6 +4122,9 @@ enum nl80211_bss_scan_width {
* @NL80211_BSS_PARENT_BSSID. (u64).
* @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
* is set.
+ * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
+ * Contains a nested array of signal strength attributes (u8, dBm),
+ * using the nesting index as the antenna number.
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -3551,6 +4148,7 @@ enum nl80211_bss {
NL80211_BSS_PAD,
NL80211_BSS_PARENT_TSF,
NL80211_BSS_PARENT_BSSID,
+ NL80211_BSS_CHAIN_SIGNAL,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -3583,6 +4181,9 @@ enum nl80211_bss_status {
* @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r)
* @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP)
* @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals
+ * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key
+ * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS
+ * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key
* @__NL80211_AUTHTYPE_NUM: internal
* @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm
* @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by
@@ -3595,6 +4196,9 @@ enum nl80211_auth_type {
NL80211_AUTHTYPE_FT,
NL80211_AUTHTYPE_NETWORK_EAP,
NL80211_AUTHTYPE_SAE,
+ NL80211_AUTHTYPE_FILS_SK,
+ NL80211_AUTHTYPE_FILS_SK_PFS,
+ NL80211_AUTHTYPE_FILS_PK,
/* keep last */
__NL80211_AUTHTYPE_NUM,
@@ -3621,10 +4225,12 @@ enum nl80211_key_type {
* enum nl80211_mfp - Management frame protection state
* @NL80211_MFP_NO: Management frame protection not used
* @NL80211_MFP_REQUIRED: Management frame protection required
+ * @NL80211_MFP_OPTIONAL: Management frame protection is optional
*/
enum nl80211_mfp {
NL80211_MFP_NO,
NL80211_MFP_REQUIRED,
+ NL80211_MFP_OPTIONAL,
};
enum nl80211_wpa_versions {
@@ -3735,7 +4341,7 @@ enum nl80211_txrate_gi {
* enum nl80211_band - Frequency band
* @NL80211_BAND_2GHZ: 2.4 GHz ISM band
* @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
- * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz)
+ * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
* @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
* since newer kernel versions may support more bands
*/
@@ -3762,7 +4368,10 @@ enum nl80211_ps_state {
* @__NL80211_ATTR_CQM_INVALID: invalid
* @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
* the threshold for the RSSI level at which an event will be sent. Zero
- * to disable.
+ * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is
+ * set, multiple values can be supplied as a low-to-high sorted array of
+ * threshold values in dBm. Events will be sent when the RSSI value
+ * crosses any of the thresholds.
* @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
* the minimum amount the RSSI level must change after an event before a
* new event may be issued (to reduce effects of RSSI oscillation).
@@ -3782,6 +4391,8 @@ enum nl80211_ps_state {
* %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
* @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
* loss event
+ * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the
+ * RSSI threshold event.
* @__NL80211_ATTR_CQM_AFTER_LAST: internal
* @NL80211_ATTR_CQM_MAX: highest key attribute
*/
@@ -3795,6 +4406,7 @@ enum nl80211_attr_cqm {
NL80211_ATTR_CQM_TXE_PKTS,
NL80211_ATTR_CQM_TXE_INTVL,
NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+ NL80211_ATTR_CQM_RSSI_LEVEL,
/* keep last */
__NL80211_ATTR_CQM_AFTER_LAST,
@@ -4203,6 +4815,9 @@ enum nl80211_iface_limit_attrs {
* of supported channel widths for radar detection.
* @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
* of supported regulatory regions for radar detection.
+ * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of
+ * different beacon intervals supported by all the interface combinations
+ * in this group (if not present, all beacon intervals be identical).
* @NUM_NL80211_IFACE_COMB: number of attributes
* @MAX_NL80211_IFACE_COMB: highest attribute number
*
@@ -4210,8 +4825,8 @@ enum nl80211_iface_limit_attrs {
* limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
* => allows an AP and a STA that must match BIs
*
- * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8
- * => allows 8 of AP/GO
+ * numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8,
+ * => allows 8 of AP/GO that can have BI gcd >= min gcd
*
* numbers = [ #{STA} <= 2 ], channels = 2, max = 2
* => allows two STAs on different channels
@@ -4237,6 +4852,7 @@ enum nl80211_if_combination_attrs {
NL80211_IFACE_COMB_NUM_CHANNELS,
NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+ NL80211_IFACE_COMB_BI_MIN_GCD,
/* keep last */
NUM_NL80211_IFACE_COMB,
@@ -4551,6 +5167,64 @@ enum nl80211_feature_flags {
* (if available).
* @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
* channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+ * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ * configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+ * configuration (AP/mesh) with VHT rates.
+ * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
+ * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA
+ * in @NL80211_CMD_FRAME while not associated.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports
+ * randomized TA in @NL80211_CMD_FRAME while associated.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
+ * for reporting BSSs with better RSSI than the current connected BSS
+ * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
+ * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the
+ * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more
+ * RSSI threshold values to monitor rather than exactly one threshold.
+ * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
+ * authentication with %NL80211_CMD_CONNECT.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way
+ * handshake with PSK in station mode (PSK is passed as part of the connect
+ * and associate commands), doing it in the host might not be supported.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way
+ * handshake with 802.1X in station mode (will pass EAP frames to the host
+ * and accept the set_pmk/del_pmk commands), doing it in the host might not
+ * be supported.
+ * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding
+ * the max channel attribute in the FILS request params IE with the
+ * actual dwell time.
+ * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe
+ * response
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending
+ * the first probe request in each channel at rate of at least 5.5Mbps.
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports
+ * probe request tx deferral and suppression
+ * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL
+ * value in %NL80211_ATTR_USE_MFP.
+ * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan.
+ * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan.
+ * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan.
+ * @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions.
+ * Device or driver will do all DFS-related actions by itself,
+ * informing user-space about CAC progress, radar detection event,
+ * channel change triggered by radar detection event.
+ * No need to start CAC from user-space, no need to react to
+ * "radar detected" event.
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and
+ * receiving control port frames over nl80211 instead of the netdevice.
+ * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports
+ * (average) ACK signal strength reporting.
+ * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
+ * TXQs.
+ * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the
+ * SN in probe request frames if requested by %NL80211_SCAN_FLAG_RANDOM_SN.
+ * @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data
+ * except for supported rates from the probe request content if requested
+ * by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4562,6 +5236,33 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_SCAN_START_TIME,
NL80211_EXT_FEATURE_BSS_PARENT_TSF,
NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ NL80211_EXT_FEATURE_BEACON_RATE_HT,
+ NL80211_EXT_FEATURE_BEACON_RATE_VHT,
+ NL80211_EXT_FEATURE_FILS_STA,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
+ NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
+ NL80211_EXT_FEATURE_CQM_RSSI_LIST,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
+ NL80211_EXT_FEATURE_DFS_OFFLOAD,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
+ NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT,
+ /* we renamed this - stay compatible */
+ NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT,
+ NL80211_EXT_FEATURE_TXQS,
+ NL80211_EXT_FEATURE_SCAN_RANDOM_SN,
+ NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -4601,12 +5302,31 @@ enum nl80211_connect_failed_reason {
};
/**
+ * enum nl80211_timeout_reason - timeout reasons
+ *
+ * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified.
+ * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out.
+ * @NL80211_TIMEOUT_AUTH: Authentication timed out.
+ * @NL80211_TIMEOUT_ASSOC: Association timed out.
+ */
+enum nl80211_timeout_reason {
+ NL80211_TIMEOUT_UNSPECIFIED,
+ NL80211_TIMEOUT_SCAN,
+ NL80211_TIMEOUT_AUTH,
+ NL80211_TIMEOUT_ASSOC,
+};
+
+/**
* enum nl80211_scan_flags - scan request control flags
*
* Scan request control flags are used to control the handling
* of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
* requests.
*
+ * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and
+ * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only
+ * one of them can be used in the request.
+ *
* @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
* @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
* @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
@@ -4623,12 +5343,52 @@ enum nl80211_connect_failed_reason {
* locally administered 1, multicast 0) is assumed.
* This flag must not be requested when the feature isn't supported, check
* the nl80211 feature flags for the device.
+ * @NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME: fill the dwell time in the FILS
+ * request parameters IE in the probe request
+ * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
+ * rate of at least 5.5M. In case non OCE AP is dicovered in the channel,
+ * only the first probe req in the channel will be sent in high rate.
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
+ * tx deferral (dot11FILSProbeDelay shall be set to 15ms)
+ * and suppression (if it has received a broadcast Probe Response frame,
+ * Beacon frame or FILS Discovery frame from an AP that the STA considers
+ * a suitable candidate for (re-)association - suitable in terms of
+ * SSID and/or RSSI.
+ * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to
+ * accomplish the scan. Thus, this flag intends the driver to perform the
+ * scan request with lesser span/duration. It is specific to the driver
+ * implementations on how this is accomplished. Scan accuracy may get
+ * impacted with this flag.
+ * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume
+ * optimal possible power. Drivers can resort to their specific means to
+ * optimize the power. Scan accuracy may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan
+ * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum
+ * possible scan results. This flag hints the driver to use the best
+ * possible scan configuration to improve the accuracy in scanning.
+ * Latency and power use may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_RANDOM_SN: randomize the sequence number in probe
+ * request frames from this scan to avoid correlation/tracking being
+ * possible.
+ * @NL80211_SCAN_FLAG_MIN_PREQ_CONTENT: minimize probe request content to
+ * only have supported rates and no additional capabilities (unless
+ * added by userspace explicitly.)
*/
enum nl80211_scan_flags {
- NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
- NL80211_SCAN_FLAG_FLUSH = 1<<1,
- NL80211_SCAN_FLAG_AP = 1<<2,
- NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+ NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
+ NL80211_SCAN_FLAG_FLUSH = 1<<1,
+ NL80211_SCAN_FLAG_AP = 1<<2,
+ NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+ NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = 1<<4,
+ NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 1<<5,
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 1<<6,
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 1<<7,
+ NL80211_SCAN_FLAG_LOW_SPAN = 1<<8,
+ NL80211_SCAN_FLAG_LOW_POWER = 1<<9,
+ NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10,
+ NL80211_SCAN_FLAG_RANDOM_SN = 1<<11,
+ NL80211_SCAN_FLAG_MIN_PREQ_CONTENT = 1<<12,
};
/**
@@ -4682,12 +5442,20 @@ enum nl80211_smps_mode {
* change to the channel status.
* @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
* over, channel becomes usable.
+ * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this
+ * non-operating channel is expired and no longer valid. New CAC must
+ * be done on this channel before starting the operation. This is not
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
*/
enum nl80211_radar_event {
NL80211_RADAR_DETECTED,
NL80211_RADAR_CAC_FINISHED,
NL80211_RADAR_CAC_ABORTED,
NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
};
/**
@@ -4814,8 +5582,9 @@ enum nl80211_sched_scan_plan {
/**
* struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
*
- * @band: band of BSS that must match for RSSI value adjustment.
- * @delta: value used to adjust the RSSI value of matching BSS.
+ * @band: band of BSS that must match for RSSI value adjustment. The value
+ * of this field is according to &enum nl80211_band.
+ * @delta: value used to adjust the RSSI value of matching BSS in dB.
*/
struct nl80211_bss_select_rssi_adjust {
__u8 band;
@@ -4855,4 +5624,182 @@ enum nl80211_bss_select_attr {
NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
};
+/**
+ * enum nl80211_nan_function_type - NAN function type
+ *
+ * Defines the function type of a NAN function
+ *
+ * @NL80211_NAN_FUNC_PUBLISH: function is publish
+ * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+ * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
+ */
+enum nl80211_nan_function_type {
+ NL80211_NAN_FUNC_PUBLISH,
+ NL80211_NAN_FUNC_SUBSCRIBE,
+ NL80211_NAN_FUNC_FOLLOW_UP,
+
+ /* keep last */
+ __NL80211_NAN_FUNC_TYPE_AFTER_LAST,
+ NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum nl80211_nan_publish_type - NAN publish tx type
+ *
+ * Defines how to send publish Service Discovery Frames
+ *
+ * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited
+ * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited
+ */
+enum nl80211_nan_publish_type {
+ NL80211_NAN_SOLICITED_PUBLISH = 1 << 0,
+ NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1,
+};
+
+/**
+ * enum nl80211_nan_func_term_reason - NAN functions termination reason
+ *
+ * Defines termination reasons of a NAN function
+ *
+ * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user
+ * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout
+ * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored
+ */
+enum nl80211_nan_func_term_reason {
+ NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST,
+ NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED,
+ NL80211_NAN_FUNC_TERM_REASON_ERROR,
+};
+
+#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6
+#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff
+#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff
+
+/**
+ * enum nl80211_nan_func_attributes - NAN function attributes
+ * @__NL80211_NAN_FUNC_INVALID: invalid
+ * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8).
+ * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as
+ * specified in NAN spec. This is a binary attribute.
+ * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is
+ * publish. Defines the transmission type for the publish Service Discovery
+ * Frame, see &enum nl80211_nan_publish_type. Its type is u8.
+ * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited
+ * publish. Should the solicited publish Service Discovery Frame be sent to
+ * the NAN Broadcast address. This is a flag.
+ * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is
+ * subscribe. Is the subscribe active. This is a flag.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up.
+ * The instance ID for the follow up Service Discovery Frame. This is u8.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+ * is follow up. This is a u8.
+ * The requestor instance ID for the follow up Service Discovery Frame.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+ * follow up Service Discovery Frame. This is a binary attribute.
+ * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+ * close range. The range itself (RSSI) is defined by the device.
+ * This is a flag.
+ * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should
+ * stay active. If not present infinite TTL is assumed. This is a u32.
+ * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service
+ * specific info. This is a binary attribute.
+ * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute.
+ * See &enum nl80211_nan_srf_attributes.
+ * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested
+ * attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a
+ * nested attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function.
+ * Its type is u8 and it cannot be 0.
+ * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason.
+ * See &enum nl80211_nan_func_term_reason.
+ *
+ * @NUM_NL80211_NAN_FUNC_ATTR: internal
+ * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute
+ */
+enum nl80211_nan_func_attributes {
+ __NL80211_NAN_FUNC_INVALID,
+ NL80211_NAN_FUNC_TYPE,
+ NL80211_NAN_FUNC_SERVICE_ID,
+ NL80211_NAN_FUNC_PUBLISH_TYPE,
+ NL80211_NAN_FUNC_PUBLISH_BCAST,
+ NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE,
+ NL80211_NAN_FUNC_FOLLOW_UP_ID,
+ NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID,
+ NL80211_NAN_FUNC_FOLLOW_UP_DEST,
+ NL80211_NAN_FUNC_CLOSE_RANGE,
+ NL80211_NAN_FUNC_TTL,
+ NL80211_NAN_FUNC_SERVICE_INFO,
+ NL80211_NAN_FUNC_SRF,
+ NL80211_NAN_FUNC_RX_MATCH_FILTER,
+ NL80211_NAN_FUNC_TX_MATCH_FILTER,
+ NL80211_NAN_FUNC_INSTANCE_ID,
+ NL80211_NAN_FUNC_TERM_REASON,
+
+ /* keep last */
+ NUM_NL80211_NAN_FUNC_ATTR,
+ NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1
+};
+
+/**
+ * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes
+ * @__NL80211_NAN_SRF_INVALID: invalid
+ * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set.
+ * This is a flag.
+ * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if
+ * %NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
+ * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if
+ * %NL80211_NAN_SRF_BF is present. This is a u8.
+ * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if
+ * and only if %NL80211_NAN_SRF_BF isn't present. This is a nested
+ * attribute. Each nested attribute is a MAC address.
+ * @NUM_NL80211_NAN_SRF_ATTR: internal
+ * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute
+ */
+enum nl80211_nan_srf_attributes {
+ __NL80211_NAN_SRF_INVALID,
+ NL80211_NAN_SRF_INCLUDE,
+ NL80211_NAN_SRF_BF,
+ NL80211_NAN_SRF_BF_IDX,
+ NL80211_NAN_SRF_MAC_ADDRS,
+
+ /* keep last */
+ NUM_NL80211_NAN_SRF_ATTR,
+ NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1,
+};
+
+/**
+ * enum nl80211_nan_match_attributes - NAN match attributes
+ * @__NL80211_NAN_MATCH_INVALID: invalid
+ * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the
+ * match. This is a nested attribute.
+ * See &enum nl80211_nan_func_attributes.
+ * @NL80211_NAN_MATCH_FUNC_PEER: the peer function
+ * that caused the match. This is a nested attribute.
+ * See &enum nl80211_nan_func_attributes.
+ *
+ * @NUM_NL80211_NAN_MATCH_ATTR: internal
+ * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute
+ */
+enum nl80211_nan_match_attributes {
+ __NL80211_NAN_MATCH_INVALID,
+ NL80211_NAN_MATCH_FUNC_LOCAL,
+ NL80211_NAN_MATCH_FUNC_PEER,
+
+ /* keep last */
+ NUM_NL80211_NAN_MATCH_ATTR,
+ NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1
+};
+
+/**
+ * nl80211_external_auth_action - Action to perform with external
+ * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION.
+ * @NL80211_EXTERNAL_AUTH_START: Start the authentication.
+ * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication.
+ */
+enum nl80211_external_auth_action {
+ NL80211_EXTERNAL_AUTH_START,
+ NL80211_EXTERNAL_AUTH_ABORT,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index 621746821538..bfe8811e33f2 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -161,7 +161,6 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub)
int generator;
u8 gen;
const struct dh_group *dh;
- size_t pub_len, i;
generator = eap_eke_dh_generator(group);
dh = eap_eke_dh_group(group);
@@ -169,33 +168,11 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub)
return -1;
gen = generator;
- /* x = random number 2 .. p-1 */
- if (random_get_bytes(ret_priv, dh->prime_len))
- return -1;
- if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) {
- /* Make sure private value is smaller than prime */
- ret_priv[0] = 0;
- }
- for (i = 0; i < dh->prime_len - 1; i++) {
- if (ret_priv[i])
- break;
- }
- if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1))
+ if (crypto_dh_init(gen, dh->prime, dh->prime_len, ret_priv,
+ ret_pub) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value",
ret_priv, dh->prime_len);
-
- /* y = g ^ x (mod p) */
- pub_len = dh->prime_len;
- if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len,
- dh->prime, dh->prime_len, ret_pub, &pub_len) < 0)
- return -1;
- if (pub_len < dh->prime_len) {
- size_t pad = dh->prime_len - pub_len;
- os_memmove(ret_pub + pad, ret_pub, pub_len);
- os_memset(ret_pub, 0, pad);
- }
-
wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value",
ret_pub, dh->prime_len);
@@ -421,8 +398,9 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
/* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */
len = dh->prime_len;
- if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len,
- dh->prime, dh->prime_len, modexp, &len) < 0)
+ if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+ dhpriv, dh->prime_len, peer_pub,
+ dh->prime_len, modexp, &len) < 0)
return -1;
if (len < dh->prime_len) {
size_t pad = dh->prime_len - len;
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
index 9ef671c41c7d..57990d254a6a 100644
--- a/src/eap_common/eap_fast_common.c
+++ b/src/eap_common/eap_fast_common.c
@@ -79,7 +79,7 @@ void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
/*
* RFC 4851, Section 5.1:
- * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
+ * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
* server_random + client_random, 48)
*/
os_memcpy(seed, server_random, TLS_RANDOM_LEN);
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 67f8f7098c4b..88c6595887ab 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -81,6 +81,27 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
}
+EAP_PWD_group * get_eap_pwd_group(u16 num)
+{
+ EAP_PWD_group *grp;
+
+ grp = os_zalloc(sizeof(EAP_PWD_group));
+ if (!grp)
+ return NULL;
+ grp->group = crypto_ec_init(num);
+ if (!grp->group) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC group");
+ os_free(grp);
+ return NULL;
+ }
+
+ grp->group_num = num;
+ wpa_printf(MSG_INFO, "EAP-pwd: provisioned group %d", num);
+
+ return grp;
+}
+
+
/*
* compute a "random" secret point on an elliptic curve based
* on the password and identities.
@@ -91,105 +112,71 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
const u8 *id_peer, size_t id_peer_len,
const u8 *token)
{
- BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
+ struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
struct crypto_hash *hash;
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
- int nid, is_odd, ret = 0;
+ int is_odd, ret = 0, check, found = 0;
size_t primebytelen, primebitlen;
+ struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ const struct crypto_bignum *prime;
- switch (num) { /* from IANA registry for IKE D-H groups */
- case 19:
- nid = NID_X9_62_prime256v1;
- break;
- case 20:
- nid = NID_secp384r1;
- break;
- case 21:
- nid = NID_secp521r1;
- break;
-#ifndef OPENSSL_IS_BORINGSSL
- case 25:
- nid = NID_X9_62_prime192v1;
- break;
-#endif /* OPENSSL_IS_BORINGSSL */
- case 26:
- nid = NID_secp224r1;
- break;
-#ifdef NID_brainpoolP224r1
- case 27:
- nid = NID_brainpoolP224r1;
- break;
-#endif /* NID_brainpoolP224r1 */
-#ifdef NID_brainpoolP256r1
- case 28:
- nid = NID_brainpoolP256r1;
- break;
-#endif /* NID_brainpoolP256r1 */
-#ifdef NID_brainpoolP384r1
- case 29:
- nid = NID_brainpoolP384r1;
- break;
-#endif /* NID_brainpoolP384r1 */
-#ifdef NID_brainpoolP512r1
- case 30:
- nid = NID_brainpoolP512r1;
- break;
-#endif /* NID_brainpoolP512r1 */
- default:
- wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
+ if (grp->pwe)
return -1;
- }
- grp->pwe = NULL;
- grp->order = NULL;
- grp->prime = NULL;
-
- if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
- goto fail;
- }
-
- if (((rnd = BN_new()) == NULL) ||
- ((cofactor = BN_new()) == NULL) ||
- ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
- ((grp->order = BN_new()) == NULL) ||
- ((grp->prime = BN_new()) == NULL) ||
- ((x_candidate = BN_new()) == NULL)) {
+ prime = crypto_ec_get_prime(grp->group);
+ cofactor = crypto_bignum_init();
+ grp->pwe = crypto_ec_point_init(grp->group);
+ tmp1 = crypto_bignum_init();
+ pm1 = crypto_bignum_init();
+ one = crypto_bignum_init_set((const u8 *) "\x01", 1);
+ if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
goto fail;
}
- if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
- {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
- "curve");
- goto fail;
- }
- if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
- goto fail;
- }
- if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
+ if (crypto_ec_cofactor(grp->group, cofactor) < 0) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
"curve");
goto fail;
}
- primebitlen = BN_num_bits(grp->prime);
- primebytelen = BN_num_bytes(grp->prime);
+ primebitlen = crypto_ec_prime_len_bits(grp->group);
+ primebytelen = crypto_ec_prime_len(grp->group);
if ((prfbuf = os_malloc(primebytelen)) == NULL) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
"buffer");
goto fail;
}
- os_memset(prfbuf, 0, primebytelen);
- ctr = 0;
- while (1) {
- if (ctr > 30) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
- "point on curve for group %d, something's "
- "fishy", num);
+ if (crypto_bignum_sub(prime, one, pm1) < 0)
+ goto fail;
+
+ /* get a random quadratic residue and nonresidue */
+ while (!qr || !qnr) {
+ int res;
+
+ if (crypto_bignum_rand(tmp1, prime) < 0)
goto fail;
+ res = crypto_bignum_legendre(tmp1, prime);
+ if (!qr && res == 1) {
+ qr = tmp1;
+ tmp1 = crypto_bignum_init();
+ } else if (!qnr && res == -1) {
+ qnr = tmp1;
+ tmp1 = crypto_bignum_init();
}
+ if (!tmp1)
+ goto fail;
+ }
+
+ os_memset(prfbuf, 0, primebytelen);
+ ctr = 0;
+
+ /*
+ * Run through the hunting-and-pecking loop 40 times to mask the time
+ * necessary to find PWE. The odds of PWE not being found in 40 loops is
+ * roughly 1 in 1 trillion.
+ */
+ while (ctr < 40) {
ctr++;
/*
@@ -207,15 +194,25 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
eap_pwd_h_update(hash, &ctr, sizeof(ctr));
eap_pwd_h_final(hash, pwe_digest);
- BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
-
+ crypto_bignum_deinit(rnd, 1);
+ rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN);
+ if (!rnd) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd");
+ goto fail;
+ }
if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
(u8 *) "EAP-pwd Hunting And Pecking",
os_strlen("EAP-pwd Hunting And Pecking"),
prfbuf, primebitlen) < 0)
goto fail;
- BN_bin2bn(prfbuf, primebytelen, x_candidate);
+ crypto_bignum_deinit(x_candidate, 1);
+ x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
+ if (!x_candidate) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: unable to create x_candidate");
+ goto fail;
+ }
/*
* eap_pwd_kdf() returns a string of bits 0..primebitlen but
@@ -224,97 +221,157 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
* then excessive bits-- those _after_ primebitlen-- so now
* we have to shift right the amount we masked off.
*/
- if (primebitlen % 8)
- BN_rshift(x_candidate, x_candidate,
- (8 - (primebitlen % 8)));
+ if ((primebitlen % 8) &&
+ crypto_bignum_rshift(x_candidate,
+ (8 - (primebitlen % 8)),
+ x_candidate) < 0)
+ goto fail;
- if (BN_ucmp(x_candidate, grp->prime) >= 0)
+ if (crypto_bignum_cmp(x_candidate, prime) >= 0)
continue;
wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
prfbuf, primebytelen);
/*
- * need to unambiguously identify the solution, if there is
- * one...
+ * compute y^2 using the equation of the curve
+ *
+ * y^2 = x^3 + ax + b
*/
- if (BN_is_odd(rnd))
- is_odd = 1;
- else
- is_odd = 0;
+ tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
+ if (!tmp2)
+ goto fail;
/*
- * solve the quadratic equation, if it's not solvable then we
- * don't have a point
+ * mask tmp2 so doing legendre won't leak timing info
+ *
+ * tmp1 is a random number between 1 and p-1
*/
- if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
- grp->pwe,
- x_candidate,
- is_odd, NULL))
- continue;
+ if (crypto_bignum_rand(tmp1, pm1) < 0 ||
+ crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 ||
+ crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0)
+ goto fail;
+
/*
- * If there's a solution to the equation then the point must be
- * on the curve so why check again explicitly? OpenSSL code
- * says this is required by X9.62. We're not X9.62 but it can't
- * hurt just to be sure.
+ * Now tmp2 (y^2) is masked, all values between 1 and p-1
+ * are equally probable. Multiplying by r^2 does not change
+ * whether or not tmp2 is a quadratic residue, just masks it.
+ *
+ * Flip a coin, multiply by the random quadratic residue or the
+ * random quadratic nonresidue and record heads or tails.
*/
- if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
- wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
- continue;
+ if (crypto_bignum_is_odd(tmp1)) {
+ crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
+ check = 1;
+ } else {
+ crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
+ check = -1;
}
- if (BN_cmp(cofactor, BN_value_one())) {
- /* make sure the point is not in a small sub-group */
- if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
- cofactor, NULL)) {
- wpa_printf(MSG_INFO, "EAP-pwd: cannot "
- "multiply generator by order");
+ /*
+ * Now it's safe to do legendre, if check is 1 then it's
+ * a straightforward test (multiplying by qr does not
+ * change result), if check is -1 then it's the opposite test
+ * (multiplying a qr by qnr would make a qnr).
+ */
+ if (crypto_bignum_legendre(tmp2, prime) == check) {
+ if (found == 1)
+ continue;
+
+ /* need to unambiguously identify the solution */
+ is_odd = crypto_bignum_is_odd(rnd);
+
+ /*
+ * We know x_candidate is a quadratic residue so set
+ * it here.
+ */
+ if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
+ x_candidate,
+ is_odd) != 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Could not solve for y");
continue;
}
- if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
- wpa_printf(MSG_INFO, "EAP-pwd: point is at "
- "infinity");
+
+ /*
+ * If there's a solution to the equation then the point
+ * must be on the curve so why check again explicitly?
+ * OpenSSL code says this is required by X9.62. We're
+ * not X9.62 but it can't hurt just to be sure.
+ */
+ if (!crypto_ec_point_is_on_curve(grp->group,
+ grp->pwe)) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: point is not on curve");
continue;
}
+
+ if (!crypto_bignum_is_one(cofactor)) {
+ /* make sure the point is not in a small
+ * sub-group */
+ if (crypto_ec_point_mul(grp->group, grp->pwe,
+ cofactor,
+ grp->pwe) != 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: cannot multiply generator by order");
+ continue;
+ }
+ if (crypto_ec_point_is_at_infinity(grp->group,
+ grp->pwe)) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: point is at infinity");
+ continue;
+ }
+ }
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd: found a PWE in %d tries", ctr);
+ found = 1;
}
- /* if we got here then we have a new generator. */
- break;
}
- wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
- grp->group_num = num;
+ if (found == 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: unable to find random point on curve for group %d, something's fishy",
+ num);
+ goto fail;
+ }
if (0) {
fail:
- EC_GROUP_free(grp->group);
- grp->group = NULL;
- EC_POINT_clear_free(grp->pwe);
+ crypto_ec_point_deinit(grp->pwe, 1);
grp->pwe = NULL;
- BN_clear_free(grp->order);
- grp->order = NULL;
- BN_clear_free(grp->prime);
- grp->prime = NULL;
ret = 1;
}
/* cleanliness and order.... */
- BN_clear_free(cofactor);
- BN_clear_free(x_candidate);
- BN_clear_free(rnd);
+ crypto_bignum_deinit(cofactor, 1);
+ crypto_bignum_deinit(x_candidate, 1);
+ crypto_bignum_deinit(rnd, 1);
+ crypto_bignum_deinit(pm1, 0);
+ crypto_bignum_deinit(tmp1, 1);
+ crypto_bignum_deinit(tmp2, 1);
+ crypto_bignum_deinit(qr, 1);
+ crypto_bignum_deinit(qnr, 1);
+ crypto_bignum_deinit(one, 0);
os_free(prfbuf);
return ret;
}
-int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
- const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
+int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
+ const struct crypto_bignum *peer_scalar,
+ const struct crypto_bignum *server_scalar,
const u8 *confirm_peer, const u8 *confirm_server,
const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
{
struct crypto_hash *hash;
u8 mk[SHA256_MAC_LEN], *cruft;
u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
- int offset;
+ size_t prime_len, order_len;
+
+ prime_len = crypto_ec_prime_len(grp->group);
+ order_len = crypto_ec_order_len(grp->group);
- if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
+ cruft = os_malloc(prime_len);
+ if (!cruft)
return -1;
/*
@@ -328,14 +385,10 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
return -1;
}
eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
- offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
- os_memset(cruft, 0, BN_num_bytes(grp->prime));
- BN_bn2bin(peer_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
- offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
- os_memset(cruft, 0, BN_num_bytes(grp->prime));
- BN_bn2bin(server_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
+ crypto_bignum_to_bin(peer_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
+ crypto_bignum_to_bin(server_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
eap_pwd_h_final(hash, &session_id[1]);
/* then compute MK = H(k | confirm-peer | confirm-server) */
@@ -344,10 +397,8 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
os_free(cruft);
return -1;
}
- offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
- os_memset(cruft, 0, BN_num_bytes(grp->prime));
- BN_bn2bin(k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
+ crypto_bignum_to_bin(k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
os_free(cruft);
eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index a0d717edfe7a..6b07cf8f797c 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -9,20 +9,14 @@
#ifndef EAP_PWD_COMMON_H
#define EAP_PWD_COMMON_H
-#include <openssl/bn.h>
-#include <openssl/ec.h>
-#include <openssl/evp.h>
-
/*
* definition of a finite cyclic group
* TODO: support one based on a prime field
*/
typedef struct group_definition_ {
u16 group_num;
- EC_GROUP *group;
- EC_POINT *pwe;
- BIGNUM *order;
- BIGNUM *prime;
+ struct crypto_ec *group;
+ struct crypto_ec_point *pwe;
} EAP_PWD_group;
/*
@@ -52,17 +46,22 @@ struct eap_pwd_id {
u8 prep;
#define EAP_PWD_PREP_NONE 0
#define EAP_PWD_PREP_MS 1
+#define EAP_PWD_PREP_SSHA1 3
+#define EAP_PWD_PREP_SSHA256 4
+#define EAP_PWD_PREP_SSHA512 5
u8 identity[0]; /* length inferred from payload */
} STRUCT_PACKED;
/* common routines */
+EAP_PWD_group * get_eap_pwd_group(u16 num);
int compute_password_element(EAP_PWD_group *grp, u16 num,
const u8 *password, size_t password_len,
const u8 *id_server, size_t id_server_len,
const u8 *id_peer, size_t id_peer_len,
const u8 *token);
-int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
- const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
+int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
+ const struct crypto_bignum *peer_scalar,
+ const struct crypto_bignum *server_scalar,
const u8 *confirm_peer, const u8 *confirm_server,
const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id);
struct crypto_hash * eap_pwd_h_init(void);
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
index 2adc3b376a8e..6290c35f1a6b 100644
--- a/src/eap_common/eap_sim_common.c
+++ b/src/eap_common/eap_sim_common.c
@@ -175,7 +175,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
return -1;
- tmp = os_malloc(wpabuf_len(req));
+ tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
if (tmp == NULL)
return -1;
@@ -185,7 +185,6 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
len[1] = extra_len;
/* HMAC-SHA1-128 */
- os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
tmp, wpabuf_len(req));
@@ -370,7 +369,7 @@ int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
return -1;
- tmp = os_malloc(wpabuf_len(req));
+ tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
if (tmp == NULL)
return -1;
@@ -380,7 +379,6 @@ int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
len[1] = extra_len;
/* HMAC-SHA-256-128 */
- os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
tmp, wpabuf_len(req));
@@ -943,10 +941,9 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
return NULL;
}
- decrypted = os_malloc(encr_data_len);
+ decrypted = os_memdup(encr_data, encr_data_len);
if (decrypted == NULL)
return NULL;
- os_memcpy(decrypted, encr_data, encr_data_len);
if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
os_free(decrypted);
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 9110ca5b9cfd..974c475ff2d4 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -95,6 +95,14 @@ static void eap_notify_status(struct eap_sm *sm, const char *status,
}
+static void eap_report_error(struct eap_sm *sm, int error_code)
+{
+ wpa_printf(MSG_DEBUG, "EAP: Error notification: %d", error_code);
+ if (sm->eapol_cb->notify_eap_error)
+ sm->eapol_cb->notify_eap_error(sm->eapol_ctx, error_code);
+}
+
+
static void eap_sm_free_key(struct eap_sm *sm)
{
if (sm->eapKeyData) {
@@ -121,15 +129,17 @@ static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
/**
- * eap_allowed_method - Check whether EAP method is allowed
+ * eap_config_allowed_method - Check whether EAP method is allowed
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @config: EAP configuration
* @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
* @method: EAP type
* Returns: 1 = allowed EAP method, 0 = not allowed
*/
-int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+static int eap_config_allowed_method(struct eap_sm *sm,
+ struct eap_peer_config *config,
+ int vendor, u32 method)
{
- struct eap_peer_config *config = eap_get_config(sm);
int i;
struct eap_method_type *m;
@@ -146,6 +156,57 @@ int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
}
+/**
+ * eap_allowed_method - Check whether EAP method is allowed
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
+ * @method: EAP type
+ * Returns: 1 = allowed EAP method, 0 = not allowed
+ */
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+{
+ return eap_config_allowed_method(sm, eap_get_config(sm), vendor,
+ method);
+}
+
+
+#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
+static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
+ size_t max_len, size_t *imsi_len,
+ int mnc_len)
+{
+ char *pos, mnc[4];
+
+ if (*imsi_len + 36 > max_len) {
+ wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
+ return -1;
+ }
+
+ if (mnc_len != 2 && mnc_len != 3)
+ mnc_len = 3;
+
+ if (mnc_len == 2) {
+ mnc[0] = '0';
+ mnc[1] = imsi[3];
+ mnc[2] = imsi[4];
+ } else if (mnc_len == 3) {
+ mnc[0] = imsi[3];
+ mnc[1] = imsi[4];
+ mnc[2] = imsi[5];
+ }
+ mnc[3] = '\0';
+
+ pos = imsi + *imsi_len;
+ pos += os_snprintf(pos, imsi + max_len - pos,
+ "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
+ mnc, imsi[0], imsi[1], imsi[2]);
+ *imsi_len = pos - imsi;
+
+ return 0;
+}
+#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
+
+
/*
* This state initializes state machine variables when the machine is
* activated (portEnabled = TRUE). This is also used when re-starting
@@ -371,9 +432,8 @@ nak:
#ifdef CONFIG_ERP
-static char * eap_home_realm(struct eap_sm *sm)
+static char * eap_get_realm(struct eap_sm *sm, struct eap_peer_config *config)
{
- struct eap_peer_config *config = eap_get_config(sm);
char *realm;
size_t i, realm_len;
@@ -413,7 +473,51 @@ static char * eap_home_realm(struct eap_sm *sm)
}
}
- return os_strdup("");
+#ifdef CONFIG_EAP_PROXY
+ /* When identity is not provided in the config, build the realm from
+ * IMSI for eap_proxy based methods.
+ */
+ if (!config->identity && !config->anonymous_identity &&
+ sm->eapol_cb->get_imsi &&
+ (eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+ EAP_TYPE_SIM) ||
+ eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+ EAP_TYPE_AKA) ||
+ eap_config_allowed_method(sm, config, EAP_VENDOR_IETF,
+ EAP_TYPE_AKA_PRIME))) {
+ char imsi[100];
+ size_t imsi_len;
+ int mnc_len, pos;
+
+ wpa_printf(MSG_DEBUG, "EAP: Build realm from IMSI (eap_proxy)");
+ mnc_len = sm->eapol_cb->get_imsi(sm->eapol_ctx, config->sim_num,
+ imsi, &imsi_len);
+ if (mnc_len < 0)
+ return NULL;
+
+ pos = imsi_len + 1; /* points to the beginning of the realm */
+ if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len,
+ mnc_len) < 0) {
+ wpa_printf(MSG_WARNING, "Could not append realm");
+ return NULL;
+ }
+
+ realm = os_strdup(&imsi[pos]);
+ if (!realm)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP: Generated realm '%s'", realm);
+ return realm;
+ }
+#endif /* CONFIG_EAP_PROXY */
+
+ return NULL;
+}
+
+
+static char * eap_home_realm(struct eap_sm *sm)
+{
+ return eap_get_realm(sm, eap_get_config(sm));
}
@@ -469,6 +573,89 @@ static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm)
}
}
+
+int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 next_seq_num)
+{
+ struct eap_erp_key *erp;
+ char *home_realm;
+
+ home_realm = eap_home_realm(sm);
+ if (!home_realm || os_strlen(home_realm) == 0) {
+ os_free(home_realm);
+ return -1;
+ }
+
+ erp = eap_erp_get_key(sm, home_realm);
+ if (!erp) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Failed to find ERP key for realm: %s",
+ home_realm);
+ os_free(home_realm);
+ return -1;
+ }
+
+ if ((u32) next_seq_num < erp->next_seq) {
+ /* Sequence number has wrapped around, clear this ERP
+ * info and do a full auth next time.
+ */
+ eap_peer_erp_free_key(erp);
+ } else {
+ erp->next_seq = (u32) next_seq_num;
+ }
+
+ os_free(home_realm);
+ return 0;
+}
+
+
+int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config,
+ const u8 **username, size_t *username_len,
+ const u8 **realm, size_t *realm_len,
+ u16 *erp_next_seq_num, const u8 **rrk,
+ size_t *rrk_len)
+{
+ struct eap_erp_key *erp;
+ char *home_realm;
+ char *pos;
+
+ if (config)
+ home_realm = eap_get_realm(sm, config);
+ else
+ home_realm = eap_home_realm(sm);
+ if (!home_realm || os_strlen(home_realm) == 0) {
+ os_free(home_realm);
+ return -1;
+ }
+
+ erp = eap_erp_get_key(sm, home_realm);
+ os_free(home_realm);
+ if (!erp)
+ return -1;
+
+ if (erp->next_seq >= 65536)
+ return -1; /* SEQ has range of 0..65535 */
+
+ pos = os_strchr(erp->keyname_nai, '@');
+ if (!pos)
+ return -1; /* this cannot really happen */
+ *username_len = pos - erp->keyname_nai;
+ *username = (u8 *) erp->keyname_nai;
+
+ pos++;
+ *realm_len = os_strlen(pos);
+ *realm = (u8 *) pos;
+
+ *erp_next_seq_num = (u16) erp->next_seq;
+
+ *rrk_len = erp->rRK_len;
+ *rrk = erp->rRK;
+
+ if (*username_len == 0 || *realm_len == 0 || *rrk_len == 0)
+ return -1;
+
+ return 0;
+}
+
#endif /* CONFIG_ERP */
@@ -483,13 +670,20 @@ void eap_peer_erp_free_keys(struct eap_sm *sm)
}
-static void eap_peer_erp_init(struct eap_sm *sm)
+/* Note: If ext_session and/or ext_emsk are passed to this function, they are
+ * expected to point to allocated memory and those allocations will be freed
+ * unconditionally. */
+void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id,
+ size_t ext_session_id_len, u8 *ext_emsk,
+ size_t ext_emsk_len)
{
#ifdef CONFIG_ERP
u8 *emsk = NULL;
size_t emsk_len = 0;
+ u8 *session_id = NULL;
+ size_t session_id_len = 0;
u8 EMSKname[EAP_EMSK_NAME_LEN];
- u8 len[2];
+ u8 len[2], ctx[3];
char *realm;
size_t realm_len, nai_buf_len;
struct eap_erp_key *erp = NULL;
@@ -497,7 +691,7 @@ static void eap_peer_erp_init(struct eap_sm *sm)
realm = eap_home_realm(sm);
if (!realm)
- return;
+ goto fail;
realm_len = os_strlen(realm);
wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm);
eap_erp_remove_keys_realm(sm, realm);
@@ -517,7 +711,13 @@ static void eap_peer_erp_init(struct eap_sm *sm)
if (erp == NULL)
goto fail;
- emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+ if (ext_emsk) {
+ emsk = ext_emsk;
+ emsk_len = ext_emsk_len;
+ } else {
+ emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+ }
+
if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
wpa_printf(MSG_DEBUG,
"EAP: No suitable EMSK available for ERP");
@@ -526,10 +726,23 @@ static void eap_peer_erp_init(struct eap_sm *sm)
wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
- WPA_PUT_BE16(len, 8);
- if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
- len, sizeof(len),
- EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+ if (ext_session_id) {
+ session_id = ext_session_id;
+ session_id_len = ext_session_id_len;
+ } else {
+ session_id = sm->eapSessionId;
+ session_id_len = sm->eapSessionIdLen;
+ }
+
+ if (!session_id || session_id_len == 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: No suitable session id available for ERP");
+ goto fail;
+ }
+
+ WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN);
+ if (hmac_sha256_kdf(session_id, session_id_len, "EMSK", len,
+ sizeof(len), EMSKname, EAP_EMSK_NAME_LEN) < 0) {
wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
goto fail;
}
@@ -550,9 +763,11 @@ static void eap_peer_erp_init(struct eap_sm *sm)
erp->rRK_len = emsk_len;
wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+ ctx[0] = EAP_ERP_CS_HMAC_SHA256_128;
+ WPA_PUT_BE16(&ctx[1], erp->rRK_len);
if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
- "EAP Re-authentication Integrity Key@ietf.org",
- len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+ "Re-authentication Integrity Key@ietf.org",
+ ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) {
wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
goto fail;
}
@@ -563,7 +778,11 @@ static void eap_peer_erp_init(struct eap_sm *sm)
dl_list_add(&sm->erp_keys, &erp->list);
erp = NULL;
fail:
- bin_clear_free(emsk, emsk_len);
+ if (ext_emsk)
+ bin_clear_free(ext_emsk, ext_emsk_len);
+ else
+ bin_clear_free(emsk, emsk_len);
+ bin_clear_free(ext_session_id, ext_session_id_len);
bin_clear_free(erp, sizeof(*erp));
os_free(realm);
#endif /* CONFIG_ERP */
@@ -571,8 +790,7 @@ fail:
#ifdef CONFIG_ERP
-static int eap_peer_erp_reauth_start(struct eap_sm *sm,
- const struct eap_hdr *hdr, size_t len)
+struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id)
{
char *realm;
struct eap_erp_key *erp;
@@ -581,16 +799,16 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm,
realm = eap_home_realm(sm);
if (!realm)
- return -1;
+ return NULL;
erp = eap_erp_get_key(sm, realm);
os_free(realm);
realm = NULL;
if (!erp)
- return -1;
+ return NULL;
if (erp->next_seq >= 65536)
- return -1; /* SEQ has range of 0..65535 */
+ return NULL; /* SEQ has range of 0..65535 */
/* TODO: check rRK lifetime expiration */
@@ -599,9 +817,9 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm,
msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
- EAP_CODE_INITIATE, hdr->identifier);
+ EAP_CODE_INITIATE, eap_id);
if (msg == NULL)
- return -1;
+ return NULL;
wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */
wpabuf_put_be16(msg, erp->next_seq);
@@ -615,13 +833,28 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm,
if (hmac_sha256(erp->rIK, erp->rIK_len,
wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
wpabuf_free(msg);
- return -1;
+ return NULL;
}
wpabuf_put_data(msg, hash, 16);
- wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
sm->erp_seq = erp->next_seq;
erp->next_seq++;
+
+ wpa_hexdump_buf(MSG_DEBUG, "ERP: EAP-Initiate/Re-auth", msg);
+
+ return msg;
+}
+
+
+static int eap_peer_erp_reauth_start(struct eap_sm *sm, u8 eap_id)
+{
+ struct wpabuf *msg;
+
+ msg = eap_peer_build_erp_reauth_start(sm, eap_id);
+ if (!msg)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
wpabuf_free(sm->eapRespData);
sm->eapRespData = msg;
sm->reauthInit = TRUE;
@@ -691,8 +924,6 @@ SM_STATE(EAP, METHOD)
if (sm->m->isKeyAvailable && sm->m->getKey &&
sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
- struct eap_peer_config *config = eap_get_config(sm);
-
eap_sm_free_key(sm);
sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
&sm->eapKeyDataLen);
@@ -705,8 +936,6 @@ SM_STATE(EAP, METHOD)
wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
sm->eapSessionId, sm->eapSessionIdLen);
}
- if (config->erp && sm->m->get_emsk && sm->eapSessionId)
- eap_peer_erp_init(sm);
}
}
@@ -804,6 +1033,8 @@ SM_STATE(EAP, RETRANSMIT)
*/
SM_STATE(EAP, SUCCESS)
{
+ struct eap_peer_config *config = eap_get_config(sm);
+
SM_ENTRY(EAP, SUCCESS);
if (sm->eapKeyData != NULL)
sm->eapKeyAvailable = TRUE;
@@ -826,6 +1057,11 @@ SM_STATE(EAP, SUCCESS)
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
"EAP authentication completed successfully");
+
+ if (config->erp && sm->m->get_emsk && sm->eapSessionId &&
+ sm->m->isKeyAvailable &&
+ sm->m->isKeyAvailable(sm, sm->eap_method_priv))
+ eap_peer_erp_init(sm, NULL, 0, NULL, 0);
}
@@ -1276,48 +1512,6 @@ static int mnc_len_from_imsi(const char *imsi)
}
-static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
- size_t max_len, size_t *imsi_len)
-{
- int mnc_len;
- char *pos, mnc[4];
-
- if (*imsi_len + 36 > max_len) {
- wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
- return -1;
- }
-
- /* MNC (2 or 3 digits) */
- mnc_len = scard_get_mnc_len(sm->scard_ctx);
- if (mnc_len < 0)
- mnc_len = mnc_len_from_imsi(imsi);
- if (mnc_len < 0) {
- wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
- "assuming 3");
- mnc_len = 3;
- }
-
- if (mnc_len == 2) {
- mnc[0] = '0';
- mnc[1] = imsi[3];
- mnc[2] = imsi[4];
- } else if (mnc_len == 3) {
- mnc[0] = imsi[3];
- mnc[1] = imsi[4];
- mnc[2] = imsi[5];
- }
- mnc[3] = '\0';
-
- pos = imsi + *imsi_len;
- pos += os_snprintf(pos, imsi + max_len - pos,
- "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
- mnc, imsi[0], imsi[1], imsi[2]);
- *imsi_len = pos - imsi;
-
- return 0;
-}
-
-
static int eap_sm_imsi_identity(struct eap_sm *sm,
struct eap_peer_config *conf)
{
@@ -1325,7 +1519,7 @@ static int eap_sm_imsi_identity(struct eap_sm *sm,
char imsi[100];
size_t imsi_len;
struct eap_method_type *m = conf->eap_methods;
- int i;
+ int i, mnc_len;
imsi_len = sizeof(imsi);
if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
@@ -1340,7 +1534,18 @@ static int eap_sm_imsi_identity(struct eap_sm *sm,
return -1;
}
- if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) {
+ /* MNC (2 or 3 digits) */
+ mnc_len = scard_get_mnc_len(sm->scard_ctx);
+ if (mnc_len < 0)
+ mnc_len = mnc_len_from_imsi(imsi);
+ if (mnc_len < 0) {
+ wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
+ "assuming 3");
+ mnc_len = 3;
+ }
+
+ if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len,
+ mnc_len) < 0) {
wpa_printf(MSG_WARNING, "Could not add realm to SIM identity");
return -1;
}
@@ -1566,7 +1771,7 @@ static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr,
/* TODO: Derivation of domain specific keys for local ER */
}
- if (eap_peer_erp_reauth_start(sm, hdr, len) == 0)
+ if (eap_peer_erp_reauth_start(sm, hdr->identifier) == 0)
return;
invalid:
@@ -1577,8 +1782,7 @@ invalid:
}
-static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr,
- size_t len)
+void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len)
{
#ifdef CONFIG_ERP
const u8 *pos = (const u8 *) (hdr + 1);
@@ -1828,6 +2032,15 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
case EAP_CODE_FAILURE:
wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
eap_notify_status(sm, "completion", "failure");
+
+ /* Get the error code from method */
+ if (sm->m && sm->m->get_error_code) {
+ int error_code;
+
+ error_code = sm->m->get_error_code(sm->eap_method_priv);
+ if (error_code != NO_EAP_METHOD_ERROR)
+ eap_report_error(sm, error_code);
+ }
sm->rxFailure = TRUE;
break;
case EAP_CODE_INITIATE:
@@ -2231,6 +2444,7 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
config->pending_req_passphrase++;
break;
case WPA_CTRL_REQ_SIM:
+ config->pending_req_sim++;
txt = msg;
break;
case WPA_CTRL_REQ_EXT_CERT_CHECK:
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index 1a645af8b200..d0837e37a0e0 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -246,12 +246,37 @@ struct eapol_callbacks {
void (*notify_status)(void *ctx, const char *status,
const char *parameter);
+ /**
+ * notify_eap_error - Report EAP method error code
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @error_code: Error code from the used EAP method
+ */
+ void (*notify_eap_error)(void *ctx, int error_code);
+
#ifdef CONFIG_EAP_PROXY
/**
* eap_proxy_cb - Callback signifying any updates from eap_proxy
* @ctx: eapol_ctx from eap_peer_sm_init() call
*/
void (*eap_proxy_cb)(void *ctx);
+
+ /**
+ * eap_proxy_notify_sim_status - Notification of SIM status change
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @sim_state: One of enum value from sim_state
+ */
+ void (*eap_proxy_notify_sim_status)(void *ctx,
+ enum eap_proxy_sim_state sim_state);
+
+ /**
+ * get_imsi - Get the IMSI value from eap_proxy
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @sim_num: SIM/USIM number to get the IMSI value for
+ * @imsi: Buffer for IMSI value
+ * @len: Buffer for returning IMSI length in octets
+ * Returns: MNC length (2 or 3) or -1 on error
+ */
+ int (*get_imsi)(void *ctx, int sim_num, char *imsi, size_t *len);
#endif /* CONFIG_EAP_PROXY */
/**
@@ -348,6 +373,16 @@ void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
int eap_peer_was_failure_expected(struct eap_sm *sm);
void eap_peer_erp_free_keys(struct eap_sm *sm);
+struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id);
+void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len);
+int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config,
+ const u8 **username, size_t *username_len,
+ const u8 **realm, size_t *realm_len, u16 *erp_seq_num,
+ const u8 **rrk, size_t *rrk_len);
+int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 seq_num);
+void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id,
+ size_t ext_session_id_len, u8 *ext_emsk,
+ size_t ext_emsk_len);
#endif /* IEEE8021X_EAPOL */
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 0bac62dee523..a4441413f0dd 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -48,11 +48,15 @@ struct eap_aka_data {
struct wpabuf *id_msgs;
int prev_id;
int result_ind, use_result_ind;
+ int use_pseudonym;
u8 eap_method;
u8 *network_name;
size_t network_name_len;
u16 kdf;
int kdf_negotiation;
+ u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX];
+ size_t last_kdf_count;
+ int error_code;
};
@@ -96,12 +100,16 @@ static void * eap_aka_init(struct eap_sm *sm)
data->eap_method = EAP_TYPE_AKA;
+ /* Zero is a valid error code, so we need to initialize */
+ data->error_code = NO_EAP_METHOD_ERROR;
+
eap_aka_state(data, CONTINUE);
data->prev_id = -1;
data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
- if (config && config->anonymous_identity) {
+ data->use_pseudonym = !sm->init_phase2;
+ if (config && config->anonymous_identity && data->use_pseudonym) {
data->pseudonym = os_malloc(config->anonymous_identity_len);
if (data->pseudonym) {
os_memcpy(data->pseudonym, config->anonymous_identity,
@@ -350,7 +358,8 @@ static void eap_aka_clear_identities(struct eap_sm *sm,
os_free(data->pseudonym);
data->pseudonym = NULL;
data->pseudonym_len = 0;
- eap_set_anon_id(sm, NULL, 0);
+ if (data->use_pseudonym)
+ eap_set_anon_id(sm, NULL, 0);
}
if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
@@ -405,20 +414,21 @@ static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data,
realm, realm_len);
}
data->pseudonym_len = attr->next_pseudonym_len + realm_len;
- eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
+ if (data->use_pseudonym)
+ eap_set_anon_id(sm, data->pseudonym,
+ data->pseudonym_len);
}
if (attr->next_reauth_id) {
os_free(data->reauth_id);
- data->reauth_id = os_malloc(attr->next_reauth_id_len);
+ data->reauth_id = os_memdup(attr->next_reauth_id,
+ attr->next_reauth_id_len);
if (data->reauth_id == NULL) {
wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
"next reauth_id");
data->reauth_id_len = 0;
return -1;
}
- os_memcpy(data->reauth_id, attr->next_reauth_id,
- attr->next_reauth_id_len);
data->reauth_id_len = attr->next_reauth_id_len;
wpa_hexdump_ascii(MSG_DEBUG,
"EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
@@ -570,7 +580,7 @@ static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
static struct wpabuf * eap_aka_synchronization_failure(
- struct eap_aka_data *data, u8 id)
+ struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr)
{
struct eap_sim_msg *msg;
@@ -584,6 +594,15 @@ static struct wpabuf * eap_aka_synchronization_failure(
wpa_printf(MSG_DEBUG, " AT_AUTS");
eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
EAP_AKA_AUTS_LEN);
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ size_t i;
+
+ for (i = 0; i < attr->kdf_count; i++) {
+ wpa_printf(MSG_DEBUG, " AT_KDF");
+ eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i],
+ NULL, 0);
+ }
+ }
return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
}
@@ -817,9 +836,13 @@ static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data,
size_t i;
for (i = 0; i < attr->kdf_count; i++) {
- if (attr->kdf[i] == EAP_AKA_PRIME_KDF)
+ if (attr->kdf[i] == EAP_AKA_PRIME_KDF) {
+ os_memcpy(data->last_kdf_attrs, attr->kdf,
+ sizeof(u16) * attr->kdf_count);
+ data->last_kdf_count = attr->kdf_count;
return eap_aka_prime_kdf_select(data, id,
EAP_AKA_PRIME_KDF);
+ }
}
/* No matching KDF found - fail authentication as if AUTN had been
@@ -840,26 +863,32 @@ static int eap_aka_prime_kdf_valid(struct eap_aka_data *data,
* of the selected KDF into the beginning of the list. */
if (data->kdf_negotiation) {
+ /* When the peer receives the new EAP-Request/AKA'-Challenge
+ * message, must check only requested change occurred in the
+ * list of AT_KDF attributes. If there are any other changes,
+ * the peer must behave like the case that AT_MAC had been
+ * incorrect and authentication is failed. These are defined in
+ * EAP-AKA' specification RFC 5448, Section 3.2. */
if (attr->kdf[0] != data->kdf) {
wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
"accept the selected KDF");
- return 0;
+ return -1;
}
- for (i = 1; i < attr->kdf_count; i++) {
- if (attr->kdf[i] == data->kdf)
- break;
- }
- if (i == attr->kdf_count &&
- attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) {
- wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
- "duplicate the selected KDF");
- return 0;
+ if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX ||
+ attr->kdf_count != data->last_kdf_count + 1) {
+ wpa_printf(MSG_WARNING,
+ "EAP-AKA': The length of KDF attributes is wrong");
+ return -1;
}
- /* TODO: should check that the list is identical to the one
- * used in the previous Challenge message apart from the added
- * entry in the beginning. */
+ for (i = 1; i < attr->kdf_count; i++) {
+ if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) {
+ wpa_printf(MSG_WARNING,
+ "EAP-AKA': The KDF attributes except selected KDF are not same as original one");
+ return -1;
+ }
+ }
}
for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
@@ -908,22 +937,25 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
return eap_aka_authentication_reject(data, id);
}
os_free(data->network_name);
- data->network_name = os_malloc(attr->kdf_input_len);
+ data->network_name = os_memdup(attr->kdf_input,
+ attr->kdf_input_len);
if (data->network_name == NULL) {
wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
"storing Network Name");
return eap_aka_authentication_reject(data, id);
}
- os_memcpy(data->network_name, attr->kdf_input,
- attr->kdf_input_len);
data->network_name_len = attr->kdf_input_len;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
"(AT_KDF_INPUT)",
data->network_name, data->network_name_len);
/* TODO: check Network Name per 3GPP.33.402 */
- if (!eap_aka_prime_kdf_valid(data, attr))
+ res = eap_aka_prime_kdf_valid(data, attr);
+ if (res == 0)
return eap_aka_authentication_reject(data, id);
+ else if (res == -1)
+ return eap_aka_client_error(
+ data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
return eap_aka_prime_kdf_neg(data, id, attr);
@@ -966,7 +998,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
} else if (res == -2) {
wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
"failed (AUTN seq# -> AUTS)");
- return eap_aka_synchronization_failure(data, id);
+ return eap_aka_synchronization_failure(data, id, attr);
} else if (res > 0) {
wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing");
return NULL;
@@ -997,8 +1029,17 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
} else if (data->pseudonym) {
identity = data->pseudonym;
identity_len = data->pseudonym_len;
- } else
- identity = eap_get_config_identity(sm, &identity_len);
+ } else {
+ struct eap_peer_config *config;
+
+ config = eap_get_config(sm);
+ if (config && config->imsi_identity) {
+ identity = config->imsi_identity;
+ identity_len = config->imsi_identity_len;
+ } else {
+ identity = eap_get_config_identity(sm, &identity_len);
+ }
+ }
wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
"derivation", identity, identity_len);
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
@@ -1143,6 +1184,7 @@ static struct wpabuf * eap_aka_process_notification(
eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
if (attr->notification >= 0 && attr->notification < 32768) {
+ data->error_code = attr->notification;
eap_aka_state(data, FAILURE);
} else if (attr->notification == EAP_SIM_SUCCESS &&
data->state == RESULT_SUCCESS)
@@ -1437,12 +1479,11 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
if (key == NULL)
return NULL;
*len = EAP_SIM_KEYING_DATA_LEN;
- os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
return key;
}
@@ -1478,17 +1519,33 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
return key;
}
+static int eap_aka_get_error_code(void *priv)
+{
+ struct eap_aka_data *data = priv;
+ int current_data_error;
+
+ if (!data)
+ return NO_EAP_METHOD_ERROR;
+
+ current_data_error = data->error_code;
+
+ /* Now reset for next transaction */
+ data->error_code = NO_EAP_METHOD_ERROR;
+
+ return current_data_error;
+}
+
+
int eap_peer_aka_register(void)
{
struct eap_method *eap;
@@ -1509,6 +1566,7 @@ int eap_peer_aka_register(void)
eap->init_for_reauth = eap_aka_init_for_reauth;
eap->get_identity = eap_aka_get_identity;
eap->get_emsk = eap_aka_get_emsk;
+ eap->get_error_code = eap_aka_get_error_code;
return eap_peer_method_register(eap);
}
@@ -1536,6 +1594,7 @@ int eap_peer_aka_prime_register(void)
eap->init_for_reauth = eap_aka_init_for_reauth;
eap->get_identity = eap_aka_get_identity;
eap->get_emsk = eap_aka_get_emsk;
+ eap->get_error_code = eap_aka_get_error_code;
return eap_peer_method_register(eap);
}
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index f98007263b33..d416afd56d59 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -46,6 +46,9 @@ struct eap_peer_config {
*/
size_t anonymous_identity_len;
+ u8 *imsi_identity;
+ size_t imsi_identity_len;
+
/**
* password - Password string for EAP
*
@@ -628,6 +631,15 @@ struct eap_peer_config {
int pending_req_passphrase;
/**
+ * pending_req_sim - Pending SIM request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_sim;
+
+ /**
* pending_req_otp - Whether there is a pending OTP request
*
* This field should not be set in configuration step. It is only used
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index f899f653fdca..0de7d6cbf49b 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -85,12 +85,11 @@ static void * eap_eke_init(struct eap_sm *sm)
identity = eap_get_config_identity(sm, &identity_len);
if (identity) {
- data->peerid = os_malloc(identity_len);
+ data->peerid = os_memdup(identity, identity_len);
if (data->peerid == NULL) {
eap_eke_deinit(sm, data);
return NULL;
}
- os_memcpy(data->peerid, identity, identity_len);
data->peerid_len = identity_len;
}
@@ -310,12 +309,11 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data,
wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity",
pos, end - pos);
os_free(data->serverid);
- data->serverid = os_malloc(end - pos);
+ data->serverid = os_memdup(pos, end - pos);
if (data->serverid == NULL) {
return eap_eke_build_fail(data, ret, id,
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
}
- os_memcpy(data->serverid, pos, end - pos);
data->serverid_len = end - pos;
wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response");
@@ -717,10 +715,9 @@ static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -735,10 +732,9 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index 964ebe74fede..74cec7dd583f 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -484,7 +484,8 @@ static int eap_fast_phase2_request(struct eap_sm *sm,
if (*resp == NULL && config &&
(config->pending_req_identity || config->pending_req_password ||
- config->pending_req_otp || config->pending_req_new_password)) {
+ config->pending_req_otp || config->pending_req_new_password ||
+ config->pending_req_sim)) {
wpabuf_free(data->pending_phase2_req);
data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
} else if (*resp == NULL)
@@ -1677,6 +1678,10 @@ static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_fast_data *data = priv;
+
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->deinit_for_reauth)
+ data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
os_free(data->key_block_p);
data->key_block_p = NULL;
wpabuf_free(data->pending_phase2_req);
@@ -1744,12 +1749,11 @@ static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (!data->success)
return NULL;
- key = os_malloc(EAP_FAST_KEY_LEN);
+ key = os_memdup(data->key_data, EAP_FAST_KEY_LEN);
if (key == NULL)
return NULL;
*len = EAP_FAST_KEY_LEN;
- os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
return key;
}
@@ -1763,12 +1767,11 @@ static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
if (!data->success || !data->session_id)
return NULL;
- id = os_malloc(data->id_len);
+ id = os_memdup(data->session_id, data->id_len);
if (id == NULL)
return NULL;
*len = data->id_len;
- os_memcpy(id, data->session_id, data->id_len);
return id;
}
@@ -1782,12 +1785,11 @@ static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (!data->success)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
return key;
}
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index c81586035513..7d674c8c0a70 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -114,10 +114,9 @@ static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
const u8 *src, size_t src_len)
{
if (src) {
- *dst = os_malloc(src_len);
+ *dst = os_memdup(src, src_len);
if (*dst == NULL)
return -1;
- os_memcpy(*dst, src, src_len);
*dst_len = src_len;
}
return 0;
@@ -720,19 +719,17 @@ static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
if (type == PAC_TYPE_A_ID) {
os_free(pac->a_id);
- pac->a_id = os_malloc(len);
+ pac->a_id = os_memdup(pos, len);
if (pac->a_id == NULL)
break;
- os_memcpy(pac->a_id, pos, len);
pac->a_id_len = len;
}
if (type == PAC_TYPE_A_ID_INFO) {
os_free(pac->a_id_info);
- pac->a_id_info = os_malloc(len);
+ pac->a_id_info = os_memdup(pos, len);
if (pac->a_id_info == NULL)
break;
- os_memcpy(pac->a_id_info, pos, len);
pac->a_id_info_len = len;
}
@@ -820,10 +817,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
if (val > end - pos)
goto parse_fail;
pac->pac_opaque_len = val;
- pac->pac_opaque = os_malloc(pac->pac_opaque_len);
+ pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
if (pac->pac_opaque == NULL)
goto parse_fail;
- os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
pos += pac->pac_opaque_len;
if (2 > end - pos)
goto parse_fail;
@@ -832,10 +828,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
if (val > end - pos)
goto parse_fail;
pac->pac_info_len = val;
- pac->pac_info = os_malloc(pac->pac_info_len);
+ pac->pac_info = os_memdup(pos, pac->pac_info_len);
if (pac->pac_info == NULL)
goto parse_fail;
- os_memcpy(pac->pac_info, pos, pac->pac_info_len);
pos += pac->pac_info_len;
eap_fast_pac_get_a_id(pac);
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
index 177cbccf5850..f9c4d3773bf7 100644
--- a/src/eap_peer/eap_gpsk.c
+++ b/src/eap_peer/eap_gpsk.c
@@ -96,12 +96,11 @@ static void * eap_gpsk_init(struct eap_sm *sm)
identity = eap_get_config_identity(sm, &identity_len);
if (identity) {
- data->id_peer = os_malloc(identity_len);
+ data->id_peer = os_memdup(identity, identity_len);
if (data->id_peer == NULL) {
eap_gpsk_deinit(sm, data);
return NULL;
}
- os_memcpy(data->id_peer, identity, identity_len);
data->id_peer_len = identity_len;
}
@@ -117,12 +116,11 @@ static void * eap_gpsk_init(struct eap_sm *sm)
}
}
- data->psk = os_malloc(password_len);
+ data->psk = os_memdup(password, password_len);
if (data->psk == NULL) {
eap_gpsk_deinit(sm, data);
return NULL;
}
- os_memcpy(data->psk, password, password_len);
data->psk_len = password_len;
return data;
@@ -158,12 +156,11 @@ static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
return NULL;
}
os_free(data->id_server);
- data->id_server = os_malloc(alen);
+ data->id_server = os_memdup(pos, alen);
if (data->id_server == NULL) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
return NULL;
}
- os_memcpy(data->id_server, pos, alen);
data->id_server_len = alen;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
data->id_server, data->id_server_len);
@@ -722,10 +719,9 @@ static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -740,10 +736,9 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
@@ -758,10 +753,9 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- sid = os_malloc(data->id_len);
+ sid = os_memdup(data->session_id, data->id_len);
if (sid == NULL)
return NULL;
- os_memcpy(sid, data->session_id, data->id_len);
*len = data->id_len;
return sid;
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 6ab24834d654..096f0f28efca 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -14,6 +14,8 @@
#include "eap_peer/eap.h"
#include "eap_common/eap_common.h"
+#define NO_EAP_METHOD_ERROR (-1)
+
/* RFC 4137 - EAP Peer state machine */
typedef enum {
@@ -206,6 +208,17 @@ struct eap_method {
const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
/**
+ * get_error_code - Get the latest EAP method error code
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: An int for the EAP method specific error code if exists or
+ * NO_EAP_METHOD_ERROR otherwise.
+ *
+ * This method is an optional handler that only EAP methods that need to
+ * report their error code need to implement.
+ */
+ int (*get_error_code)(void *priv);
+
+ /**
* free - Free EAP method data
* @method: Pointer to the method data registered with
* eap_peer_method_register().
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
index 390f0ec8cf4d..6ddf50835ade 100644
--- a/src/eap_peer/eap_ikev2.c
+++ b/src/eap_peer/eap_ikev2.c
@@ -83,18 +83,16 @@ static void * eap_ikev2_init(struct eap_sm *sm)
if (data->ikev2.key_pad == NULL)
goto failed;
data->ikev2.key_pad_len = 21;
- data->ikev2.IDr = os_malloc(identity_len);
+ data->ikev2.IDr = os_memdup(identity, identity_len);
if (data->ikev2.IDr == NULL)
goto failed;
- os_memcpy(data->ikev2.IDr, identity, identity_len);
data->ikev2.IDr_len = identity_len;
password = eap_get_config_password(sm, &password_len);
if (password) {
- data->ikev2.shared_secret = os_malloc(password_len);
+ data->ikev2.shared_secret = os_memdup(password, password_len);
if (data->ikev2.shared_secret == NULL)
goto failed;
- os_memcpy(data->ikev2.shared_secret, password, password_len);
data->ikev2.shared_secret_len = password_len;
}
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
index ff6fa4afd2f7..233b9eeb1f83 100644
--- a/src/eap_peer/eap_leap.c
+++ b/src/eap_peer/eap_leap.c
@@ -115,10 +115,14 @@ static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
wpabuf_put_u8(resp, 0); /* unused */
wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
- if (pwhash)
- challenge_response(challenge, password, rpos);
- else
- nt_challenge_response(challenge, password, password_len, rpos);
+ if ((pwhash && challenge_response(challenge, password, rpos)) ||
+ (!pwhash &&
+ nt_challenge_response(challenge, password, password_len, rpos))) {
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Failed to derive response");
+ ret->ignore = TRUE;
+ wpabuf_free(resp);
+ return NULL;
+ }
os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
rpos, LEAP_RESPONSE_LEN);
@@ -239,7 +243,10 @@ static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
return NULL;
}
}
- challenge_response(data->ap_challenge, pw_hash_hash, expected);
+ if (challenge_response(data->ap_challenge, pw_hash_hash, expected)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
ret->methodState = METHOD_DONE;
ret->allowNotifications = FALSE;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index ce2227d388ef..877495cf3ac7 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -109,23 +109,21 @@ static void * eap_mschapv2_init(struct eap_sm *sm)
return NULL;
if (sm->peer_challenge) {
- data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+ data->peer_challenge = os_memdup(sm->peer_challenge,
+ MSCHAPV2_CHAL_LEN);
if (data->peer_challenge == NULL) {
eap_mschapv2_deinit(sm, data);
return NULL;
}
- os_memcpy(data->peer_challenge, sm->peer_challenge,
- MSCHAPV2_CHAL_LEN);
}
if (sm->auth_challenge) {
- data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+ data->auth_challenge = os_memdup(sm->auth_challenge,
+ MSCHAPV2_CHAL_LEN);
if (data->auth_challenge == NULL) {
eap_mschapv2_deinit(sm, data);
return NULL;
}
- os_memcpy(data->auth_challenge, sm->auth_challenge,
- MSCHAPV2_CHAL_LEN);
}
data->phase2 = sm->init_phase2;
@@ -567,11 +565,11 @@ static struct wpabuf * eap_mschapv2_change_password(
if (pwhash) {
u8 new_password_hash[16];
if (nt_password_hash(new_password, new_password_len,
- new_password_hash))
+ new_password_hash) ||
+ nt_password_hash_encrypted_with_block(password,
+ new_password_hash,
+ cp->encr_hash))
goto fail;
- nt_password_hash_encrypted_with_block(password,
- new_password_hash,
- cp->encr_hash);
} else {
if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
new_password, new_password_len,
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
index a7012d2870cf..3cef1c8800a2 100644
--- a/src/eap_peer/eap_pax.c
+++ b/src/eap_peer/eap_pax.c
@@ -69,12 +69,11 @@ static void * eap_pax_init(struct eap_sm *sm)
return NULL;
data->state = PAX_INIT;
- data->cid = os_malloc(identity_len);
+ data->cid = os_memdup(identity, identity_len);
if (data->cid == NULL) {
eap_pax_deinit(sm, data);
return NULL;
}
- os_memcpy(data->cid, identity, identity_len);
data->cid_len = identity_len;
os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 45ba38168d4f..34075b1d9af6 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -726,7 +726,8 @@ static int eap_peap_phase2_request(struct eap_sm *sm,
if (*resp == NULL &&
(config->pending_req_identity || config->pending_req_password ||
- config->pending_req_otp || config->pending_req_new_password)) {
+ config->pending_req_otp || config->pending_req_new_password ||
+ config->pending_req_sim)) {
wpabuf_free(data->pending_phase2_req);
data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
}
@@ -1082,7 +1083,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
eap_peer_tls_derive_key(sm, &data->ssl, label,
EAP_TLS_KEY_LEN);
if (data->key_data) {
- wpa_hexdump_key(MSG_DEBUG,
+ wpa_hexdump_key(MSG_DEBUG,
"EAP-PEAP: Derived key",
data->key_data,
EAP_TLS_KEY_LEN);
@@ -1163,6 +1164,10 @@ static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_peap_data *data = priv;
+
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->deinit_for_reauth)
+ data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
wpabuf_free(data->pending_phase2_req);
data->pending_phase2_req = NULL;
wpabuf_free(data->pending_resp);
@@ -1267,12 +1272,11 @@ static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
if (data->session_id == NULL || !data->phase2_success)
return NULL;
- id = os_malloc(data->id_len);
+ id = os_memdup(data->session_id, data->id_len);
if (id == NULL)
return NULL;
*len = data->id_len;
- os_memcpy(id, data->session_id, data->id_len);
return id;
}
diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h
index 23cdbe698b3c..9d8e57026722 100644
--- a/src/eap_peer/eap_proxy.h
+++ b/src/eap_peer/eap_proxy.h
@@ -20,7 +20,7 @@ enum eap_proxy_status {
};
struct eap_proxy_sm *
-eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb,
void *msg_ctx);
void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy);
@@ -40,10 +40,16 @@ eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData,
int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
int verbose);
-int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
- size_t *imsi_len);
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num,
+ char *imsi_buf, size_t *imsi_len);
int eap_proxy_notify_config(struct eap_proxy_sm *sm,
struct eap_peer_config *config);
+u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len);
+
+u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len);
+
+void eap_proxy_sm_abort(struct eap_proxy_sm *sm);
+
#endif /* EAP_PROXY_H */
diff --git a/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c
index d84f01234ed5..2cc05c92cdfc 100644
--- a/src/eap_peer/eap_proxy_dummy.c
+++ b/src/eap_peer/eap_proxy_dummy.c
@@ -12,7 +12,7 @@
#include "eap_proxy.h"
struct eap_proxy_sm *
-eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb,
void *msg_ctx)
{
return NULL;
@@ -63,8 +63,8 @@ int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
}
-int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
- size_t *imsi_len)
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num,
+ char *imsi_buf, size_t *imsi_len)
{
return -1;
}
@@ -75,3 +75,20 @@ int eap_proxy_notify_config(struct eap_proxy_sm *sm,
{
return -1;
}
+
+
+u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len)
+{
+ return NULL;
+}
+
+
+u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len)
+{
+ return NULL;
+}
+
+
+void eap_proxy_sm_abort(struct eap_proxy_sm *sm)
+{
+}
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
index ac18c158ad8b..eea9430d2406 100644
--- a/src/eap_peer/eap_psk.c
+++ b/src/eap_peer/eap_psk.c
@@ -116,14 +116,13 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
os_free(data->id_s);
data->id_s_len = len - sizeof(*hdr1);
- data->id_s = os_malloc(data->id_s_len);
+ data->id_s = os_memdup(hdr1 + 1, data->id_s_len);
if (data->id_s == NULL) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
"ID_S (len=%lu)", (unsigned long) data->id_s_len);
ret->ignore = TRUE;
return NULL;
}
- os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
data->id_s, data->id_s_len);
@@ -273,13 +272,12 @@ static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data,
wpabuf_head(reqData), 5);
wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
- decrypted = os_malloc(left);
+ decrypted = os_memdup(msg, left);
if (decrypted == NULL) {
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return NULL;
}
- os_memcpy(decrypted, msg, left);
if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
wpabuf_head(reqData),
@@ -425,12 +423,11 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != PSK_DONE)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_MSK_LEN;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
return key;
}
@@ -466,12 +463,11 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != PSK_DONE)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
return key;
}
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index d2bc981cd06b..761c16af996a 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -9,8 +9,11 @@
#include "includes.h"
#include "common.h"
+#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/sha512.h"
#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
#include "eap_peer/eap_i.h"
#include "eap_common/eap_pwd_common.h"
@@ -28,6 +31,8 @@ struct eap_pwd_data {
size_t password_len;
int password_hash;
u16 group_num;
+ u8 prep;
+ u8 token[4];
EAP_PWD_group *grp;
struct wpabuf *inbuf;
@@ -36,18 +41,16 @@ struct eap_pwd_data {
size_t out_frag_pos;
size_t mtu;
- BIGNUM *k;
- BIGNUM *private_value;
- BIGNUM *server_scalar;
- BIGNUM *my_scalar;
- EC_POINT *my_element;
- EC_POINT *server_element;
+ struct crypto_bignum *k;
+ struct crypto_bignum *private_value;
+ struct crypto_bignum *server_scalar;
+ struct crypto_bignum *my_scalar;
+ struct crypto_ec_point *my_element;
+ struct crypto_ec_point *server_element;
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 session_id[1 + SHA256_MAC_LEN];
-
- BN_CTX *bnctx;
};
@@ -107,15 +110,8 @@ static void * eap_pwd_init(struct eap_sm *sm)
return NULL;
}
- if ((data->bnctx = BN_CTX_new()) == NULL) {
- wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
- os_free(data);
- return NULL;
- }
-
if ((data->id_peer = os_malloc(identity_len)) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
- BN_CTX_free(data->bnctx);
os_free(data);
return NULL;
}
@@ -125,7 +121,6 @@ static void * eap_pwd_init(struct eap_sm *sm)
if ((data->password = os_malloc(password_len)) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
- BN_CTX_free(data->bnctx);
bin_clear_free(data->id_peer, data->id_peer_len);
os_free(data);
return NULL;
@@ -152,21 +147,18 @@ static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
{
struct eap_pwd_data *data = priv;
- BN_clear_free(data->private_value);
- BN_clear_free(data->server_scalar);
- BN_clear_free(data->my_scalar);
- BN_clear_free(data->k);
- BN_CTX_free(data->bnctx);
- EC_POINT_clear_free(data->my_element);
- EC_POINT_clear_free(data->server_element);
+ crypto_bignum_deinit(data->private_value, 1);
+ crypto_bignum_deinit(data->server_scalar, 1);
+ crypto_bignum_deinit(data->my_scalar, 1);
+ crypto_bignum_deinit(data->k, 1);
+ crypto_ec_point_deinit(data->my_element, 1);
+ crypto_ec_point_deinit(data->server_element, 1);
bin_clear_free(data->id_peer, data->id_peer_len);
bin_clear_free(data->id_server, data->id_server_len);
bin_clear_free(data->password, data->password_len);
if (data->grp) {
- EC_GROUP_free(data->grp->group);
- EC_POINT_clear_free(data->grp->pwe);
- BN_clear_free(data->grp->order);
- BN_clear_free(data->grp->prime);
+ crypto_ec_deinit(data->grp->group);
+ crypto_ec_point_deinit(data->grp->pwe, 1);
os_free(data->grp);
}
wpabuf_free(data->inbuf);
@@ -183,11 +175,10 @@ static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -202,11 +193,10 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- id = os_malloc(1 + SHA256_MAC_LEN);
+ id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN);
if (id == NULL)
return NULL;
- os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
*len = 1 + SHA256_MAC_LEN;
return id;
@@ -220,10 +210,6 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
struct eap_pwd_id *id;
- const u8 *password;
- size_t password_len;
- u8 pwhashhash[16];
- int res;
if (data->state != PWD_ID_Req) {
ret->ignore = TRUE;
@@ -250,7 +236,10 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
}
if (id->prep != EAP_PWD_PREP_NONE &&
- id->prep != EAP_PWD_PREP_MS) {
+ id->prep != EAP_PWD_PREP_MS &&
+ id->prep != EAP_PWD_PREP_SSHA1 &&
+ id->prep != EAP_PWD_PREP_SSHA256 &&
+ id->prep != EAP_PWD_PREP_SSHA512) {
wpa_printf(MSG_DEBUG,
"EAP-PWD: Unsupported password pre-processing technique (Prep=%u)",
id->prep);
@@ -268,6 +257,15 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
data->group_num);
+ data->prep = id->prep;
+ os_memcpy(data->token, id->token, sizeof(id->token));
+
+ if (data->id_server || data->grp) {
+ wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
if (data->id_server == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
@@ -279,7 +277,7 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
data->id_server, data->id_server_len);
- data->grp = os_zalloc(sizeof(EAP_PWD_group));
+ data->grp = get_eap_pwd_group(data->group_num);
if (data->grp == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
"group");
@@ -287,13 +285,74 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
return;
}
- if (id->prep == EAP_PWD_PREP_MS) {
+ data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
+ data->id_peer_len);
+ if (data->outbuf == NULL) {
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ wpabuf_put_be16(data->outbuf, data->group_num);
+ wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
+ wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
+ wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
+ wpabuf_put_u8(data->outbuf, id->prep);
+ wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
+
+ eap_pwd_state(data, PWD_Commit_Req);
+}
+
+
+static void
+eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload, size_t payload_len)
+{
+ struct crypto_ec_point *K = NULL, *point = NULL;
+ struct crypto_bignum *mask = NULL, *cofactor = NULL;
+ const u8 *ptr = payload;
+ u8 *scalar = NULL, *element = NULL;
+ size_t prime_len, order_len;
+ const u8 *password;
+ size_t password_len;
+ u8 pwhashhash[16];
+ const u8 *salt_pwd[2];
+ size_t salt_pwd_len[2], exp_len;
+ u8 salt_len, salthashpwd[64]; /* 64 = SHA512_DIGEST_LENGTH */
+ int res;
+
+ if (data->state != PWD_Commit_Req) {
+ ret->ignore = TRUE;
+ goto fin;
+ }
+
+ if (!data->grp) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-PWD (client): uninitialized EAP-pwd group");
+ ret->ignore = TRUE;
+ goto fin;
+ }
+
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
+
+ switch (data->prep) {
+ case EAP_PWD_PREP_MS:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is MS");
#ifdef CONFIG_FIPS
wpa_printf(MSG_ERROR,
"EAP-PWD (peer): MS password hash not supported in FIPS mode");
eap_pwd_state(data, FAILURE);
return;
#else /* CONFIG_FIPS */
+ if (payload_len != 2 * prime_len + order_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) (2 * prime_len + order_len));
+ goto fin;
+ }
if (data->password_hash) {
res = hash_nt_password_hash(data->password, pwhashhash);
} else {
@@ -314,9 +373,139 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
password = pwhashhash;
password_len = sizeof(pwhashhash);
#endif /* CONFIG_FIPS */
- } else {
+ break;
+ case EAP_PWD_PREP_SSHA1:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is salted sha1");
+ if (payload_len < 1 || *ptr == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len");
+ goto fin;
+ }
+ salt_len = *ptr++;
+ exp_len = 1 + salt_len + 2 * prime_len + order_len;
+ if (payload_len != exp_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) exp_len);
+ goto fin;
+ }
+
+ /* salted-password = Hash(password | salt) */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password",
+ data->password, data->password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len);
+ salt_pwd[0] = data->password;
+ salt_pwd[1] = ptr;
+ salt_pwd_len[0] = data->password_len;
+ salt_pwd_len[1] = salt_len;
+ if (sha1_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0)
+ goto fin;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd: sha1 hashed %d byte salt with password",
+ (int) salt_len);
+ ptr += salt_len;
+ password = salthashpwd;
+ password_len = SHA1_MAC_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password",
+ password, password_len);
+ break;
+ case EAP_PWD_PREP_SSHA256:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is salted sha256");
+ if (payload_len < 1 || *ptr == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len");
+ goto fin;
+ }
+ salt_len = *ptr++;
+ exp_len = 1 + salt_len + 2 * prime_len + order_len;
+ if (payload_len != exp_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) exp_len);
+ goto fin;
+ }
+
+ /* salted-password = Hash(password | salt) */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password",
+ data->password, data->password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len);
+ salt_pwd[0] = data->password;
+ salt_pwd[1] = ptr;
+ salt_pwd_len[0] = data->password_len;
+ salt_pwd_len[1] = salt_len;
+ if (sha256_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0)
+ goto fin;
+
+ ptr += salt_len;
+ password = salthashpwd;
+ password_len = SHA256_MAC_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password",
+ password, password_len);
+ break;
+#ifdef CONFIG_SHA512
+ case EAP_PWD_PREP_SSHA512:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is salted sha512");
+ if (payload_len < 1 || *ptr == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len");
+ goto fin;
+ }
+ salt_len = *ptr++;
+ exp_len = 1 + salt_len + 2 * prime_len + order_len;
+ if (payload_len != exp_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) exp_len);
+ goto fin;
+ }
+
+ /* salted-password = Hash(password | salt) */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password",
+ data->password, data->password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len);
+ salt_pwd[0] = data->password;
+ salt_pwd[1] = ptr;
+ salt_pwd_len[0] = data->password_len;
+ salt_pwd_len[1] = salt_len;
+ if (sha512_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0)
+ goto fin;
+
+ ptr += salt_len;
+ password = salthashpwd;
+ password_len = SHA512_MAC_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password",
+ password, password_len);
+ break;
+#endif /* CONFIG_SHA512 */
+ case EAP_PWD_PREP_NONE:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is NONE");
+ if (data->password_hash) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-PWD: Unhashed password not available");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ if (payload_len != 2 * prime_len + order_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) (2 * prime_len + order_len));
+ goto fin;
+ }
password = data->password;
password_len = data->password_len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd: Unsupported password pre-processing technique (Prep=%u)",
+ data->prep);
+ eap_pwd_state(data, FAILURE);
+ return;
}
/* compute PWE */
@@ -324,8 +513,9 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
password, password_len,
data->id_server, data->id_server_len,
data->id_peer, data->id_peer_len,
- id->token);
+ data->token);
os_memset(pwhashhash, 0, sizeof(pwhashhash));
+ os_memset(salthashpwd, 0, sizeof(salthashpwd));
if (res) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
eap_pwd_state(data, FAILURE);
@@ -333,134 +523,86 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
}
wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
- BN_num_bits(data->grp->prime));
-
- data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
- data->id_peer_len);
- if (data->outbuf == NULL) {
- eap_pwd_state(data, FAILURE);
- return;
- }
- wpabuf_put_be16(data->outbuf, data->group_num);
- wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
- wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
- wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
- wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
- wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
-
- eap_pwd_state(data, PWD_Commit_Req);
-}
-
-
-static void
-eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
- struct eap_method_ret *ret,
- const struct wpabuf *reqData,
- const u8 *payload, size_t payload_len)
-{
- EC_POINT *K = NULL, *point = NULL;
- BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
- u16 offset;
- u8 *ptr, *scalar = NULL, *element = NULL;
- size_t prime_len, order_len;
-
- if (data->state != PWD_Commit_Req) {
- ret->ignore = TRUE;
- goto fin;
- }
-
- prime_len = BN_num_bytes(data->grp->prime);
- order_len = BN_num_bytes(data->grp->order);
-
- if (payload_len != 2 * prime_len + order_len) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
- (unsigned int) payload_len,
- (unsigned int) (2 * prime_len + order_len));
- goto fin;
- }
-
- if (((data->private_value = BN_new()) == NULL) ||
- ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
- ((cofactor = BN_new()) == NULL) ||
- ((data->my_scalar = BN_new()) == NULL) ||
- ((mask = BN_new()) == NULL)) {
+ (int) crypto_ec_prime_len_bits(data->grp->group));
+
+ data->private_value = crypto_bignum_init();
+ data->my_element = crypto_ec_point_init(data->grp->group);
+ cofactor = crypto_bignum_init();
+ data->my_scalar = crypto_bignum_init();
+ mask = crypto_bignum_init();
+ if (!data->private_value || !data->my_element || !cofactor ||
+ !data->my_scalar || !mask) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
goto fin;
}
- if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+ if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) {
wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
"for curve");
goto fin;
}
- if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
- BN_rand_range(mask, data->grp->order) != 1 ||
- BN_add(data->my_scalar, data->private_value, mask) != 1 ||
- BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
- data->bnctx) != 1) {
+ if (crypto_bignum_rand(data->private_value,
+ crypto_ec_get_order(data->grp->group)) < 0 ||
+ crypto_bignum_rand(mask,
+ crypto_ec_get_order(data->grp->group)) < 0 ||
+ crypto_bignum_add(data->private_value, mask,
+ data->my_scalar) < 0 ||
+ crypto_bignum_mod(data->my_scalar,
+ crypto_ec_get_order(data->grp->group),
+ data->my_scalar) < 0) {
wpa_printf(MSG_INFO,
"EAP-pwd (peer): unable to get randomness");
goto fin;
}
- if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
- data->grp->pwe, mask, data->bnctx)) {
+ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
+ data->my_element) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
"fail");
eap_pwd_state(data, FAILURE);
goto fin;
}
- if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
- {
+ if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
goto fin;
}
- if (((x = BN_new()) == NULL) ||
- ((y = BN_new()) == NULL)) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
- goto fin;
- }
-
/* process the request */
- if (((data->server_scalar = BN_new()) == NULL) ||
- ((data->k = BN_new()) == NULL) ||
- ((K = EC_POINT_new(data->grp->group)) == NULL) ||
- ((point = EC_POINT_new(data->grp->group)) == NULL) ||
- ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
- {
+ data->k = crypto_bignum_init();
+ K = crypto_ec_point_init(data->grp->group);
+ point = crypto_ec_point_init(data->grp->group);
+ if (!data->k || !K || !point) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
"fail");
goto fin;
}
/* element, x then y, followed by scalar */
- ptr = (u8 *) payload;
- BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
- ptr += BN_num_bytes(data->grp->prime);
- BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
- ptr += BN_num_bytes(data->grp->prime);
- BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
- if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
- data->server_element, x, y,
- data->bnctx)) {
+ data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+ if (!data->server_element) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
"fail");
goto fin;
}
+ ptr += prime_len * 2;
+ data->server_scalar = crypto_bignum_init_set(ptr, order_len);
+ if (!data->server_scalar) {
+ wpa_printf(MSG_INFO,
+ "EAP-PWD (peer): setting peer scalar fail");
+ goto fin;
+ }
/* check to ensure server's element is not in a small sub-group */
- if (BN_cmp(cofactor, BN_value_one())) {
- if (!EC_POINT_mul(data->grp->group, point, NULL,
- data->server_element, cofactor, NULL)) {
+ if (!crypto_bignum_is_one(cofactor)) {
+ if (crypto_ec_point_mul(data->grp->group, data->server_element,
+ cofactor, point) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
"server element by order!\n");
goto fin;
}
- if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+ if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
"is at infinity!\n");
goto fin;
@@ -468,21 +610,20 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
}
/* compute the shared key, k */
- if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
- data->server_scalar, data->bnctx)) ||
- (!EC_POINT_add(data->grp->group, K, K, data->server_element,
- data->bnctx)) ||
- (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
- data->bnctx))) {
+ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe,
+ data->server_scalar, K) < 0 ||
+ crypto_ec_point_add(data->grp->group, K, data->server_element,
+ K) < 0 ||
+ crypto_ec_point_mul(data->grp->group, K, data->private_value,
+ K) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
"fail");
goto fin;
}
/* ensure that the shared key isn't in a small sub-group */
- if (BN_cmp(cofactor, BN_value_one())) {
- if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
- NULL)) {
+ if (!crypto_bignum_is_one(cofactor)) {
+ if (crypto_ec_point_mul(data->grp->group, K, cofactor, K) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
"shared key point by order");
goto fin;
@@ -495,30 +636,22 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
* never going to happen it is a simple and safe check "just to be
* sure" so let's be safe.
*/
- if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+ if (crypto_ec_point_is_at_infinity(data->grp->group, K)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
"infinity!\n");
goto fin;
}
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
- NULL, data->bnctx)) {
+ if (crypto_ec_point_x(data->grp->group, K, data->k) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
"shared secret from point");
goto fin;
}
/* now do the response */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
- goto fin;
- }
-
- if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
- ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
- NULL)) {
+ scalar = os_zalloc(order_len);
+ element = os_zalloc(prime_len * 2);
+ if (!scalar || !element) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
goto fin;
}
@@ -528,36 +661,28 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
* sufficiently smaller than the prime or order might need pre-pending
* with zeros.
*/
- os_memset(scalar, 0, BN_num_bytes(data->grp->order));
- os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, scalar + offset);
-
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, element + offset);
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
-
- data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) +
- 2 * BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
+ element + prime_len) != 0) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
+ goto fin;
+ }
+
+ data->outbuf = wpabuf_alloc(order_len + 2 * prime_len);
if (data->outbuf == NULL)
goto fin;
/* we send the element as (x,y) follwed by the scalar */
- wpabuf_put_data(data->outbuf, element,
- 2 * BN_num_bytes(data->grp->prime));
- wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
+ wpabuf_put_data(data->outbuf, element, 2 * prime_len);
+ wpabuf_put_data(data->outbuf, scalar, order_len);
fin:
os_free(scalar);
os_free(element);
- BN_clear_free(x);
- BN_clear_free(y);
- BN_clear_free(mask);
- BN_clear_free(cofactor);
- EC_POINT_clear_free(K);
- EC_POINT_clear_free(point);
+ crypto_bignum_deinit(mask, 1);
+ crypto_bignum_deinit(cofactor, 1);
+ crypto_ec_point_deinit(K, 1);
+ crypto_ec_point_deinit(point, 1);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
else
@@ -571,12 +696,11 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
- BIGNUM *x = NULL, *y = NULL;
- struct crypto_hash *hash;
+ struct crypto_hash *hash = NULL;
u32 cs;
u16 grp;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
- int offset;
+ size_t prime_len = 0, order_len = 0;
if (data->state != PWD_Confirm_Req) {
ret->ignore = TRUE;
@@ -590,6 +714,9 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
}
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
+
/*
* first build up the ciphersuite which is group | random_function |
* prf
@@ -602,9 +729,9 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
ptr += sizeof(u8);
*ptr = EAP_PWD_DEFAULT_PRF;
- /* each component of the cruft will be at most as big as the prime */
- if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
- ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ /* each component of the point will be at most as big as the prime */
+ cruft = os_malloc(prime_len * 2);
+ if (!cruft) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
"fail");
goto fin;
@@ -622,65 +749,41 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
* zero the memory each time because this is mod prime math and some
* value may start with a few zeros and the previous one did not.
*/
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
- BN_bn2bin(data->k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
/* server element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->server_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->server_element,
+ cruft, cruft + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->server_scalar);
- BN_bn2bin(data->server_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* my element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+ cruft + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
-
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* my scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* the ciphersuite */
eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
/* random function fin */
eap_pwd_h_final(hash, conf);
+ hash = NULL;
ptr = (u8 *) payload;
if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
@@ -700,66 +803,43 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
/* k */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
- BN_bn2bin(data->k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
/* my element */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+ cruft + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
"assignment fail");
goto fin;
}
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* my scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* server element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->server_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->server_element,
+ cruft, cruft + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
"assignment fail");
goto fin;
}
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->server_scalar);
- BN_bn2bin(data->server_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* the ciphersuite */
eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
/* all done */
eap_pwd_h_final(hash, conf);
+ hash = NULL;
- if (compute_keys(data->grp, data->bnctx, data->k,
+ if (compute_keys(data->grp, data->k,
data->my_scalar, data->server_scalar, conf, ptr,
&cs, data->msk, data->emsk, data->session_id) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
@@ -774,10 +854,7 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
fin:
- if (data->grp)
- bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
- BN_clear_free(x);
- BN_clear_free(y);
+ bin_clear_free(cruft, prime_len * 2);
if (data->outbuf == NULL) {
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
@@ -785,6 +862,10 @@ fin:
} else {
eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
}
+
+ /* clean allocated memory */
+ if (hash)
+ eap_pwd_h_final(hash, conf);
}
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 330febbefd78..0a6ce255af4d 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -85,12 +85,11 @@ static void * eap_sake_init(struct eap_sm *sm)
identity = eap_get_config_identity(sm, &identity_len);
if (identity) {
- data->peerid = os_malloc(identity_len);
+ data->peerid = os_memdup(identity, identity_len);
if (data->peerid == NULL) {
eap_sake_deinit(sm, data);
return NULL;
}
- os_memcpy(data->peerid, identity, identity_len);
data->peerid_len = identity_len;
}
@@ -230,10 +229,9 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
if (attr.serverid) {
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
attr.serverid, attr.serverid_len);
- data->serverid = os_malloc(attr.serverid_len);
+ data->serverid = os_memdup(attr.serverid, attr.serverid_len);
if (data->serverid == NULL)
return NULL;
- os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
data->serverid_len = attr.serverid_len;
}
@@ -450,10 +448,9 @@ static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -490,10 +487,9 @@ static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index b97c95db196f..ba5eea9ddfa2 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -46,6 +46,8 @@ struct eap_sim_data {
CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
} state;
int result_ind, use_result_ind;
+ int use_pseudonym;
+ int error_code;
};
@@ -93,6 +95,9 @@ static void * eap_sim_init(struct eap_sm *sm)
return NULL;
}
+ /* Zero is a valid error code, so we need to initialize */
+ data->error_code = NO_EAP_METHOD_ERROR;
+
data->min_num_chal = 2;
if (config && config->phase1) {
char *pos = os_strstr(config->phase1, "sim_min_num_chal=");
@@ -115,7 +120,8 @@ static void * eap_sim_init(struct eap_sm *sm)
NULL;
}
- if (config && config->anonymous_identity) {
+ data->use_pseudonym = !sm->init_phase2;
+ if (config && config->anonymous_identity && data->use_pseudonym) {
data->pseudonym = os_malloc(config->anonymous_identity_len);
if (data->pseudonym) {
os_memcpy(data->pseudonym, config->anonymous_identity,
@@ -372,7 +378,8 @@ static void eap_sim_clear_identities(struct eap_sm *sm,
os_free(data->pseudonym);
data->pseudonym = NULL;
data->pseudonym_len = 0;
- eap_set_anon_id(sm, NULL, 0);
+ if (data->use_pseudonym)
+ eap_set_anon_id(sm, NULL, 0);
}
if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id");
@@ -427,20 +434,21 @@ static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data,
realm, realm_len);
}
data->pseudonym_len = attr->next_pseudonym_len + realm_len;
- eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
+ if (data->use_pseudonym)
+ eap_set_anon_id(sm, data->pseudonym,
+ data->pseudonym_len);
}
if (attr->next_reauth_id) {
os_free(data->reauth_id);
- data->reauth_id = os_malloc(attr->next_reauth_id_len);
+ data->reauth_id = os_memdup(attr->next_reauth_id,
+ attr->next_reauth_id_len);
if (data->reauth_id == NULL) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
"next reauth_id");
data->reauth_id_len = 0;
return -1;
}
- os_memcpy(data->reauth_id, attr->next_reauth_id,
- attr->next_reauth_id_len);
data->reauth_id_len = attr->next_reauth_id_len;
wpa_hexdump_ascii(MSG_DEBUG,
"EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
@@ -635,14 +643,13 @@ static struct wpabuf * eap_sim_process_start(struct eap_sm *sm,
}
os_free(data->ver_list);
- data->ver_list = os_malloc(attr->version_list_len);
+ data->ver_list = os_memdup(attr->version_list, attr->version_list_len);
if (data->ver_list == NULL) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
"memory for version list");
return eap_sim_client_error(data, id,
EAP_SIM_UNABLE_TO_PROCESS_PACKET);
}
- os_memcpy(data->ver_list, attr->version_list, attr->version_list_len);
data->ver_list_len = attr->version_list_len;
pos = data->ver_list;
for (i = 0; i < data->ver_list_len / 2; i++) {
@@ -764,8 +771,17 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
} else if (data->pseudonym) {
identity = data->pseudonym;
identity_len = data->pseudonym_len;
- } else
- identity = eap_get_config_identity(sm, &identity_len);
+ } else {
+ struct eap_peer_config *config;
+
+ config = eap_get_config(sm);
+ if (config && config->imsi_identity) {
+ identity = config->imsi_identity;
+ identity_len = config->imsi_identity_len;
+ } else {
+ identity = eap_get_config_identity(sm, &identity_len);
+ }
+ }
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK "
"derivation", identity, identity_len);
eap_sim_derive_mk(identity, identity_len, data->nonce_mt,
@@ -908,6 +924,7 @@ static struct wpabuf * eap_sim_process_notification(
eap_sim_report_notification(sm->msg_ctx, attr->notification, 0);
if (attr->notification >= 0 && attr->notification < 32768) {
+ data->error_code = attr->notification;
eap_sim_state(data, FAILURE);
} else if (attr->notification == EAP_SIM_SUCCESS &&
data->state == RESULT_SUCCESS)
@@ -1181,12 +1198,11 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
if (key == NULL)
return NULL;
*len = EAP_SIM_KEYING_DATA_LEN;
- os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
return key;
}
@@ -1223,17 +1239,33 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
return key;
}
+static int eap_sim_get_error_code(void *priv)
+{
+ struct eap_sim_data *data = priv;
+ int current_data_error;
+
+ if (!data)
+ return NO_EAP_METHOD_ERROR;
+
+ current_data_error = data->error_code;
+
+ /* Now reset for next transaction */
+ data->error_code = NO_EAP_METHOD_ERROR;
+
+ return current_data_error;
+}
+
+
int eap_peer_sim_register(void)
{
struct eap_method *eap;
@@ -1254,6 +1286,7 @@ int eap_peer_sim_register(void)
eap->init_for_reauth = eap_sim_init_for_reauth;
eap->get_identity = eap_sim_get_identity;
eap->get_emsk = eap_sim_get_emsk;
+ eap->get_error_code = eap_sim_get_error_code;
return eap_peer_method_register(eap);
}
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index ca2354f8a785..cb747026cb8a 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -173,14 +173,31 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
struct eap_method_ret *ret)
{
+ const char *label;
+
wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_UNCOND_SUCC;
+ if (data->ssl.tls_out) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Fragment(s) remaining");
+ return;
+ }
+
+ if (data->ssl.tls_v13) {
+ label = "EXPORTER_EAP_TLS_Key_Material";
+
+ /* A possible NewSessionTicket may be received before
+ * EAP-Success, so need to allow it to be received. */
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ label = "client EAP encryption";
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
eap_tls_free_key(data);
- data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
- "client EAP encryption",
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label,
EAP_TLS_KEY_LEN +
EAP_EMSK_LEN);
if (data->key_data) {
@@ -338,12 +355,11 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->key_data == NULL)
return NULL;
- key = os_malloc(EAP_TLS_KEY_LEN);
+ key = os_memdup(data->key_data, EAP_TLS_KEY_LEN);
if (key == NULL)
return NULL;
*len = EAP_TLS_KEY_LEN;
- os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
return key;
}
@@ -357,12 +373,11 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->key_data == NULL)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
- os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
return key;
}
@@ -376,12 +391,11 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
if (data->session_id == NULL)
return NULL;
- id = os_malloc(data->id_len);
+ id = os_memdup(data->session_id, data->id_len);
if (id == NULL)
return NULL;
*len = data->id_len;
- os_memcpy(id, data->session_id, data->id_len);
return id;
}
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 0dcb9c138f81..0de131526a51 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -80,10 +80,22 @@ static void eap_tls_params_flags(struct tls_connection_params *params,
params->flags |= TLS_CONN_DISABLE_TLSv1_2;
if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
+ if (os_strstr(txt, "tls_disable_tlsv1_3=1"))
+ params->flags |= TLS_CONN_DISABLE_TLSv1_3;
+ if (os_strstr(txt, "tls_disable_tlsv1_3=0"))
+ params->flags &= ~TLS_CONN_DISABLE_TLSv1_3;
if (os_strstr(txt, "tls_ext_cert_check=1"))
params->flags |= TLS_CONN_EXT_CERT_CHECK;
if (os_strstr(txt, "tls_ext_cert_check=0"))
params->flags &= ~TLS_CONN_EXT_CERT_CHECK;
+ if (os_strstr(txt, "tls_suiteb=1"))
+ params->flags |= TLS_CONN_SUITEB;
+ if (os_strstr(txt, "tls_suiteb=0"))
+ params->flags &= ~TLS_CONN_SUITEB;
+ if (os_strstr(txt, "tls_suiteb_no_ecdh=1"))
+ params->flags |= TLS_CONN_SUITEB_NO_ECDH;
+ if (os_strstr(txt, "tls_suiteb_no_ecdh=0"))
+ params->flags &= ~TLS_CONN_SUITEB_NO_ECDH;
}
@@ -151,6 +163,23 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
*/
params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
}
+ if (data->eap_type == EAP_TYPE_FAST ||
+ data->eap_type == EAP_TYPE_TTLS ||
+ data->eap_type == EAP_TYPE_PEAP) {
+ /* The current EAP peer implementation is not yet ready for the
+ * TLS v1.3 changes, so disable this by default for now. */
+ params->flags |= TLS_CONN_DISABLE_TLSv1_3;
+ }
+ if (data->eap_type == EAP_TYPE_TLS) {
+ /* While the current EAP-TLS implementation is more or less
+ * complete for TLS v1.3, there has been no interoperability
+ * testing with other implementations, so disable for by default
+ * for now until there has been chance to confirm that no
+ * significant interoperability issues show up with TLS version
+ * update.
+ */
+ params->flags |= TLS_CONN_DISABLE_TLSv1_3;
+ }
if (phase2) {
wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
eap_tls_params_from_conf2(params, config);
@@ -358,6 +387,13 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
struct tls_random keys;
u8 *out;
+ if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
+ *len = 64;
+ return eap_peer_tls_derive_key(sm, data,
+ "EXPORTER_EAP_TLS_Session-Id",
+ 64);
+ }
+
if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) ||
keys.client_random == NULL || keys.server_random == NULL)
return NULL;
@@ -661,6 +697,8 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
* the AS.
*/
int res = eap_tls_process_input(sm, data, in_data, out_data);
+ char buf[20];
+
if (res) {
/*
* Input processing failed (res = -1) or more data is
@@ -673,6 +711,12 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
* The incoming message has been reassembled and processed. The
* response was allocated into data->tls_out buffer.
*/
+
+ if (tls_get_version(data->ssl_ctx, data->conn,
+ buf, sizeof(buf)) == 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf);
+ data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0;
+ }
}
if (data->tls_out == NULL) {
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index acd2b783617f..306e6a98bc3f 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -73,6 +73,11 @@ struct eap_ssl_data {
* eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
*/
u8 eap_type;
+
+ /**
+ * tls_v13 - Whether TLS v1.3 or newer is used
+ */
+ int tls_v13;
};
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 92f94dcd6019..f18788ce8cb5 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -458,7 +458,7 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
if (*resp == NULL &&
(config->pending_req_identity || config->pending_req_password ||
- config->pending_req_otp)) {
+ config->pending_req_otp || config->pending_req_sim)) {
return 0;
}
@@ -624,12 +624,28 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
os_memset(pos, 0, 24); /* LM-Response */
pos += 24;
if (pwhash) {
- challenge_response(challenge, password, pos); /* NT-Response */
+ /* NT-Response */
+ if (challenge_response(challenge, password, pos)) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAP: Failed derive password hash");
+ wpabuf_free(msg);
+ os_free(challenge);
+ return -1;
+ }
+
wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
password, 16);
} else {
- nt_challenge_response(challenge, password, password_len,
- pos); /* NT-Response */
+ /* NT-Response */
+ if (nt_challenge_response(challenge, password, password_len,
+ pos)) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAP: Failed derive password");
+ wpabuf_free(msg);
+ os_free(challenge);
+ return -1;
+ }
+
wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
password, password_len);
}
@@ -870,13 +886,12 @@ static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
{
wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
if (parse->eapdata == NULL) {
- parse->eapdata = os_malloc(dlen);
+ parse->eapdata = os_memdup(dpos, dlen);
if (parse->eapdata == NULL) {
wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
"memory for Phase 2 EAP data");
return -1;
}
- os_memcpy(parse->eapdata, dpos, dlen);
parse->eap_len = dlen;
} else {
u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
@@ -1280,7 +1295,8 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
} else if (config->pending_req_identity ||
config->pending_req_password ||
config->pending_req_otp ||
- config->pending_req_new_password) {
+ config->pending_req_new_password ||
+ config->pending_req_sim) {
wpabuf_free(data->pending_phase2_req);
data->pending_phase2_req = wpabuf_dup(in_decrypted);
}
@@ -1317,7 +1333,8 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
(config->pending_req_identity ||
config->pending_req_password ||
config->pending_req_otp ||
- config->pending_req_new_password)) {
+ config->pending_req_new_password ||
+ config->pending_req_sim)) {
/*
* Use empty buffer to force implicit request
* processing when EAP request is re-processed after
@@ -1537,7 +1554,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm,
}
-static void eap_ttls_check_auth_status(struct eap_sm *sm,
+static void eap_ttls_check_auth_status(struct eap_sm *sm,
struct eap_ttls_data *data,
struct eap_method_ret *ret)
{
@@ -1648,6 +1665,10 @@ static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_ttls_data *data = priv;
+
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->deinit_for_reauth)
+ data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
wpabuf_free(data->pending_phase2_req);
data->pending_phase2_req = NULL;
wpabuf_free(data->pending_resp);
@@ -1739,12 +1760,11 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->key_data == NULL || !data->phase2_success)
return NULL;
- key = os_malloc(EAP_TLS_KEY_LEN);
+ key = os_memdup(data->key_data, EAP_TLS_KEY_LEN);
if (key == NULL)
return NULL;
*len = EAP_TLS_KEY_LEN;
- os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
return key;
}
@@ -1758,12 +1778,11 @@ static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
if (data->session_id == NULL || !data->phase2_success)
return NULL;
- id = os_malloc(data->id_len);
+ id = os_memdup(data->session_id, data->id_len);
if (id == NULL)
return NULL;
*len = data->id_len;
- os_memcpy(id, data->session_id, data->id_len);
return id;
}
@@ -1777,12 +1796,11 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->key_data == NULL)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
*len = EAP_EMSK_LEN;
- os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
return key;
}
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index ca6502ea02e7..7bd97b1b997e 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -476,10 +476,9 @@ static int ikev2_process_idi(struct ikev2_responder_data *data,
wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type);
wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len);
os_free(data->IDi);
- data->IDi = os_malloc(idi_len);
+ data->IDi = os_memdup(idi, idi_len);
if (data->IDi == NULL)
return -1;
- os_memcpy(data->IDi, idi, idi_len);
data->IDi_len = idi_len;
data->IDi_type = id_type;
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index 0c5caa7dd522..a9bafe2886c0 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -126,12 +126,10 @@ static TNC_Result TNC_TNCC_ReportMessageTypes(
imc = tnc_imc[imcID];
os_free(imc->supported_types);
- imc->supported_types =
- os_malloc(typeCount * sizeof(TNC_MessageType));
+ imc->supported_types = os_memdup(supportedTypes,
+ typeCount * sizeof(TNC_MessageType));
if (imc->supported_types == NULL)
return TNC_RESULT_FATAL;
- os_memcpy(imc->supported_types, supportedTypes,
- typeCount * sizeof(TNC_MessageType));
imc->num_supported_types = typeCount;
return TNC_RESULT_SUCCESS;
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 69eaab8de946..4fbc661c22fe 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -31,6 +31,8 @@ struct eap_user {
size_t password_len;
int password_hash; /* whether password is hashed with
* nt_password_hash() */
+ u8 *salt;
+ size_t salt_len;
int phase2;
int force_version;
unsigned int remediation:1;
@@ -38,6 +40,7 @@ struct eap_user {
int ttls_auth; /* bitfield of
* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
struct hostapd_radius_attr *accept_attr;
+ u32 t_c_timestamp;
};
struct eap_eapol_interface {
@@ -132,6 +135,7 @@ struct eap_config {
size_t server_id_len;
int erp;
unsigned int tls_session_lifetime;
+ unsigned int tls_flags;
#ifdef CONFIG_TESTING_OPTIONS
u32 tls_test_flags;
@@ -148,10 +152,12 @@ void eap_sm_notify_cached(struct eap_sm *sm);
void eap_sm_pending_cb(struct eap_sm *sm);
int eap_sm_method_pending(struct eap_sm *sm);
const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
+const char * eap_get_serial_num(struct eap_sm *sm);
struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
void eap_server_clear_identity(struct eap_sm *sm);
void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
const u8 *username, size_t username_len,
const u8 *challenge, const u8 *response);
+void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len);
#endif /* EAP_H */
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index c90443d19cb9..cf8a9f0d98e1 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -159,6 +159,7 @@ struct eap_sm {
void *eap_method_priv;
u8 *identity;
size_t identity_len;
+ char *serial_num;
/* Whether Phase 2 method should validate identity match */
int require_identity_match;
int lastId; /* Identifier used in the last EAP-Packet */
@@ -211,6 +212,7 @@ struct eap_sm {
Boolean try_initiate_reauth;
int erp;
unsigned int tls_session_lifetime;
+ unsigned int tls_flags;
#ifdef CONFIG_TESTING_OPTIONS
u32 tls_test_flags;
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 84ecafc7ca3e..38a1b5c9ee22 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -326,6 +326,9 @@ SM_STATE(EAP, RETRANSMIT)
if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
sm->eap_if.eapReq = TRUE;
}
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -415,7 +418,7 @@ static void eap_server_erp_init(struct eap_sm *sm)
u8 *emsk = NULL;
size_t emsk_len = 0;
u8 EMSKname[EAP_EMSK_NAME_LEN];
- u8 len[2];
+ u8 len[2], ctx[3];
const char *domain;
size_t domain_len, nai_buf_len;
struct eap_server_erp_key *erp = NULL;
@@ -452,7 +455,7 @@ static void eap_server_erp_init(struct eap_sm *sm)
wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
- WPA_PUT_BE16(len, 8);
+ WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN);
if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen,
"EMSK", len, sizeof(len),
EMSKname, EAP_EMSK_NAME_LEN) < 0) {
@@ -476,9 +479,11 @@ static void eap_server_erp_init(struct eap_sm *sm)
erp->rRK_len = emsk_len;
wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+ ctx[0] = EAP_ERP_CS_HMAC_SHA256_128;
+ WPA_PUT_BE16(&ctx[1], erp->rRK_len);
if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
- "EAP Re-authentication Integrity Key@ietf.org",
- len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+ "Re-authentication Integrity Key@ietf.org",
+ ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) {
wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
goto fail;
}
@@ -632,6 +637,9 @@ SM_STATE(EAP, TIMEOUT_FAILURE)
SM_ENTRY(EAP, TIMEOUT_FAILURE);
sm->eap_if.eapTimeout = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1009,6 +1017,9 @@ SM_STATE(EAP, RETRANSMIT2)
if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
sm->eap_if.eapReq = TRUE;
}
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1099,6 +1110,9 @@ SM_STATE(EAP, TIMEOUT_FAILURE2)
SM_ENTRY(EAP, TIMEOUT_FAILURE2);
sm->eap_if.eapTimeout = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1108,6 +1122,9 @@ SM_STATE(EAP, FAILURE2)
eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
sm->eap_if.eapFail = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1134,6 +1151,9 @@ SM_STATE(EAP, SUCCESS2)
* started properly.
*/
sm->start_reauth = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1800,6 +1820,8 @@ static void eap_user_free(struct eap_user *user)
return;
bin_clear_free(user->password, user->password_len);
user->password = NULL;
+ bin_clear_free(user->salt, user->salt_len);
+ user->salt = NULL;
os_free(user);
}
@@ -1866,6 +1888,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx,
sm->server_id_len = conf->server_id_len;
sm->erp = conf->erp;
sm->tls_session_lifetime = conf->tls_session_lifetime;
+ sm->tls_flags = conf->tls_flags;
#ifdef CONFIG_TESTING_OPTIONS
sm->tls_test_flags = conf->tls_test_flags;
@@ -1897,6 +1920,7 @@ void eap_server_sm_deinit(struct eap_sm *sm)
wpabuf_free(sm->lastReqData);
wpabuf_free(sm->eap_if.eapRespData);
os_free(sm->identity);
+ os_free(sm->serial_num);
os_free(sm->pac_opaque_encr_key);
os_free(sm->eap_fast_a_id);
os_free(sm->eap_fast_a_id_info);
@@ -1969,6 +1993,55 @@ const u8 * eap_get_identity(struct eap_sm *sm, size_t *len)
/**
+ * eap_get_serial_num - Get the serial number of user certificate
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: Pointer to the serial number or %NULL if not available
+ */
+const char * eap_get_serial_num(struct eap_sm *sm)
+{
+ return sm->serial_num;
+}
+
+
+void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len)
+{
+#ifdef CONFIG_ERP
+ const struct eap_hdr *hdr;
+ const u8 *pos, *end;
+ struct erp_tlvs parse;
+
+ if (len < sizeof(*hdr) + 1)
+ return;
+ hdr = (const struct eap_hdr *) eap;
+ end = eap + len;
+ pos = (const u8 *) (hdr + 1);
+ if (hdr->code != EAP_CODE_INITIATE || *pos != EAP_ERP_TYPE_REAUTH)
+ return;
+ pos++;
+ if (pos + 3 > end)
+ return;
+
+ /* Skip Flags and SEQ */
+ pos += 3;
+
+ if (erp_parse_tlvs(pos, end, &parse, 1) < 0 || !parse.keyname)
+ return;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP: Update identity based on EAP-Initiate/Re-auth keyName-NAI",
+ parse.keyname, parse.keyname_len);
+ os_free(sm->identity);
+ sm->identity = os_malloc(parse.keyname_len);
+ if (sm->identity) {
+ os_memcpy(sm->identity, parse.keyname, parse.keyname_len);
+ sm->identity_len = parse.keyname_len;
+ } else {
+ sm->identity_len = 0;
+ }
+#endif /* CONFIG_ERP */
+}
+
+
+/**
* eap_get_interface - Get pointer to EAP-EAPOL interface data
* @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
* Returns: Pointer to the EAP-EAPOL interface data
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index a8bb5eae6b56..175021163c1d 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -1261,10 +1261,9 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
*len = EAP_SIM_KEYING_DATA_LEN;
return key;
}
@@ -1278,10 +1277,9 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index 1eba8f515648..71580bf7bf52 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -467,13 +467,12 @@ static void eap_eke_process_identity(struct eap_sm *sm,
data->peerid_type = *pos++;
os_free(data->peerid);
- data->peerid = os_malloc(end - pos);
+ data->peerid = os_memdup(pos, end - pos);
if (data->peerid == NULL) {
wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid");
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
return;
}
- os_memcpy(data->peerid, pos, end - pos);
data->peerid_len = end - pos;
wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity",
@@ -731,10 +730,9 @@ static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -749,10 +747,9 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 20491726880e..a63f820465c8 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -471,12 +471,11 @@ static void * eap_fast_init(struct eap_sm *sm)
eap_fast_reset(sm, data);
return NULL;
}
- data->srv_id = os_malloc(sm->eap_fast_a_id_len);
+ data->srv_id = os_memdup(sm->eap_fast_a_id, sm->eap_fast_a_id_len);
if (data->srv_id == NULL) {
eap_fast_reset(sm, data);
return NULL;
}
- os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
data->srv_id_len = sm->eap_fast_a_id_len;
if (sm->eap_fast_a_id_info == NULL) {
@@ -561,7 +560,7 @@ static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data)
return -1;
}
data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
-
+
if (data->anon_provisioning) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning");
eap_fast_derive_key_provisioning(sm, data);
@@ -789,7 +788,7 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
/* A-ID (inside PAC-Info) */
eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
-
+
/* Note: headers may be misaligned after A-ID */
if (sm->identity) {
@@ -1517,7 +1516,7 @@ static void eap_fast_process_msg(struct eap_sm *sm, void *priv,
if (eap_fast_process_phase1(sm, data))
break;
- /* fall through to PHASE2_START */
+ /* fall through */
case PHASE2_START:
eap_fast_process_phase2_start(sm, data);
break;
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index 94e74ec9b2f7..fb3d11748c8c 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -269,13 +269,12 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
return;
}
os_free(data->id_peer);
- data->id_peer = os_malloc(alen);
+ data->id_peer = os_memdup(pos, alen);
if (data->id_peer == NULL) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
"%d-octet ID_Peer", alen);
return;
}
- os_memcpy(data->id_peer, pos, alen);
data->id_peer_len = alen;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
data->id_peer, data->id_peer_len);
@@ -575,10 +574,9 @@ static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -593,10 +591,9 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
@@ -618,10 +615,9 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- sid = os_malloc(data->id_len);
+ sid = os_memdup(data->session_id, data->id_len);
if (sid == NULL)
return NULL;
- os_memcpy(sid, data->session_id, data->id_len);
*len = data->id_len;
return sid;
diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c
index 193a8517ac08..fcccbcbd5efa 100644
--- a/src/eap_server/eap_server_gtc.c
+++ b/src/eap_server/eap_server_gtc.c
@@ -141,12 +141,11 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv,
} else {
os_free(sm->identity);
sm->identity_len = pos2 - pos;
- sm->identity = os_malloc(sm->identity_len);
+ sm->identity = os_memdup(pos, sm->identity_len);
if (sm->identity == NULL) {
data->state = FAILURE;
return;
}
- os_memcpy(sm->identity, pos, sm->identity_len);
}
if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 3a249d141e0c..32e6872045c5 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -103,10 +103,9 @@ static void * eap_ikev2_init(struct eap_sm *sm)
data->ikev2.proposal.encr = ENCR_AES_CBC;
data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
- data->ikev2.IDi = os_malloc(sm->server_id_len);
+ data->ikev2.IDi = os_memdup(sm->server_id, sm->server_id_len);
if (data->ikev2.IDi == NULL)
goto failed;
- os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len);
data->ikev2.IDi_len = sm->server_id_len;
data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
@@ -224,7 +223,7 @@ static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id)
}
data->out_used = 0;
}
- /* pass through */
+ /* fall through */
case WAIT_FRAG_ACK:
return eap_ikev2_build_msg(data, id);
case FRAG_ACK:
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 460cd9c82ff5..6c47bb636aab 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -71,13 +71,12 @@ static void * eap_mschapv2_init(struct eap_sm *sm)
}
if (sm->peer_challenge) {
- data->peer_challenge = os_malloc(CHALLENGE_LEN);
+ data->peer_challenge = os_memdup(sm->peer_challenge,
+ CHALLENGE_LEN);
if (data->peer_challenge == NULL) {
os_free(data);
return NULL;
}
- os_memcpy(data->peer_challenge, sm->peer_challenge,
- CHALLENGE_LEN);
}
return data;
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 782b8c316537..3257789695cd 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -327,13 +327,12 @@ static void eap_pax_process_std_2(struct eap_sm *sm,
}
data->cid_len = cid_len;
os_free(data->cid);
- data->cid = os_malloc(data->cid_len);
+ data->cid = os_memdup(pos + 2, data->cid_len);
if (data->cid == NULL) {
wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
"CID");
return;
}
- os_memcpy(data->cid, pos + 2, data->cid_len);
pos += 2 + data->cid_len;
left -= 2 + data->cid_len;
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
index 857d421393bc..0eab89339ff6 100644
--- a/src/eap_server/eap_server_psk.c
+++ b/src/eap_server/eap_server_psk.c
@@ -236,13 +236,12 @@ static void eap_psk_process_2(struct eap_sm *sm,
left -= sizeof(*resp);
os_free(data->id_p);
- data->id_p = os_malloc(left);
+ data->id_p = os_memdup(cpos, left);
if (data->id_p == NULL) {
wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for "
"ID_P");
return;
}
- os_memcpy(data->id_p, cpos, left);
data->id_p_len = left;
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P",
data->id_p, data->id_p_len);
@@ -371,10 +370,9 @@ static void eap_psk_process_4(struct eap_sm *sm,
pos += 16;
left -= 16;
- decrypted = os_malloc(left);
+ decrypted = os_memdup(pos, left);
if (decrypted == NULL)
return;
- os_memcpy(decrypted, pos, left);
if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
wpabuf_head(respData), 22, decrypted, left,
@@ -450,10 +448,9 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -468,10 +465,9 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 64bf708e039a..d0fa54a3aba7 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "crypto/sha256.h"
#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_pwd_common.h"
@@ -26,8 +27,11 @@ struct eap_pwd_data {
u8 *password;
size_t password_len;
int password_hash;
+ u8 *salt;
+ size_t salt_len;
u32 token;
u16 group_num;
+ u8 password_prep;
EAP_PWD_group *grp;
struct wpabuf *inbuf;
@@ -36,20 +40,18 @@ struct eap_pwd_data {
size_t out_frag_pos;
size_t mtu;
- BIGNUM *k;
- BIGNUM *private_value;
- BIGNUM *peer_scalar;
- BIGNUM *my_scalar;
- EC_POINT *my_element;
- EC_POINT *peer_element;
+ struct crypto_bignum *k;
+ struct crypto_bignum *private_value;
+ struct crypto_bignum *peer_scalar;
+ struct crypto_bignum *my_scalar;
+ struct crypto_ec_point *my_element;
+ struct crypto_ec_point *peer_element;
u8 my_confirm[SHA256_MAC_LEN];
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 session_id[1 + SHA256_MAC_LEN];
-
- BN_CTX *bnctx;
};
@@ -116,13 +118,17 @@ static void * eap_pwd_init(struct eap_sm *sm)
os_memcpy(data->password, sm->user->password, data->password_len);
data->password_hash = sm->user->password_hash;
- data->bnctx = BN_CTX_new();
- if (data->bnctx == NULL) {
- wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
- bin_clear_free(data->password, data->password_len);
- bin_clear_free(data->id_server, data->id_server_len);
- os_free(data);
- return NULL;
+ data->salt_len = sm->user->salt_len;
+ if (data->salt_len) {
+ data->salt = os_memdup(sm->user->salt, sm->user->salt_len);
+ if (!data->salt) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Memory allocation of salt failed");
+ bin_clear_free(data->id_server, data->id_server_len);
+ bin_clear_free(data->password, data->password_len);
+ os_free(data);
+ return NULL;
+ }
}
data->in_frag_pos = data->out_frag_pos = 0;
@@ -138,21 +144,19 @@ static void eap_pwd_reset(struct eap_sm *sm, void *priv)
{
struct eap_pwd_data *data = priv;
- BN_clear_free(data->private_value);
- BN_clear_free(data->peer_scalar);
- BN_clear_free(data->my_scalar);
- BN_clear_free(data->k);
- BN_CTX_free(data->bnctx);
- EC_POINT_clear_free(data->my_element);
- EC_POINT_clear_free(data->peer_element);
+ crypto_bignum_deinit(data->private_value, 1);
+ crypto_bignum_deinit(data->peer_scalar, 1);
+ crypto_bignum_deinit(data->my_scalar, 1);
+ crypto_bignum_deinit(data->k, 1);
+ crypto_ec_point_deinit(data->my_element, 1);
+ crypto_ec_point_deinit(data->peer_element, 1);
bin_clear_free(data->id_peer, data->id_peer_len);
bin_clear_free(data->id_server, data->id_server_len);
bin_clear_free(data->password, data->password_len);
+ bin_clear_free(data->salt, data->salt_len);
if (data->grp) {
- EC_GROUP_free(data->grp->group);
- EC_POINT_clear_free(data->grp->pwe);
- BN_clear_free(data->grp->order);
- BN_clear_free(data->grp->prime);
+ crypto_ec_deinit(data->grp->group);
+ crypto_ec_point_deinit(data->grp->pwe, 1);
os_free(data->grp);
}
wpabuf_free(data->inbuf);
@@ -185,12 +189,45 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data,
return;
}
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd (server): password",
+ data->password, data->password_len);
+ if (data->salt_len)
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd (server): salt",
+ data->salt, data->salt_len);
+
+ /*
+ * If this is a salted password then figure out how it was hashed
+ * based on the length.
+ */
+ if (data->salt_len) {
+ switch (data->password_len) {
+ case 20:
+ data->password_prep = EAP_PWD_PREP_SSHA1;
+ break;
+ case 32:
+ data->password_prep = EAP_PWD_PREP_SSHA256;
+ break;
+ case 64:
+ data->password_prep = EAP_PWD_PREP_SSHA512;
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "EAP-pwd (server): bad size %d for salted password",
+ (int) data->password_len);
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ } else {
+ /* Otherwise, figure out whether it's MS hashed or plain */
+ data->password_prep = data->password_hash ? EAP_PWD_PREP_MS :
+ EAP_PWD_PREP_NONE;
+ }
+
wpabuf_put_be16(data->outbuf, data->group_num);
wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token));
- wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS :
- EAP_PWD_PREP_NONE);
+ wpabuf_put_u8(data->outbuf, data->password_prep);
wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len);
}
@@ -198,9 +235,9 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data,
static void eap_pwd_build_commit_req(struct eap_sm *sm,
struct eap_pwd_data *data, u8 id)
{
- BIGNUM *mask = NULL, *x = NULL, *y = NULL;
+ struct crypto_bignum *mask = NULL;
u8 *scalar = NULL, *element = NULL;
- u16 offset;
+ size_t prime_len, order_len;
wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
/*
@@ -210,93 +247,82 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm,
if (data->out_frag_pos)
return;
- if (((data->private_value = BN_new()) == NULL) ||
- ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
- ((data->my_scalar = BN_new()) == NULL) ||
- ((mask = BN_new()) == NULL)) {
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
+
+ data->private_value = crypto_bignum_init();
+ data->my_element = crypto_ec_point_init(data->grp->group);
+ data->my_scalar = crypto_bignum_init();
+ mask = crypto_bignum_init();
+ if (!data->private_value || !data->my_element || !data->my_scalar ||
+ !mask) {
wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
"fail");
goto fin;
}
- if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
- BN_rand_range(mask, data->grp->order) != 1 ||
- BN_add(data->my_scalar, data->private_value, mask) != 1 ||
- BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
- data->bnctx) != 1) {
+ if (crypto_bignum_rand(data->private_value,
+ crypto_ec_get_order(data->grp->group)) < 0 ||
+ crypto_bignum_rand(mask,
+ crypto_ec_get_order(data->grp->group)) < 0 ||
+ crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 ||
+ crypto_bignum_mod(data->my_scalar,
+ crypto_ec_get_order(data->grp->group),
+ data->my_scalar) < 0) {
wpa_printf(MSG_INFO,
"EAP-pwd (server): unable to get randomness");
goto fin;
}
- if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
- data->grp->pwe, mask, data->bnctx)) {
+ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
+ data->my_element) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
"fail");
eap_pwd_state(data, FAILURE);
goto fin;
}
- if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
- {
+ if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
"fail");
goto fin;
}
- BN_clear_free(mask);
- if (((x = BN_new()) == NULL) ||
- ((y = BN_new()) == NULL)) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
- "fail");
+ scalar = os_malloc(order_len);
+ element = os_malloc(prime_len * 2);
+ if (!scalar || !element) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
goto fin;
}
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
+ element + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
"fail");
goto fin;
}
- if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
- ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
- NULL)) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
- goto fin;
- }
+ crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
- /*
- * bignums occupy as little memory as possible so one that is
- * sufficiently smaller than the prime or order might need pre-pending
- * with zeros.
- */
- os_memset(scalar, 0, BN_num_bytes(data->grp->order));
- os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, scalar + offset);
-
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, element + offset);
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
-
- data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) +
- BN_num_bytes(data->grp->order));
+ data->outbuf = wpabuf_alloc(2 * prime_len + order_len +
+ (data->salt ? 1 + data->salt_len : 0));
if (data->outbuf == NULL)
goto fin;
+ /* If we're doing salted password prep, add the salt */
+ if (data->salt_len) {
+ wpabuf_put_u8(data->outbuf, data->salt_len);
+ wpabuf_put_data(data->outbuf, data->salt, data->salt_len);
+ }
+
/* We send the element as (x,y) followed by the scalar */
- wpabuf_put_data(data->outbuf, element,
- 2 * BN_num_bytes(data->grp->prime));
- wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
+ wpabuf_put_data(data->outbuf, element, 2 * prime_len);
+ wpabuf_put_data(data->outbuf, scalar, order_len);
fin:
+ crypto_bignum_deinit(mask, 1);
os_free(scalar);
os_free(element);
- BN_clear_free(x);
- BN_clear_free(y);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
}
@@ -305,11 +331,10 @@ fin:
static void eap_pwd_build_confirm_req(struct eap_sm *sm,
struct eap_pwd_data *data, u8 id)
{
- BIGNUM *x = NULL, *y = NULL;
struct crypto_hash *hash;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
u16 grp;
- int offset;
+ size_t prime_len, order_len;
wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
/*
@@ -319,9 +344,12 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm,
if (data->out_frag_pos)
return;
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
+
/* Each component of the cruft will be at most as big as the prime */
- if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
- ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ cruft = os_malloc(prime_len * 2);
+ if (!cruft) {
wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
"fail");
goto fin;
@@ -341,64 +369,38 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm,
*
* First is k
*/
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
- BN_bn2bin(data->k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
/* server element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+ cruft + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
-
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* peer element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->peer_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft,
+ cruft + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
-
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* peer scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->peer_scalar);
- BN_bn2bin(data->peer_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* ciphersuite */
grp = htons(data->group_num);
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, prime_len);
ptr = cruft;
os_memcpy(ptr, &grp, sizeof(u16));
ptr += sizeof(u16);
@@ -419,9 +421,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm,
wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
fin:
- bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
- BN_clear_free(x);
- BN_clear_free(y);
+ bin_clear_free(cruft, prime_len * 2);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
}
@@ -602,11 +602,16 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm,
if ((data->group_num != be_to_host16(id->group_num)) ||
(id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
(os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
- (id->prf != EAP_PWD_DEFAULT_PRF)) {
+ (id->prf != EAP_PWD_DEFAULT_PRF) ||
+ (id->prep != data->password_prep)) {
wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
eap_pwd_state(data, FAILURE);
return;
}
+ if (data->id_peer || data->grp) {
+ wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated");
+ return;
+ }
data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
if (data->id_peer == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
@@ -617,14 +622,19 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm,
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
data->id_peer, data->id_peer_len);
- data->grp = os_zalloc(sizeof(EAP_PWD_group));
+ data->grp = get_eap_pwd_group(data->group_num);
if (data->grp == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
"group");
return;
}
- if (data->password_hash) {
+ /*
+ * If it's PREP_MS then hash the password again, otherwise regardless
+ * of the prep the client is doing, the password we have is the one to
+ * use to generate the password element.
+ */
+ if (data->password_prep == EAP_PWD_PREP_MS) {
res = hash_nt_password_hash(data->password, pwhashhash);
if (res)
return;
@@ -647,7 +657,7 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm,
return;
}
wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
- BN_num_bits(data->grp->prime));
+ (int) crypto_ec_prime_len_bits(data->grp->group));
eap_pwd_state(data, PWD_Commit_Req);
}
@@ -657,16 +667,16 @@ static void
eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
- u8 *ptr;
- BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
- EC_POINT *K = NULL, *point = NULL;
+ const u8 *ptr;
+ struct crypto_bignum *cofactor = NULL;
+ struct crypto_ec_point *K = NULL, *point = NULL;
int res = 0;
size_t prime_len, order_len;
wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
- prime_len = BN_num_bytes(data->grp->prime);
- order_len = BN_num_bytes(data->grp->order);
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
if (payload_len != 2 * prime_len + order_len) {
wpa_printf(MSG_INFO,
@@ -676,49 +686,47 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
}
- if (((data->peer_scalar = BN_new()) == NULL) ||
- ((data->k = BN_new()) == NULL) ||
- ((cofactor = BN_new()) == NULL) ||
- ((x = BN_new()) == NULL) ||
- ((y = BN_new()) == NULL) ||
- ((point = EC_POINT_new(data->grp->group)) == NULL) ||
- ((K = EC_POINT_new(data->grp->group)) == NULL) ||
- ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) {
+ data->k = crypto_bignum_init();
+ cofactor = crypto_bignum_init();
+ point = crypto_ec_point_init(data->grp->group);
+ K = crypto_ec_point_init(data->grp->group);
+ if (!data->k || !cofactor || !point || !K) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
"fail");
goto fin;
}
- if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+ if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
"cofactor for curve");
goto fin;
}
/* element, x then y, followed by scalar */
- ptr = (u8 *) payload;
- BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
- ptr += BN_num_bytes(data->grp->prime);
- BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
- ptr += BN_num_bytes(data->grp->prime);
- BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar);
- if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
- data->peer_element, x, y,
- data->bnctx)) {
+ ptr = payload;
+ data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+ if (!data->peer_element) {
wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
"fail");
goto fin;
}
+ ptr += prime_len * 2;
+ data->peer_scalar = crypto_bignum_init_set(ptr, order_len);
+ if (!data->peer_scalar) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+ "fail");
+ goto fin;
+ }
/* check to ensure peer's element is not in a small sub-group */
- if (BN_cmp(cofactor, BN_value_one())) {
- if (!EC_POINT_mul(data->grp->group, point, NULL,
- data->peer_element, cofactor, NULL)) {
+ if (!crypto_bignum_is_one(cofactor)) {
+ if (crypto_ec_point_mul(data->grp->group, data->peer_element,
+ cofactor, point) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
"multiply peer element by order");
goto fin;
}
- if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+ if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
"is at infinity!\n");
goto fin;
@@ -726,21 +734,21 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
}
/* compute the shared key, k */
- if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
- data->peer_scalar, data->bnctx)) ||
- (!EC_POINT_add(data->grp->group, K, K, data->peer_element,
- data->bnctx)) ||
- (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
- data->bnctx))) {
+ if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe,
+ data->peer_scalar, K) < 0) ||
+ (crypto_ec_point_add(data->grp->group, K, data->peer_element,
+ K) < 0) ||
+ (crypto_ec_point_mul(data->grp->group, K, data->private_value,
+ K) < 0)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
"fail");
goto fin;
}
/* ensure that the shared key isn't in a small sub-group */
- if (BN_cmp(cofactor, BN_value_one())) {
- if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
- NULL)) {
+ if (!crypto_bignum_is_one(cofactor)) {
+ if (crypto_ec_point_mul(data->grp->group, K, cofactor,
+ K) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
"multiply shared key point by order!\n");
goto fin;
@@ -753,13 +761,12 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
* never going to happen it is a simple and safe check "just to be
* sure" so let's be safe.
*/
- if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+ if (crypto_ec_point_is_at_infinity(data->grp->group, K)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
"at infinity");
goto fin;
}
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
- NULL, data->bnctx)) {
+ if (crypto_ec_point_x(data->grp->group, K, data->k)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
"shared secret from secret point");
goto fin;
@@ -767,11 +774,9 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
res = 1;
fin:
- EC_POINT_clear_free(K);
- EC_POINT_clear_free(point);
- BN_clear_free(cofactor);
- BN_clear_free(x);
- BN_clear_free(y);
+ crypto_ec_point_deinit(K, 1);
+ crypto_ec_point_deinit(point, 1);
+ crypto_bignum_deinit(cofactor, 1);
if (res)
eap_pwd_state(data, PWD_Confirm_Req);
@@ -784,12 +789,14 @@ static void
eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
- BIGNUM *x = NULL, *y = NULL;
struct crypto_hash *hash;
u32 cs;
u16 grp;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
- int offset;
+ size_t prime_len, order_len;
+
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
if (payload_len != SHA256_MAC_LEN) {
wpa_printf(MSG_INFO,
@@ -808,8 +815,8 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
*ptr = EAP_PWD_DEFAULT_PRF;
/* each component of the cruft will be at most as big as the prime */
- if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
- ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ cruft = os_malloc(prime_len * 2);
+ if (!cruft) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
goto fin;
}
@@ -823,62 +830,36 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
/* k */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
- BN_bn2bin(data->k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
/* peer element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->peer_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft,
+ cruft + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* peer scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->peer_scalar);
- BN_bn2bin(data->peer_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* server element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+ cruft + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
-
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* ciphersuite */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
/* all done */
@@ -892,7 +873,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
}
wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
- if (compute_keys(data->grp, data->bnctx, data->k,
+ if (compute_keys(data->grp, data->k,
data->peer_scalar, data->my_scalar, conf,
data->my_confirm, &cs, data->msk, data->emsk,
data->session_id) < 0)
@@ -901,9 +882,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
eap_pwd_state(data, SUCCESS);
fin:
- bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
- BN_clear_free(x);
- BN_clear_free(y);
+ bin_clear_free(cruft, prime_len * 2);
}
@@ -1033,11 +1012,10 @@ static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -1052,11 +1030,10 @@ static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
@@ -1085,11 +1062,10 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- id = os_malloc(1 + SHA256_MAC_LEN);
+ id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN);
if (id == NULL)
return NULL;
- os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
*len = 1 + SHA256_MAC_LEN;
return id;
@@ -1127,4 +1103,3 @@ int eap_server_pwd_register(void)
return eap_server_method_register(eap);
}
-
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 84d0e0be4dd1..66183f5f5c04 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -326,10 +326,9 @@ static void eap_sake_process_challenge(struct eap_sm *sm,
data->peerid = NULL;
data->peerid_len = 0;
if (attr.peerid) {
- data->peerid = os_malloc(attr.peerid_len);
+ data->peerid = os_memdup(attr.peerid, attr.peerid_len);
if (data->peerid == NULL)
return;
- os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
data->peerid_len = attr.peerid_len;
}
@@ -460,10 +459,9 @@ static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_MSK_LEN);
+ key = os_memdup(data->msk, EAP_MSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_MSK_LEN);
*len = EAP_MSK_LEN;
return key;
@@ -478,10 +476,9 @@ static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 3a6ed795c768..10637d4c66b9 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -787,10 +787,9 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
*len = EAP_SIM_KEYING_DATA_LEN;
return key;
}
@@ -804,10 +803,9 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- key = os_malloc(EAP_EMSK_LEN);
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (key == NULL)
return NULL;
- os_memcpy(key, data->emsk, EAP_EMSK_LEN);
*len = EAP_EMSK_LEN;
return key;
}
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 7249858844ef..8b9e53c61d79 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -302,17 +302,22 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_tls_data *data = priv;
u8 *eapKeyData;
+ const char *label;
if (data->state != SUCCESS)
return NULL;
- eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "client EAP encryption",
- EAP_TLS_KEY_LEN);
+ if (data->ssl.tls_v13)
+ label = "EXPORTER_EAP_TLS_Key_Material";
+ else
+ label = "client EAP encryption";
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
+ EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
*len = EAP_TLS_KEY_LEN;
wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
eapKeyData, EAP_TLS_KEY_LEN);
+ os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN);
} else {
wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
}
@@ -325,12 +330,16 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_tls_data *data = priv;
u8 *eapKeyData, *emsk;
+ const char *label;
if (data->state != SUCCESS)
return NULL;
- eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "client EAP encryption",
+ if (data->ssl.tls_v13)
+ label = "EXPORTER_EAP_TLS_Key_Material";
+ else
+ label = "client EAP encryption";
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
emsk = os_malloc(EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 69096954b826..0ae7867fccf7 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -47,7 +47,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer, int eap_type)
{
u8 session_ctx[8];
- unsigned int flags = 0;
+ unsigned int flags = sm->tls_flags;
if (sm->ssl_ctx == NULL) {
wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
@@ -107,7 +107,7 @@ void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
- char *label, size_t len)
+ const char *label, size_t len)
{
u8 *out;
@@ -145,6 +145,13 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
struct tls_random keys;
u8 *out;
+ if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
+ *len = 64;
+ return eap_server_tls_derive_key(sm, data,
+ "EXPORTER_EAP_TLS_Session-Id",
+ 64);
+ }
+
if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
return NULL;
@@ -305,6 +312,8 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data,
int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data)
{
+ char buf[20];
+
if (data->tls_out) {
/* This should not happen.. */
wpa_printf(MSG_INFO, "SSL: pending tls_out data when "
@@ -327,6 +336,16 @@ int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data)
return -1;
}
+ if (tls_get_version(sm->ssl_ctx, data->conn, buf, sizeof(buf)) == 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf);
+ data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0;
+ }
+
+ if (!sm->serial_num &&
+ tls_connection_established(sm->ssl_ctx, data->conn))
+ sm->serial_num = tls_connection_peer_serial_num(sm->ssl_ctx,
+ data->conn);
+
return 0;
}
@@ -373,7 +392,7 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
if (data->tls_in &&
eap_server_tls_process_cont(data, *pos, end - *pos) < 0)
return -1;
-
+
if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) {
if (eap_server_tls_process_fragment(data, flags, tls_msg_len,
*pos, end - *pos) < 0)
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index a53633f8f1fe..b14996b0b990 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -228,14 +228,13 @@ static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse)
if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
if (parse->eap == NULL) {
- parse->eap = os_malloc(dlen);
+ parse->eap = os_memdup(dpos, dlen);
if (parse->eap == NULL) {
wpa_printf(MSG_WARNING, "EAP-TTLS: "
"failed to allocate memory "
"for Phase 2 EAP data");
goto fail;
}
- os_memcpy(parse->eap, dpos, dlen);
parse->eap_len = dlen;
} else {
u8 *neweap = os_realloc(parse->eap,
@@ -372,7 +371,7 @@ static void eap_ttls_reset(struct eap_sm *sm, void *priv)
static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm,
struct eap_ttls_data *data, u8 id)
-{
+{
struct wpabuf *req;
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1,
@@ -666,11 +665,14 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
}
os_free(chal);
- if (sm->user->password_hash)
- challenge_response(challenge, sm->user->password, nt_response);
- else
- nt_challenge_response(challenge, sm->user->password,
- sm->user->password_len, nt_response);
+ if ((sm->user->password_hash &&
+ challenge_response(challenge, sm->user->password, nt_response)) ||
+ (!sm->user->password_hash &&
+ nt_challenge_response(challenge, sm->user->password,
+ sm->user->password_len, nt_response))) {
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
@@ -1051,12 +1053,11 @@ static void eap_ttls_process_phase2(struct eap_sm *sm,
}
os_free(sm->identity);
- sm->identity = os_malloc(parse.user_name_len);
+ sm->identity = os_memdup(parse.user_name, parse.user_name_len);
if (sm->identity == NULL) {
eap_ttls_state(data, FAILURE);
goto done;
}
- os_memcpy(sm->identity, parse.user_name, parse.user_name_len);
sm->identity_len = parse.user_name_len;
if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
!= 0) {
diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c
index 7d9d285c39d0..4a5cb980afac 100644
--- a/src/eap_server/eap_server_wsc.c
+++ b/src/eap_server/eap_server_wsc.c
@@ -257,7 +257,7 @@ static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id)
}
data->out_used = 0;
}
- /* pass through */
+ /* fall through */
case WAIT_FRAG_ACK:
return eap_wsc_build_msg(data, id);
case FRAG_ACK:
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index dc943eb207d7..31f6e72d779a 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -50,6 +50,11 @@ struct eap_ssl_data {
enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state;
struct wpabuf tmpbuf;
+
+ /**
+ * tls_v13 - Whether TLS v1.3 or newer is used
+ */
+ int tls_v13;
};
@@ -73,7 +78,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer, int eap_type);
void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
- char *label, size_t len);
+ const char *label, size_t len);
u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
struct eap_ssl_data *data, u8 eap_type,
size_t *len);
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
index 5385cd89246f..0e9210e67b50 100644
--- a/src/eap_server/ikev2.c
+++ b/src/eap_server/ikev2.c
@@ -544,10 +544,9 @@ static int ikev2_process_idr(struct ikev2_initiator_data *data,
}
os_free(data->IDr);
}
- data->IDr = os_malloc(idr_len);
+ data->IDr = os_memdup(idr, idr_len);
if (data->IDr == NULL)
return -1;
- os_memcpy(data->IDr, idr, idr_len);
data->IDr_len = idr_len;
data->IDr_type = id_type;
@@ -1147,10 +1146,9 @@ static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data)
return NULL;
} else {
os_free(data->shared_secret);
- data->shared_secret = os_malloc(secret_len);
+ data->shared_secret = os_memdup(secret, secret_len);
if (data->shared_secret == NULL)
return NULL;
- os_memcpy(data->shared_secret, secret, secret_len);
data->shared_secret_len = secret_len;
}
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
index cfcbd3ed828c..942a195761ac 100644
--- a/src/eap_server/tncs.c
+++ b/src/eap_server/tncs.c
@@ -161,12 +161,10 @@ static TNC_Result TNC_TNCS_ReportMessageTypes(
if (imv == NULL)
return TNC_RESULT_INVALID_PARAMETER;
os_free(imv->supported_types);
- imv->supported_types =
- os_malloc(typeCount * sizeof(TNC_MessageType));
+ imv->supported_types = os_memdup(supportedTypes,
+ typeCount * sizeof(TNC_MessageType));
if (imv->supported_types == NULL)
return TNC_RESULT_FATAL;
- os_memcpy(imv->supported_types, supportedTypes,
- typeCount * sizeof(TNC_MessageType));
imv->num_supported_types = typeCount;
return TNC_RESULT_SUCCESS;
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index ff673bb2e785..36074d3e0474 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -848,6 +848,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
eap_conf.server_id_len = eapol->conf.server_id_len;
eap_conf.erp = eapol->conf.erp;
eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
+ eap_conf.tls_flags = eapol->conf.tls_flags;
sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
if (sm->eap == NULL) {
eapol_auth_free(sm);
@@ -1197,30 +1198,27 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
dst->server_id = src->server_id;
dst->server_id_len = src->server_id_len;
if (src->eap_req_id_text) {
- dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
+ dst->eap_req_id_text = os_memdup(src->eap_req_id_text,
+ src->eap_req_id_text_len);
if (dst->eap_req_id_text == NULL)
return -1;
- os_memcpy(dst->eap_req_id_text, src->eap_req_id_text,
- src->eap_req_id_text_len);
dst->eap_req_id_text_len = src->eap_req_id_text_len;
} else {
dst->eap_req_id_text = NULL;
dst->eap_req_id_text_len = 0;
}
if (src->pac_opaque_encr_key) {
- dst->pac_opaque_encr_key = os_malloc(16);
+ dst->pac_opaque_encr_key = os_memdup(src->pac_opaque_encr_key,
+ 16);
if (dst->pac_opaque_encr_key == NULL)
goto fail;
- os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
- 16);
} else
dst->pac_opaque_encr_key = NULL;
if (src->eap_fast_a_id) {
- dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
+ dst->eap_fast_a_id = os_memdup(src->eap_fast_a_id,
+ src->eap_fast_a_id_len);
if (dst->eap_fast_a_id == NULL)
goto fail;
- os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
- src->eap_fast_a_id_len);
dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
} else
dst->eap_fast_a_id = NULL;
@@ -1249,6 +1247,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
dst->erp_send_reauth_start = src->erp_send_reauth_start;
dst->erp = src->erp;
dst->tls_session_lifetime = src->tls_session_lifetime;
+ dst->tls_flags = src->tls_flags;
return 0;
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index e1974e4354da..44f3f31cc601 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -28,6 +28,7 @@ struct eapol_auth_config {
char *erp_domain; /* a copy of this will be allocated */
int erp; /* Whether ERP is enabled on authentication server */
unsigned int tls_session_lifetime;
+ unsigned int tls_flags;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 65460fc3bec0..9f029b0d3710 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -16,6 +16,7 @@
#include "crypto/md5.h"
#include "common/eapol_common.h"
#include "eap_peer/eap.h"
+#include "eap_peer/eap_config.h"
#include "eap_peer/eap_proxy.h"
#include "eapol_supp_sm.h"
@@ -95,7 +96,7 @@ struct eapol_sm {
SUPP_BE_RECEIVE = 4,
SUPP_BE_RESPONSE = 5,
SUPP_BE_FAIL = 6,
- SUPP_BE_TIMEOUT = 7,
+ SUPP_BE_TIMEOUT = 7,
SUPP_BE_SUCCESS = 8
} SUPP_BE_state; /* dot1xSuppBackendPaeState */
/* Variables */
@@ -250,6 +251,8 @@ SM_STATE(SUPP_PAE, CONNECTING)
if (sm->eapTriggerStart)
send_start = 1;
+ if (sm->ctx->preauth)
+ send_start = 1;
sm->eapTriggerStart = FALSE;
if (send_start) {
@@ -490,9 +493,24 @@ SM_STATE(SUPP_BE, SUCCESS)
#ifdef CONFIG_EAP_PROXY
if (sm->use_eap_proxy) {
if (eap_proxy_key_available(sm->eap_proxy)) {
+ u8 *session_id, *emsk;
+ size_t session_id_len, emsk_len;
+
/* New key received - clear IEEE 802.1X EAPOL-Key replay
* counter */
sm->replay_counter_valid = FALSE;
+
+ session_id = eap_proxy_get_eap_session_id(
+ sm->eap_proxy, &session_id_len);
+ emsk = eap_proxy_get_emsk(sm->eap_proxy, &emsk_len);
+ if (sm->config->erp && session_id && emsk) {
+ eap_peer_erp_init(sm->eap, session_id,
+ session_id_len, emsk,
+ emsk_len);
+ } else {
+ os_free(session_id);
+ bin_clear_free(emsk, emsk_len);
+ }
}
return;
}
@@ -897,6 +915,9 @@ static void eapol_sm_abortSupp(struct eapol_sm *sm)
wpabuf_free(sm->eapReqData);
sm->eapReqData = NULL;
eap_sm_abort(sm->eap);
+#ifdef CONFIG_EAP_PROXY
+ eap_proxy_sm_abort(sm->eap_proxy);
+#endif /* CONFIG_EAP_PROXY */
}
@@ -1998,7 +2019,17 @@ static void eapol_sm_notify_status(void *ctx, const char *status,
}
+static void eapol_sm_notify_eap_error(void *ctx, int error_code)
+{
+ struct eapol_sm *sm = ctx;
+
+ if (sm->ctx->eap_error_cb)
+ sm->ctx->eap_error_cb(sm->ctx->ctx, error_code);
+}
+
+
#ifdef CONFIG_EAP_PROXY
+
static void eapol_sm_eap_proxy_cb(void *ctx)
{
struct eapol_sm *sm = ctx;
@@ -2006,6 +2037,18 @@ static void eapol_sm_eap_proxy_cb(void *ctx)
if (sm->ctx->eap_proxy_cb)
sm->ctx->eap_proxy_cb(sm->ctx->ctx);
}
+
+
+static void
+eapol_sm_eap_proxy_notify_sim_status(void *ctx,
+ enum eap_proxy_sim_state sim_state)
+{
+ struct eapol_sm *sm = ctx;
+
+ if (sm->ctx->eap_proxy_notify_sim_status)
+ sm->ctx->eap_proxy_notify_sim_status(sm->ctx->ctx, sim_state);
+}
+
#endif /* CONFIG_EAP_PROXY */
@@ -2032,8 +2075,11 @@ static const struct eapol_callbacks eapol_cb =
eapol_sm_eap_param_needed,
eapol_sm_notify_cert,
eapol_sm_notify_status,
+ eapol_sm_notify_eap_error,
#ifdef CONFIG_EAP_PROXY
eapol_sm_eap_proxy_cb,
+ eapol_sm_eap_proxy_notify_sim_status,
+ eapol_sm_get_eap_proxy_imsi,
#endif /* CONFIG_EAP_PROXY */
eapol_sm_set_anon_id
};
@@ -2141,16 +2187,16 @@ int eapol_sm_failed(struct eapol_sm *sm)
}
-int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len)
-{
#ifdef CONFIG_EAP_PROXY
+int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi, size_t *len)
+{
+ struct eapol_sm *sm = ctx;
+
if (sm->eap_proxy == NULL)
return -1;
- return eap_proxy_get_imsi(sm->eap_proxy, imsi, len);
-#else /* CONFIG_EAP_PROXY */
- return -1;
-#endif /* CONFIG_EAP_PROXY */
+ return eap_proxy_get_imsi(sm->eap_proxy, sim_num, imsi, len);
}
+#endif /* CONFIG_EAP_PROXY */
void eapol_sm_erp_flush(struct eapol_sm *sm)
@@ -2158,3 +2204,56 @@ void eapol_sm_erp_flush(struct eapol_sm *sm)
if (sm)
eap_peer_erp_free_keys(sm->eap);
}
+
+
+struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm)
+{
+#ifdef CONFIG_ERP
+ if (!sm)
+ return NULL;
+ return eap_peer_build_erp_reauth_start(sm->eap, 0);
+#else /* CONFIG_ERP */
+ return NULL;
+#endif /* CONFIG_ERP */
+}
+
+
+void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
+ size_t len)
+{
+#ifdef CONFIG_ERP
+ if (!sm)
+ return;
+ eap_peer_finish(sm->eap, (const struct eap_hdr *) buf, len);
+#endif /* CONFIG_ERP */
+}
+
+
+int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num)
+{
+#ifdef CONFIG_ERP
+ if (!sm)
+ return -1;
+ return eap_peer_update_erp_next_seq_num(sm->eap, next_seq_num);
+#else /* CONFIG_ERP */
+ return -1;
+#endif /* CONFIG_ERP */
+}
+
+
+int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+ const u8 **username, size_t *username_len,
+ const u8 **realm, size_t *realm_len,
+ u16 *erp_next_seq_num, const u8 **rrk,
+ size_t *rrk_len)
+{
+#ifdef CONFIG_ERP
+ if (!sm)
+ return -1;
+ return eap_peer_get_erp_info(sm->eap, config, username, username_len,
+ realm, realm_len, erp_next_seq_num, rrk,
+ rrk_len);
+#else /* CONFIG_ERP */
+ return -1;
+#endif /* CONFIG_ERP */
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 1309ff7547e8..74f40bb1cd63 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -271,12 +271,27 @@ struct eapol_ctx {
void (*status_cb)(void *ctx, const char *status,
const char *parameter);
+ /**
+ * eap_error_cb - Notification of EAP method error
+ * @ctx: Callback context (ctx)
+ * @error_code: EAP method error code
+ */
+ void (*eap_error_cb)(void *ctx, int error_code);
+
#ifdef CONFIG_EAP_PROXY
/**
* eap_proxy_cb - Callback signifying any updates from eap_proxy
* @ctx: eapol_ctx from eap_peer_sm_init() call
*/
void (*eap_proxy_cb)(void *ctx);
+
+ /**
+ * eap_proxy_notify_sim_status - Notification of SIM status change
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @status: One of enum value from sim_state
+ */
+ void (*eap_proxy_notify_sim_status)(void *ctx,
+ enum eap_proxy_sim_state sim_state);
#endif /* CONFIG_EAP_PROXY */
/**
@@ -328,7 +343,18 @@ void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
struct ext_password_data *ext);
int eapol_sm_failed(struct eapol_sm *sm);
void eapol_sm_erp_flush(struct eapol_sm *sm);
-int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
+struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm);
+void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
+ size_t len);
+int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi,
+ size_t *len);
+int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num);
+int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+ const u8 **username, size_t *username_len,
+ const u8 **realm, size_t *realm_len,
+ u16 *erp_next_seq_num, const u8 **rrk,
+ size_t *rrk_len);
+
#else /* IEEE8021X_EAPOL */
static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
{
@@ -438,6 +464,28 @@ static inline int eapol_sm_failed(struct eapol_sm *sm)
static inline void eapol_sm_erp_flush(struct eapol_sm *sm)
{
}
+static inline struct wpabuf *
+eapol_sm_build_erp_reauth_start(struct eapol_sm *sm)
+{
+ return NULL;
+}
+static inline void eapol_sm_process_erp_finish(struct eapol_sm *sm,
+ const u8 *buf, size_t len)
+{
+}
+static inline int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm,
+ u16 next_seq_num)
+{
+ return -1;
+}
+static inline int
+eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
+ const u8 **username, size_t *username_len,
+ const u8 **realm, size_t *realm_len,
+ u16 *erp_next_seq_num, const u8 **rrk, size_t *rrk_len)
+{
+ return -1;
+}
#endif /* IEEE8021X_EAPOL */
#endif /* EAPOL_SUPP_SM_H */
diff --git a/src/fst/fst_ctrl_aux.h b/src/fst/fst_ctrl_aux.h
index e2133f5062bd..0aff5d061eae 100644
--- a/src/fst/fst_ctrl_aux.h
+++ b/src/fst/fst_ctrl_aux.h
@@ -35,6 +35,17 @@ enum fst_event_type {
* more info */
};
+enum fst_reason {
+ REASON_TEARDOWN,
+ REASON_SETUP,
+ REASON_SWITCH,
+ REASON_STT,
+ REASON_REJECT,
+ REASON_ERROR_PARAMS,
+ REASON_RESET,
+ REASON_DETACH_IFACE,
+};
+
enum fst_initiator {
FST_INITIATOR_UNDEFINED,
FST_INITIATOR_LOCAL,
@@ -57,16 +68,7 @@ union fst_event_extra {
enum fst_session_state new_state;
union fst_session_state_switch_extra {
struct {
- enum fst_reason {
- REASON_TEARDOWN,
- REASON_SETUP,
- REASON_SWITCH,
- REASON_STT,
- REASON_REJECT,
- REASON_ERROR_PARAMS,
- REASON_RESET,
- REASON_DETACH_IFACE,
- } reason;
+ enum fst_reason reason;
u8 reject_code; /* REASON_REJECT */
/* REASON_SWITCH,
* REASON_TEARDOWN,
diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c
index 7820e586629f..7df3362b6e93 100644
--- a/src/fst/fst_ctrl_iface.c
+++ b/src/fst/fst_ctrl_iface.c
@@ -49,7 +49,7 @@ static Boolean format_session_state_extra(const union fst_event_extra *extra,
if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
os_snprintf(reject_str, sizeof(reject_str), "%u",
ss->extra.to_initial.reject_code);
- /* no break */
+ /* fall through */
case REASON_TEARDOWN:
case REASON_SWITCH:
switch (ss->extra.to_initial.initiator) {
diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c
index 321d40d50cd2..a4ae016d9163 100644
--- a/src/fst/fst_group.c
+++ b/src/fst/fst_group.c
@@ -29,7 +29,7 @@ static void fst_dump_mb_ies(const char *group_id, const char *ifname,
const struct multi_band_ie *mbie =
(const struct multi_band_ie *) p;
WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
- WPA_ASSERT(2 + mbie->len >= sizeof(*mbie));
+ WPA_ASSERT(2U + mbie->len >= sizeof(*mbie));
fst_printf(MSG_WARNING,
"%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
diff --git a/src/fst/fst_iface.h b/src/fst/fst_iface.h
index 0eb27325a2b8..cbaa7d81788d 100644
--- a/src/fst/fst_iface.h
+++ b/src/fst/fst_iface.h
@@ -106,7 +106,7 @@ static inline void fst_iface_update_mb_ie(struct fst_iface *i,
const u8 *addr,
const u8 *buf, size_t size)
{
- return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
+ i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
}
static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i,
diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c
index 76e2c78f4ff6..a02a93e76e43 100644
--- a/src/fst/fst_session.c
+++ b/src/fst/fst_session.c
@@ -756,8 +756,6 @@ struct fst_session * fst_session_create(struct fst_group *g)
struct fst_session *s;
u32 id;
- WPA_ASSERT(!is_zero_ether_addr(own_addr));
-
id = fst_find_free_session_id();
if (id == FST_INVALID_SESSION_ID) {
fst_printf(MSG_ERROR, "Cannot assign new session ID");
diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h
index 2a452458214b..53871774b4e7 100644
--- a/src/l2_packet/l2_packet.h
+++ b/src/l2_packet/l2_packet.h
@@ -42,6 +42,7 @@ struct l2_ethhdr {
enum l2_packet_filter_type {
L2_PACKET_FILTER_DHCP,
L2_PACKET_FILTER_NDISC,
+ L2_PACKET_FILTER_PKTTYPE,
};
/**
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index a7a300e568e6..291c9dd263a6 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -84,6 +84,26 @@ static const struct sock_fprog ndisc_sock_filter = {
.filter = ndisc_sock_filter_insns,
};
+/* drop packet if skb->pkt_type is PACKET_OTHERHOST (0x03). Generated by:
+ * $ bpfc - <<EOF
+ * > ldb #type
+ * > jeq #0x03, drop
+ * > pass: ret #-1
+ * > drop: ret #0
+ * > EOF
+ */
+static struct sock_filter pkt_type_filter_insns[] = {
+ { 0x30, 0, 0, 0xfffff004 },
+ { 0x15, 1, 0, 0x00000003 },
+ { 0x6, 0, 0, 0xffffffff },
+ { 0x6, 0, 0, 0x00000000 },
+};
+
+static const struct sock_fprog pkt_type_sock_filter = {
+ .len = ARRAY_SIZE(pkt_type_filter_insns),
+ .filter = pkt_type_filter_insns,
+};
+
int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
{
@@ -96,6 +116,9 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
const u8 *buf, size_t len)
{
int ret;
+
+ if (TEST_FAIL())
+ return -1;
if (l2 == NULL)
return -1;
if (l2->l2_hdr) {
@@ -458,6 +481,9 @@ int l2_packet_set_packet_filter(struct l2_packet_data *l2,
{
const struct sock_fprog *sock_filter;
+ if (TEST_FAIL())
+ return -1;
+
switch (type) {
case L2_PACKET_FILTER_DHCP:
sock_filter = &dhcp_sock_filter;
@@ -465,6 +491,9 @@ int l2_packet_set_packet_filter(struct l2_packet_data *l2,
case L2_PACKET_FILTER_NDISC:
sock_filter = &ndisc_sock_filter;
break;
+ case L2_PACKET_FILTER_PKTTYPE:
+ sock_filter = &pkt_type_sock_filter;
+ break;
default:
return -1;
}
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
index e26ca20a8625..ce86802c2353 100644
--- a/src/l2_packet/l2_packet_privsep.c
+++ b/src/l2_packet/l2_packet_privsep.c
@@ -51,7 +51,7 @@ static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd,
return 0;
}
-
+
int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
{
os_memcpy(addr, l2->own_addr, ETH_ALEN);
@@ -258,7 +258,7 @@ void l2_packet_deinit(struct l2_packet_data *l2)
unlink(l2->own_socket_path);
os_free(l2->own_socket_path);
}
-
+
os_free(l2);
}
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 996b4e824986..b4660c4f9616 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -711,6 +711,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
struct p2p_message msg;
const u8 *p2p_dev_addr;
int wfd_changed;
+ int dev_name_changed;
int i;
struct os_reltime time_now;
@@ -821,6 +822,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
}
dev->info.level = level;
+ dev_name_changed = os_strncmp(dev->info.device_name, msg.device_name,
+ WPS_DEV_NAME_MAX_LEN) != 0;
+
p2p_copy_wps_info(p2p, dev, 0, &msg);
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
@@ -839,9 +843,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
wfd_changed = p2p_compare_wfd_info(dev, &msg);
- if (msg.wfd_subelems) {
+ if (wfd_changed) {
wpabuf_free(dev->info.wfd_subelems);
- dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+ if (msg.wfd_subelems)
+ dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+ else
+ dev->info.wfd_subelems = NULL;
}
if (scan_res) {
@@ -855,6 +862,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
p2p_update_peer_vendor_elems(dev, ies, ies_len);
if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
+ !dev_name_changed &&
(!msg.adv_service_instance ||
(dev->flags & P2P_DEV_P2PS_REPORTED)))
return 0;
@@ -1002,8 +1010,16 @@ static void p2p_search(struct p2p_data *p2p)
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
- if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
- (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+ if (p2p->find_pending_full &&
+ (p2p->find_type == P2P_FIND_PROGRESSIVE ||
+ p2p->find_type == P2P_FIND_START_WITH_FULL)) {
+ type = P2P_SCAN_FULL;
+ p2p_dbg(p2p, "Starting search (pending full scan)");
+ p2p->find_pending_full = 0;
+ } else if ((p2p->find_type == P2P_FIND_PROGRESSIVE &&
+ (freq = p2p_get_next_prog_freq(p2p)) > 0) ||
+ (p2p->find_type == P2P_FIND_START_WITH_FULL &&
+ (freq = p2p->find_specified_freq) > 0)) {
type = P2P_SCAN_SOCIAL_PLUS_ONE;
p2p_dbg(p2p, "Starting search (+ freq %u)", freq);
} else {
@@ -1165,12 +1181,11 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
p2p_free_req_dev_types(p2p);
if (req_dev_types && num_req_dev_types) {
- p2p->req_dev_types = os_malloc(num_req_dev_types *
+ p2p->req_dev_types = os_memdup(req_dev_types,
+ num_req_dev_types *
WPS_DEV_TYPE_LEN);
if (p2p->req_dev_types == NULL)
return -1;
- os_memcpy(p2p->req_dev_types, req_dev_types,
- num_req_dev_types * WPS_DEV_TYPE_LEN);
p2p->num_req_dev_types = num_req_dev_types;
}
@@ -1227,7 +1242,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
p2p->pending_listen_freq = 0;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->find_pending_full = 0;
p2p->find_type = type;
+ if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480)
+ p2p->find_specified_freq = freq;
+ else
+ p2p->find_specified_freq = 0;
p2p_device_clear_reported(p2p);
os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN);
p2p_set_state(p2p, P2P_SEARCH);
@@ -1243,7 +1263,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
if (freq > 0) {
/*
* Start with the specified channel and then move to
- * social channels only scans.
+ * scans for social channels and this specific channel.
*/
res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
P2P_SCAN_SPECIFIC, freq,
@@ -1272,6 +1292,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
if (res != 0 && p2p->p2p_scan_running) {
p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
/* wait for the previous p2p_scan to complete */
+ if (type == P2P_FIND_PROGRESSIVE ||
+ (type == P2P_FIND_START_WITH_FULL && freq == 0))
+ p2p->find_pending_full = 1;
res = 0; /* do not report failure */
} else if (res != 0) {
p2p_dbg(p2p, "Failed to start p2p_scan");
@@ -1833,6 +1856,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
p2p_clear_timeout(p2p);
p2p->ssid_set = 0;
peer->go_neg_req_sent = 0;
+ peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
peer->wps_method = WPS_NOT_READY;
peer->oob_pw_id = 0;
wpabuf_free(peer->go_neg_conf);
@@ -2822,6 +2846,7 @@ void p2p_service_flush_asp(struct p2p_data *p2p)
}
p2p->p2ps_adv_list = NULL;
+ p2ps_prov_free(p2p);
p2p_dbg(p2p, "All ASP advertisements flushed");
}
@@ -2983,6 +3008,7 @@ void p2p_deinit(struct p2p_data *p2p)
wpabuf_free(p2p->wfd_dev_info);
wpabuf_free(p2p->wfd_assoc_bssid);
wpabuf_free(p2p->wfd_coupled_sink_info);
+ wpabuf_free(p2p->wfd_r2_dev_info);
#endif /* CONFIG_WIFI_DISPLAY */
eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
@@ -3022,6 +3048,10 @@ void p2p_flush(struct p2p_data *p2p)
os_free(p2p->after_scan_tx);
p2p->after_scan_tx = NULL;
p2p->ssid_set = 0;
+ p2ps_prov_free(p2p);
+ p2p_reset_pending_pd(p2p);
+ p2p->override_pref_op_class = 0;
+ p2p->override_pref_channel = 0;
}
@@ -3859,6 +3889,19 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
if (p2p->in_listen)
return 0; /* Internal timeout will trigger the next step */
+ if (p2p->state == P2P_WAIT_PEER_CONNECT && p2p->go_neg_peer &&
+ p2p->pending_listen_freq) {
+ /*
+ * Better wait a bit if the driver is unable to start
+ * offchannel operation for some reason to continue with
+ * P2P_WAIT_PEER_(IDLE/CONNECT) state transitions.
+ */
+ p2p_dbg(p2p,
+ "Listen operation did not seem to start - delay idle phase to avoid busy loop");
+ p2p_set_timeout(p2p, 0, 100000);
+ return 1;
+ }
+
if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
if (p2p->go_neg_peer->connect_reqs >= 120) {
p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
@@ -4803,11 +4846,10 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
struct p2p_channel *n;
if (pref_chan) {
- n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+ n = os_memdup(pref_chan,
+ num_pref_chan * sizeof(struct p2p_channel));
if (n == NULL)
return -1;
- os_memcpy(n, pref_chan,
- num_pref_chan * sizeof(struct p2p_channel));
} else
n = NULL;
@@ -5129,6 +5171,20 @@ int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
}
+int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+ wpabuf_free(p2p->wfd_r2_dev_info);
+ if (elem) {
+ p2p->wfd_r2_dev_info = wpabuf_dup(elem);
+ if (p2p->wfd_r2_dev_info == NULL)
+ return -1;
+ } else
+ p2p->wfd_r2_dev_info = NULL;
+
+ return 0;
+}
+
+
int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
{
wpabuf_free(p2p->wfd_assoc_bssid);
@@ -5510,6 +5566,14 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
}
+void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class,
+ u8 chan)
+{
+ p2p->override_pref_op_class = op_class;
+ p2p->override_pref_channel = chan;
+}
+
+
struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p,
unsigned int freq)
{
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 7b18dcfc3ff3..fac5ce05ae6e 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -2266,6 +2266,7 @@ int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie);
int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
const struct wpabuf *elem);
@@ -2373,6 +2374,8 @@ void p2p_expire_peers(struct p2p_data *p2p);
void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
const unsigned int *pref_freq_list,
unsigned int size);
+void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class,
+ u8 chan);
/**
* p2p_group_get_common_freqs - Get the group common frequencies
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 9f0b3f3d37a4..65ab4b8d3fd6 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -315,7 +315,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
group_capab);
p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
- if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
+ if (p2p->override_pref_op_class) {
+ p2p_dbg(p2p, "Override operating channel preference");
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->override_pref_op_class,
+ p2p->override_pref_channel);
+ } else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
p2p_dbg(p2p, "Omit Operating Channel attribute");
} else {
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
@@ -562,26 +567,11 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
* also supported by the peer device.
*/
for (i = 0; i < size && !found; i++) {
- /*
- * Make sure that the common frequency is:
- * 1. Supported by peer
- * 2. Allowed for P2P use.
- */
+ /* Make sure that the common frequency is supported by peer. */
oper_freq = freq_list[i];
if (p2p_freq_to_channel(oper_freq, &op_class,
- &op_channel) < 0) {
- p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq);
- continue;
- }
- if (!p2p_channels_includes(&p2p->cfg->channels,
- op_class, op_channel) &&
- (go || !p2p_channels_includes(&p2p->cfg->cli_channels,
- op_class, op_channel))) {
- p2p_dbg(p2p,
- "Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
- oper_freq, op_class, op_channel);
- break;
- }
+ &op_channel) < 0)
+ continue; /* cannot happen due to earlier check */
for (j = 0; j < msg->channel_list_len; j++) {
if (op_channel != msg->channel_list[j])
@@ -602,8 +592,7 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
oper_freq);
} else {
p2p_dbg(p2p,
- "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel",
- dev->oper_freq);
+ "None of our preferred channels are supported by peer!");
}
}
@@ -629,29 +618,9 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
msg->pref_freq_list[2 * j + 1]);
if (freq_list[i] != oper_freq)
continue;
-
- /*
- * Make sure that the found frequency is:
- * 1. Supported
- * 2. Allowed for P2P use.
- */
if (p2p_freq_to_channel(oper_freq, &op_class,
- &op_channel) < 0) {
- p2p_dbg(p2p, "Unsupported frequency %u MHz",
- oper_freq);
- continue;
- }
-
- if (!p2p_channels_includes(&p2p->cfg->channels,
- op_class, op_channel) &&
- (go ||
- !p2p_channels_includes(&p2p->cfg->cli_channels,
- op_class, op_channel))) {
- p2p_dbg(p2p,
- "Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
- oper_freq, op_class, op_channel);
- break;
- }
+ &op_channel) < 0)
+ continue; /* cannot happen */
p2p->op_reg_class = op_class;
p2p->op_channel = op_channel;
os_memcpy(&p2p->channels, &p2p->cfg->channels,
@@ -666,9 +635,7 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
"Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel",
oper_freq);
} else {
- p2p_dbg(p2p,
- "No common preferred channels found! Use: %d MHz for oper_channel",
- dev->oper_freq);
+ p2p_dbg(p2p, "No common preferred channels found!");
}
}
@@ -679,6 +646,8 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go,
unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size;
unsigned int i;
u8 op_class, op_channel;
+ char txt[100], *pos, *end;
+ int res;
/*
* Use the preferred channel list from the driver only if there is no
@@ -694,6 +663,39 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go,
if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size,
freq_list))
return;
+ /* Filter out frequencies that are not acceptable for P2P use */
+ i = 0;
+ while (i < size) {
+ if (p2p_freq_to_channel(freq_list[i], &op_class,
+ &op_channel) < 0 ||
+ (!p2p_channels_includes(&p2p->cfg->channels,
+ op_class, op_channel) &&
+ (go || !p2p_channels_includes(&p2p->cfg->cli_channels,
+ op_class, op_channel)))) {
+ p2p_dbg(p2p,
+ "Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)",
+ freq_list[i], go);
+ if (size - i - 1 > 0)
+ os_memmove(&freq_list[i], &freq_list[i + 1], size - i - 1);
+ size--;
+ continue;
+ }
+
+ /* Preferred frequency is acceptable for P2P use */
+ i++;
+ }
+
+ pos = txt;
+ end = pos + sizeof(txt);
+ for (i = 0; i < size; i++) {
+ res = os_snprintf(pos, end - pos, " %u", freq_list[i]);
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+ *pos = '\0';
+ p2p_dbg(p2p, "Local driver frequency preference (size=%u):%s",
+ size, txt);
/*
* Check if peer's preference of operating channel is in
@@ -703,20 +705,14 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go,
if (freq_list[i] == (unsigned int) dev->oper_freq)
break;
}
- if (i != size) {
+ if (i != size &&
+ p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) == 0) {
/* Peer operating channel preference matches our preference */
- if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) <
- 0) {
- p2p_dbg(p2p,
- "Peer operating channel preference is unsupported frequency %u MHz",
- freq_list[i]);
- } else {
- p2p->op_reg_class = op_class;
- p2p->op_channel = op_channel;
- os_memcpy(&p2p->channels, &p2p->cfg->channels,
- sizeof(struct p2p_channels));
- return;
- }
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ return;
}
p2p_dbg(p2p,
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 051b4e391505..16c28a0d95ee 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -367,6 +367,8 @@ wifi_display_build_go_ie(struct p2p_group *group)
return NULL;
if (group->p2p->wfd_dev_info)
wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
+ if (group->p2p->wfd_r2_dev_info)
+ wpabuf_put_buf(wfd_subelems, group->p2p->wfd_r2_dev_info);
if (group->p2p->wfd_assoc_bssid)
wpabuf_put_buf(wfd_subelems,
group->p2p->wfd_assoc_bssid);
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 47524d4991a5..6a4d751c090a 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -437,9 +437,11 @@ struct p2p_data {
int inv_persistent;
enum p2p_discovery_type find_type;
+ int find_specified_freq;
unsigned int last_p2p_find_timeout;
u8 last_prog_scan_class;
u8 last_prog_scan_chan;
+ unsigned int find_pending_full:1;
int p2p_scan_running;
enum p2p_after_scan {
P2P_AFTER_SCAN_NOTHING,
@@ -545,6 +547,7 @@ struct p2p_data {
struct wpabuf *wfd_dev_info;
struct wpabuf *wfd_assoc_bssid;
struct wpabuf *wfd_coupled_sink_info;
+ struct wpabuf *wfd_r2_dev_info;
#endif /* CONFIG_WIFI_DISPLAY */
u16 authorized_oob_dev_pw_id;
@@ -553,6 +556,10 @@ struct p2p_data {
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
unsigned int num_pref_freq;
+
+ /* Override option for preferred operating channel in GO Negotiation */
+ u8 override_pref_op_class;
+ u8 override_pref_channel;
};
/**
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 93a0535f873a..3994ec03f86b 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -1163,6 +1163,9 @@ out:
msg.group_id, msg.group_id_len);
}
+ if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+ p2ps_prov_free(p2p);
+
if (reject == P2P_SC_SUCCESS) {
switch (config_methods) {
case WPS_CONFIG_DISPLAY:
@@ -1581,7 +1584,7 @@ out:
report_config_methods);
if (p2p->state == P2P_PD_DURING_FIND) {
- p2p_clear_timeout(p2p);
+ p2p_stop_listen_for_freq(p2p, 0);
p2p_continue_find(p2p);
}
}
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index a8bc5ba7f344..b9e753f20516 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -426,6 +426,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
{
struct wpabuf *resp;
size_t max_len;
+ unsigned int wait_time = 200;
/*
* In the 60 GHz, we have a smaller maximum frame length for management
@@ -460,6 +461,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
1, p2p->srv_update_indic, NULL);
} else {
p2p_dbg(p2p, "SD response fits in initial response");
+ wait_time = 0; /* no more SD frames in the sequence */
resp = p2p_build_sd_response(dialog_token,
WLAN_STATUS_SUCCESS, 0,
p2p->srv_update_indic, resp_tlvs);
@@ -470,7 +472,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr,
p2p->cfg->dev_addr,
- wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+ wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0)
p2p_dbg(p2p, "Failed to send Action frame");
wpabuf_free(resp);
@@ -623,6 +625,7 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
u8 dialog_token;
size_t frag_len, max_len;
int more = 0;
+ unsigned int wait_time = 200;
wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
if (len < 1)
@@ -675,12 +678,13 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
p2p_dbg(p2p, "All fragments of SD response sent");
wpabuf_free(p2p->sd_resp);
p2p->sd_resp = NULL;
+ wait_time = 0; /* no more SD frames in the sequence */
}
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
p2p->cfg->dev_addr,
- wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+ wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0)
p2p_dbg(p2p, "Failed to send Action frame");
wpabuf_free(resp);
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
index e294e6466285..360fcd3f5fcd 100644
--- a/src/pae/ieee802_1x_cp.c
+++ b/src/pae/ieee802_1x_cp.c
@@ -159,6 +159,7 @@ SM_STATE(CP, ALLOWED)
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+ secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
}
@@ -177,6 +178,7 @@ SM_STATE(CP, AUTHENTICATED)
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+ secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
}
@@ -203,6 +205,7 @@ SM_STATE(CP, SECURED)
secy_cp_control_confidentiality_offset(sm->kay,
sm->confidentiality_offset);
secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+ secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
}
@@ -466,6 +469,7 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
wpa_printf(MSG_DEBUG, "CP: state machine created");
secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+ secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index a8e7efc9b3bd..cda23fcab41a 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -45,6 +45,14 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = {
.sak_len = DEFAULT_SA_KEY_LEN,
.index = 0,
},
+ /* GCM-AES-256 */
+ {
+ .id = CS_ID_GCM_AES_256,
+ .name = CS_NAME_GCM_AES_256,
+ .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50,
+ .sak_len = 32,
+ .index = 1 /* index */
+ },
};
#define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
#define DEFAULT_CS_INDEX 0
@@ -245,13 +253,15 @@ ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body)
* ieee802_1x_kay_get_participant -
*/
static struct ieee802_1x_mka_participant *
-ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn)
+ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn,
+ size_t len)
{
struct ieee802_1x_mka_participant *participant;
dl_list_for_each(participant, &kay->participant_list,
struct ieee802_1x_mka_participant, list) {
- if (os_memcmp(participant->ckn.name, ckn,
+ if (participant->ckn.len == len &&
+ os_memcmp(participant->ckn.name, ckn,
participant->ckn.len) == 0)
return participant;
}
@@ -379,6 +389,17 @@ ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
}
+u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci)
+{
+ struct ieee802_1x_mka_sci tmp;
+
+ os_memcpy(tmp.addr, sci->addr, ETH_ALEN);
+ tmp.port = sci->port;
+
+ return *((u64 *) &tmp);
+}
+
+
static Boolean sci_equal(const struct ieee802_1x_mka_sci *a,
const struct ieee802_1x_mka_sci *b)
{
@@ -411,6 +432,8 @@ ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant,
}
+static void ieee802_1x_kay_use_data_key(struct data_key *pkey);
+
/**
* ieee802_1x_kay_init_receive_sa -
*/
@@ -429,6 +452,7 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
return NULL;
}
+ ieee802_1x_kay_use_data_key(key);
psa->pkey = key;
psa->lowest_pn = lowest_pn;
psa->next_pn = lowest_pn;
@@ -440,18 +464,21 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
dl_list_add(&psc->sa_list, &psa->list);
wpa_printf(MSG_DEBUG,
- "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC(channel: %d)",
- an, lowest_pn, psc->channel);
+ "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC",
+ an, lowest_pn);
return psa;
}
+static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey);
+
/**
* ieee802_1x_kay_deinit_receive_sa -
*/
static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
{
+ ieee802_1x_kay_deinit_data_key(psa->pkey);
psa->pkey = NULL;
wpa_printf(MSG_DEBUG,
"KaY: Delete receive SA(an: %hhu) of SC",
@@ -465,8 +492,7 @@ static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
* ieee802_1x_kay_init_receive_sc -
*/
static struct receive_sc *
-ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci,
- int channel)
+ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci)
{
struct receive_sc *psc;
@@ -480,19 +506,27 @@ ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci,
}
os_memcpy(&psc->sci, psci, sizeof(psc->sci));
- psc->channel = channel;
os_get_time(&psc->created_time);
psc->receiving = FALSE;
dl_list_init(&psc->sa_list);
- wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel);
+ wpa_printf(MSG_DEBUG, "KaY: Create receive SC");
wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci));
return psc;
}
+static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay,
+ struct receive_sa *sa)
+{
+ secy_disable_receive_sa(kay, sa);
+ secy_delete_receive_sa(kay, sa);
+ ieee802_1x_kay_deinit_receive_sa(sa);
+}
+
+
/**
* ieee802_1x_kay_deinit_receive_sc -
**/
@@ -502,14 +536,13 @@ ieee802_1x_kay_deinit_receive_sc(
{
struct receive_sa *psa, *pre_sa;
- wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)",
- psc->channel);
+ wpa_printf(MSG_DEBUG, "KaY: Delete receive SC");
dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
- list) {
- secy_disable_receive_sa(participant->kay, psa);
- ieee802_1x_kay_deinit_receive_sa(psa);
- }
+ list)
+ ieee802_1x_delete_receive_sa(participant->kay, psa);
+
dl_list_del(&psc->list);
+ secy_delete_receive_sc(participant->kay, psc);
os_free(psc);
}
@@ -552,7 +585,6 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
{
struct ieee802_1x_kay_peer *peer;
struct receive_sc *rxsc;
- u32 sc_ch = 0;
peer = ieee802_1x_kay_create_peer(mi, mn);
if (!peer)
@@ -561,9 +593,7 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
os_memcpy(&peer->sci, &participant->current_peer_sci,
sizeof(peer->sci));
- secy_get_available_receive_sc(participant->kay, &sc_ch);
-
- rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
+ rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci);
if (!rxsc) {
os_free(peer);
return NULL;
@@ -611,12 +641,12 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
{
struct ieee802_1x_kay_peer *peer;
struct receive_sc *rxsc;
- u32 sc_ch = 0;
peer = ieee802_1x_kay_get_potential_peer(participant, mi);
+ if (!peer)
+ return NULL;
- rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci,
- sc_ch);
+ rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci);
if (!rxsc)
return NULL;
@@ -631,8 +661,6 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
dl_list_del(&peer->list);
dl_list_add_tail(&participant->live_peers, &peer->list);
- secy_get_available_receive_sc(participant->kay, &sc_ch);
-
dl_list_add(&participant->rxsc_list, &rxsc->list);
secy_create_receive_sc(participant->kay, rxsc);
@@ -730,6 +758,8 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
struct ieee802_1x_mka_participant *participant;
const struct ieee802_1x_mka_basic_body *body;
struct ieee802_1x_kay_peer *peer;
+ size_t ckn_len;
+ size_t body_len;
body = (const struct ieee802_1x_mka_basic_body *) mka_msg;
@@ -743,7 +773,15 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
return NULL;
}
- participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+ body_len = get_mka_param_body_len(body);
+ if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
+ wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
+ body_len);
+ return NULL;
+ }
+ ckn_len = body_len -
+ (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
+ participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
if (!participant) {
wpa_printf(MSG_DEBUG, "Peer is not included in my CA");
return NULL;
@@ -946,21 +984,19 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
body_len = get_mka_param_body_len(hdr);
body_type = get_mka_param_body_type(hdr);
- if (body_type != MKA_LIVE_PEER_LIST &&
- body_type != MKA_POTENTIAL_PEER_LIST)
- continue;
-
- ieee802_1x_mka_dump_peer_body(
- (struct ieee802_1x_mka_peer_body *)pos);
-
- if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+ if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) {
wpa_printf(MSG_ERROR,
"KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
left_len, MKA_HDR_LEN,
- body_len, DEFAULT_ICV_LEN);
- continue;
+ MKA_ALIGN_LENGTH(body_len),
+ DEFAULT_ICV_LEN);
+ return FALSE;
}
+ if (body_type != MKA_LIVE_PEER_LIST &&
+ body_type != MKA_POTENTIAL_PEER_LIST)
+ continue;
+
if ((body_len % 16) != 0) {
wpa_printf(MSG_ERROR,
"KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
@@ -968,6 +1004,9 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
continue;
}
+ ieee802_1x_mka_dump_peer_body(
+ (struct ieee802_1x_mka_peer_body *)pos);
+
for (i = 0; i < body_len;
i += sizeof(struct ieee802_1x_mka_peer_id)) {
const struct ieee802_1x_mka_peer_id *peer_mi;
@@ -1312,8 +1351,8 @@ ieee802_1x_mka_decode_sak_use_body(
}
}
- /* check old key is valid */
- if (body->otx || body->orx) {
+ /* check old key is valid (but only if we remember our old key) */
+ if (participant->oki.kn != 0 && (body->otx || body->orx)) {
if (os_memcmp(participant->oki.mi, body->osrv_mi,
sizeof(participant->oki.mi)) != 0 ||
be_to_host32(body->okn) != participant->oki.kn ||
@@ -1544,7 +1583,7 @@ ieee802_1x_mka_decode_dist_sak_body(
ieee802_1x_cp_connect_authenticated(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec");
- participant->to_use_sak = TRUE;
+ participant->to_use_sak = FALSE;
return 0;
}
@@ -1595,7 +1634,8 @@ ieee802_1x_mka_decode_dist_sak_body(
os_free(unwrap_sak);
return -1;
}
- wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len);
+ wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK:",
+ unwrap_sak, sak_len);
sa_key = os_zalloc(sizeof(*sa_key));
if (!sa_key) {
@@ -1614,6 +1654,7 @@ ieee802_1x_mka_decode_dist_sak_body(
sa_key->an = body->dan;
ieee802_1x_kay_init_data_key(sa_key);
+ ieee802_1x_kay_use_data_key(sa_key);
dl_list_add(&participant->sak_list, &sa_key->list);
ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
@@ -1625,6 +1666,7 @@ ieee802_1x_mka_decode_dist_sak_body(
ieee802_1x_cp_signal_newsak(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
+ kay->rcvd_keys++;
participant->to_use_sak = TRUE;
return 0;
@@ -1875,7 +1917,17 @@ static struct mka_param_body_handler mka_body_handler[] = {
/**
- * ieee802_1x_kay_deinit_data_key -
+ * ieee802_1x_kay_use_data_key - Take reference on a key
+ */
+static void ieee802_1x_kay_use_data_key(struct data_key *pkey)
+{
+ pkey->user++;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_data_key - Release reference on a key and
+ * free if there are no remaining users
*/
static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
{
@@ -1886,7 +1938,6 @@ static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
if (pkey->user > 1)
return;
- dl_list_del(&pkey->list);
os_free(pkey->key);
os_free(pkey);
}
@@ -1975,7 +2026,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
wpa_printf(MSG_ERROR, "KaY: SAK Length not support");
goto fail;
}
- wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
+ wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
os_free(context);
context = NULL;
@@ -1996,7 +2047,9 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
participant->new_key = sa_key;
+ ieee802_1x_kay_use_data_key(sa_key);
dl_list_add(&participant->sak_list, &sa_key->list);
+
ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
ieee802_1x_cp_sm_step(kay->cp);
ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality);
@@ -2049,6 +2102,7 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
struct ieee802_1x_kay_peer *key_server = NULL;
struct ieee802_1x_kay *kay = participant->kay;
Boolean i_is_key_server;
+ int priority_comparison;
if (participant->is_obliged_key_server) {
participant->new_sak = TRUE;
@@ -2079,8 +2133,14 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
tmp.key_server_priority = kay->actor_priority;
os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci));
- if (compare_priorities(&tmp, key_server) < 0)
+ priority_comparison = compare_priorities(&tmp, key_server);
+ if (priority_comparison < 0) {
i_is_key_server = TRUE;
+ } else if (priority_comparison == 0) {
+ wpa_printf(MSG_WARNING,
+ "KaY: Cannot elect key server between me and peer, duplicate MAC detected");
+ key_server = NULL;
+ }
} else if (participant->can_be_key_server) {
i_is_key_server = TRUE;
}
@@ -2280,6 +2340,16 @@ ieee802_1x_participant_send_mkpdu(
static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
+
+static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *sa)
+{
+ secy_disable_transmit_sa(kay, sa);
+ secy_delete_transmit_sa(kay, sa);
+ ieee802_1x_kay_deinit_transmit_sa(sa);
+}
+
+
/**
* ieee802_1x_participant_timer -
*/
@@ -2323,7 +2393,6 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
&participant->rxsc_list,
struct receive_sc, list) {
if (sci_equal(&rxsc->sci, &peer->sci)) {
- secy_delete_receive_sc(kay, rxsc);
ieee802_1x_kay_deinit_receive_sc(
participant, rxsc);
}
@@ -2340,7 +2409,13 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
participant->advised_capability =
MACSEC_CAP_NOT_IMPLEMENTED;
participant->to_use_sak = FALSE;
- kay->authenticated = TRUE;
+ participant->ltx = FALSE;
+ participant->lrx = FALSE;
+ participant->otx = FALSE;
+ participant->orx = FALSE;
+ participant->is_key_server = FALSE;
+ participant->is_elected = FALSE;
+ kay->authenticated = FALSE;
kay->secured = FALSE;
kay->failed = FALSE;
kay->ltx_kn = 0;
@@ -2354,11 +2429,10 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
dl_list_for_each_safe(txsa, pre_txsa,
&participant->txsc->sa_list,
struct transmit_sa, list) {
- secy_disable_transmit_sa(kay, txsa);
- ieee802_1x_kay_deinit_transmit_sa(txsa);
+ ieee802_1x_delete_transmit_sa(kay, txsa);
}
- ieee802_1x_cp_connect_authenticated(kay->cp);
+ ieee802_1x_cp_connect_pending(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
} else {
ieee802_1x_kay_elect_key_server(participant);
@@ -2385,7 +2459,8 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
participant->new_sak = FALSE;
}
- if (participant->retry_count < MAX_RETRY_CNT) {
+ if (participant->retry_count < MAX_RETRY_CNT ||
+ participant->mode == PSK) {
ieee802_1x_participant_send_mkpdu(participant);
participant->retry_count++;
}
@@ -2429,6 +2504,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
psa->confidentiality = FALSE;
psa->an = an;
+ ieee802_1x_kay_use_data_key(key);
psa->pkey = key;
psa->next_pn = next_PN;
psa->sc = psc;
@@ -2438,8 +2514,8 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
dl_list_add(&psc->sa_list, &psa->list);
wpa_printf(MSG_DEBUG,
- "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC(channel: %d)",
- an, next_PN, psc->channel);
+ "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC",
+ an, next_PN);
return psa;
}
@@ -2450,6 +2526,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
*/
static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
{
+ ieee802_1x_kay_deinit_data_key(psa->pkey);
psa->pkey = NULL;
wpa_printf(MSG_DEBUG,
"KaY: Delete transmit SA(an: %hhu) of SC",
@@ -2463,8 +2540,7 @@ static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
* init_transmit_sc -
*/
static struct transmit_sc *
-ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci,
- int channel)
+ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci)
{
struct transmit_sc *psc;
@@ -2474,7 +2550,6 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci,
return NULL;
}
os_memcpy(&psc->sci, sci, sizeof(psc->sci));
- psc->channel = channel;
os_get_time(&psc->created_time);
psc->transmitting = FALSE;
@@ -2482,7 +2557,7 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci,
psc->enciphering_sa = FALSE;
dl_list_init(&psc->sa_list);
- wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel);
+ wpa_printf(MSG_DEBUG, "KaY: Create transmit SC");
wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci));
return psc;
@@ -2498,14 +2573,11 @@ ieee802_1x_kay_deinit_transmit_sc(
{
struct transmit_sa *psa, *tmp;
- wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)",
- psc->channel);
- dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa,
- list) {
- secy_disable_transmit_sa(participant->kay, psa);
- ieee802_1x_kay_deinit_transmit_sa(psa);
- }
+ wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC");
+ dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
+ ieee802_1x_delete_transmit_sa(participant->kay, psa);
+ secy_delete_transmit_sc(participant->kay, psc);
os_free(psc);
}
@@ -2582,6 +2654,32 @@ int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
}
+static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an)
+{
+ struct transmit_sa *txsa;
+
+ dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) {
+ if (txsa->an == an)
+ return txsa;
+ }
+
+ return NULL;
+}
+
+
+static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an)
+{
+ struct receive_sa *rxsa;
+
+ dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) {
+ if (rxsa->an == an)
+ return rxsa;
+ }
+
+ return NULL;
+}
+
+
/**
* ieee802_1x_kay_create_sas -
*/
@@ -2616,6 +2714,9 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
}
dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+ while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL)
+ ieee802_1x_delete_receive_sa(kay, rxsa);
+
rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
latest_sak);
if (!rxsa)
@@ -2624,6 +2725,10 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
secy_create_receive_sa(kay, rxsa);
}
+ while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) !=
+ NULL)
+ ieee802_1x_delete_transmit_sa(kay, txsa);
+
txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
1, latest_sak);
if (!txsa)
@@ -2657,20 +2762,16 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
/* remove the transmit sa */
dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
struct transmit_sa, list) {
- if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
- secy_disable_transmit_sa(kay, txsa);
- ieee802_1x_kay_deinit_transmit_sa(txsa);
- }
+ if (is_ki_equal(&txsa->pkey->key_identifier, ki))
+ ieee802_1x_delete_transmit_sa(kay, txsa);
}
/* remove the receive sa */
dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
struct receive_sa, list) {
- if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
- secy_disable_receive_sa(kay, rxsa);
- ieee802_1x_kay_deinit_receive_sa(rxsa);
- }
+ if (is_ki_equal(&rxsa->pkey->key_identifier, ki))
+ ieee802_1x_delete_receive_sa(kay, rxsa);
}
}
@@ -2678,6 +2779,7 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
struct data_key, list) {
if (is_ki_equal(&sa_key->key_identifier, ki)) {
+ dl_list_del(&sa_key->list);
ieee802_1x_kay_deinit_data_key(sa_key);
break;
}
@@ -2759,7 +2861,7 @@ int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
if (!principal)
return -1;
- if (principal->retry_count < MAX_RETRY_CNT) {
+ if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) {
ieee802_1x_participant_send_mkpdu(principal);
principal->retry_count++;
}
@@ -2782,6 +2884,7 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
size_t mka_msg_len;
struct ieee802_1x_mka_participant *participant;
size_t body_len;
+ size_t ckn_len;
u8 icv[MAX_ICV_LEN];
u8 *msg_icv;
@@ -2821,8 +2924,22 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
return -1;
}
+ if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
+ wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
+ body_len);
+ return -1;
+ }
+ ckn_len = body_len -
+ (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
+ if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) {
+ wpa_printf(MSG_ERROR,
+ "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)",
+ ckn_len, MAX_CKN_LEN);
+ return -1;
+ }
+
/* CKN should be owned by I */
- participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+ participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
if (!participant) {
wpa_printf(MSG_DEBUG, "CKN is not included in my CA");
return -1;
@@ -2945,7 +3062,7 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
"KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
left_len, MKA_HDR_LEN,
body_len, DEFAULT_ICV_LEN);
- continue;
+ return -1;
}
if (handled[body_type])
@@ -3020,13 +3137,14 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
*/
struct ieee802_1x_kay *
ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
- const char *ifname, const u8 *addr)
+ u16 port, u8 priority, const char *ifname, const u8 *addr)
{
struct ieee802_1x_kay *kay;
kay = os_zalloc(sizeof(*kay));
if (!kay) {
wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+ os_free(ctx);
return NULL;
}
@@ -3042,8 +3160,8 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
- kay->actor_sci.port = host_to_be16(0x0001);
- kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+ kay->actor_sci.port = host_to_be16(port ? port : 0x0001);
+ kay->actor_priority = priority;
/* While actor acts as a key server, shall distribute sakey */
kay->dist_kn = 1;
@@ -3060,7 +3178,12 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
dl_list_init(&kay->participant_list);
- if (policy == DO_NOT_SECURE) {
+ if (policy != DO_NOT_SECURE &&
+ secy_get_capability(kay, &kay->macsec_capable) < 0)
+ goto error;
+
+ if (policy == DO_NOT_SECURE ||
+ kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
kay->macsec_desired = FALSE;
kay->macsec_protect = FALSE;
@@ -3069,29 +3192,32 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
kay->macsec_replay_window = 0;
kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
} else {
- kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
kay->macsec_desired = TRUE;
kay->macsec_protect = TRUE;
+ kay->macsec_encrypt = policy == SHOULD_ENCRYPT;
kay->macsec_validate = Strict;
kay->macsec_replay_protect = FALSE;
kay->macsec_replay_window = 0;
- kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+ if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF)
+ kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+ else
+ kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
}
wpa_printf(MSG_DEBUG, "KaY: state machine created");
/* Initialize the SecY must be prio to CP, as CP will control SecY */
- secy_init_macsec(kay);
- secy_get_available_transmit_sc(kay, &kay->sc_ch);
+ if (secy_init_macsec(kay) < 0) {
+ wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec");
+ goto error;
+ }
wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
/* init CP */
kay->cp = ieee802_1x_cp_sm_init(kay);
- if (kay->cp == NULL) {
- ieee802_1x_kay_deinit(kay);
- return NULL;
- }
+ if (kay->cp == NULL)
+ goto error;
if (policy == DO_NOT_SECURE) {
ieee802_1x_cp_connect_authenticated(kay->cp);
@@ -3102,12 +3228,15 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
if (kay->l2_mka == NULL) {
wpa_printf(MSG_WARNING,
"KaY: Failed to initialize L2 packet processing for MKA packet");
- ieee802_1x_kay_deinit(kay);
- return NULL;
+ goto error;
}
}
return kay;
+
+error:
+ ieee802_1x_kay_deinit(kay);
+ return NULL;
}
@@ -3148,8 +3277,9 @@ ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
* ieee802_1x_kay_create_mka -
*/
struct ieee802_1x_mka_participant *
-ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn,
- struct mka_key *cak, u32 life,
+ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
+ const struct mka_key_name *ckn,
+ const struct mka_key *cak, u32 life,
enum mka_created_mode mode, Boolean is_authenticator)
{
struct ieee802_1x_mka_participant *participant;
@@ -3243,8 +3373,7 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn,
dl_list_init(&participant->sak_list);
participant->new_key = NULL;
dl_list_init(&participant->rxsc_list);
- participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci,
- kay->sc_ch);
+ participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci);
secy_cp_control_protect_frames(kay, kay->macsec_protect);
secy_cp_control_replay(kay, kay->macsec_replay_protect,
kay->macsec_replay_window);
@@ -3281,8 +3410,17 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn,
usecs = os_random() % (MKA_HELLO_TIME * 1000);
eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
participant, NULL);
- participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
- usecs / 1000000;
+
+ /* Disable MKA lifetime for PSK mode.
+ * The peer(s) can take a long time to come up, because we
+ * create a "standby" MKA, and we need it to remain live until
+ * some peer appears.
+ */
+ if (mode != PSK) {
+ participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
+ usecs / 1000000;
+ }
+ participant->mode = mode;
return participant;
@@ -3309,7 +3447,7 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
wpa_printf(MSG_DEBUG, "KaY: participant removed");
/* get the participant */
- participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+ participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
if (!participant) {
wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
ckn->name, ckn->len);
@@ -3340,16 +3478,13 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
sak = dl_list_entry(participant->sak_list.next,
struct data_key, list);
dl_list_del(&sak->list);
- os_free(sak->key);
- os_free(sak);
+ ieee802_1x_kay_deinit_data_key(sak);
}
while (!dl_list_empty(&participant->rxsc_list)) {
rxsc = dl_list_entry(participant->rxsc_list.next,
struct receive_sc, list);
- secy_delete_receive_sc(kay, rxsc);
ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
}
- secy_delete_transmit_sc(kay, participant->txsc);
ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
os_memset(&participant->cak, 0, sizeof(participant->cak));
@@ -3371,7 +3506,7 @@ void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
if (!kay || !ckn)
return;
- participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+ participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
if (!participant)
return;
@@ -3409,6 +3544,7 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
unsigned int cs_index)
{
struct ieee802_1x_mka_participant *participant;
+ enum macsec_cap secy_cap;
if (!kay)
return -1;
@@ -3427,6 +3563,12 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
kay->macsec_csindex = cs_index;
kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
+ if (secy_get_capability(kay, &secy_cap) < 0)
+ return -3;
+
+ if (kay->macsec_capable > secy_cap)
+ kay->macsec_capable = secy_cap;
+
participant = ieee802_1x_kay_get_principal_participant(kay);
if (participant) {
wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
@@ -3435,3 +3577,51 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
return 0;
}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details
+ * @sm: Pointer to KaY allocated with ieee802_1x_kay_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query KAY status information. This function fills in a text area with current
+ * status information. If the buffer (buf) is not large enough, status
+ * information will be truncated to fit the buffer.
+ */
+int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
+ size_t buflen)
+{
+ int len;
+
+ if (!kay)
+ return 0;
+
+ len = os_snprintf(buf, buflen,
+ "PAE KaY status=%s\n"
+ "Authenticated=%s\n"
+ "Secured=%s\n"
+ "Failed=%s\n"
+ "Actor Priority=%u\n"
+ "Key Server Priority=%u\n"
+ "Is Key Server=%s\n"
+ "Number of Keys Distributed=%u\n"
+ "Number of Keys Received=%u\n",
+ kay->active ? "Active" : "Not-Active",
+ kay->authenticated ? "Yes" : "No",
+ kay->secured ? "Yes" : "No",
+ kay->failed ? "Yes" : "No",
+ kay->actor_priority,
+ kay->key_server_priority,
+ kay->is_key_server ? "Yes" : "No",
+ kay->dist_kn - 1,
+ kay->rcvd_keys);
+ if (os_snprintf_error(buflen, len))
+ return 0;
+
+ return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index afbaa336cbda..b2650596cae3 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -15,7 +15,7 @@
struct macsec_init_params;
-#define MI_LEN 12
+#define MI_LEN 12 /* 96-bit Member Identifier */
#define MAX_KEY_LEN 32 /* 32 bytes, 256 bits */
#define MAX_CKN_LEN 32 /* 32 bytes, 256 bits */
@@ -24,6 +24,12 @@ struct macsec_init_params;
#define MKA_LIFE_TIME 6000
#define MKA_SAK_RETIRE_TIME 3000
+/**
+ * struct ieee802_1x_mka_ki - Key Identifier (KI)
+ * @mi: Key Server's Member Identifier
+ * @kn: Key Number, assigned by the Key Server
+ * IEEE 802.1X-2010 9.8 SAK generation, distribution, and selection
+ */
struct ieee802_1x_mka_ki {
u8 mi[MI_LEN];
u32 kn;
@@ -49,6 +55,84 @@ enum mka_created_mode {
EAP_EXCHANGE,
};
+struct data_key {
+ u8 *key;
+ int key_len;
+ struct ieee802_1x_mka_ki key_identifier;
+ enum confidentiality_offset confidentiality_offset;
+ u8 an;
+ Boolean transmits;
+ Boolean receives;
+ struct os_time created_time;
+ u32 next_pn;
+
+ /* not defined data */
+ Boolean rx_latest;
+ Boolean tx_latest;
+
+ int user;
+
+ struct dl_list list;
+};
+
+/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sc {
+ struct ieee802_1x_mka_sci sci; /* const SCI sci */
+ Boolean transmitting; /* bool transmitting (read only) */
+
+ struct os_time created_time; /* Time createdTime */
+
+ u8 encoding_sa; /* AN encodingSA (read only) */
+ u8 enciphering_sa; /* AN encipheringSA (read only) */
+
+ /* not defined data */
+ struct dl_list list;
+ struct dl_list sa_list;
+};
+
+/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sa {
+ Boolean in_use; /* bool inUse (read only) */
+ u32 next_pn; /* PN nextPN (read only) */
+ struct os_time created_time; /* Time createdTime */
+
+ Boolean enable_transmit; /* bool EnableTransmit */
+
+ u8 an;
+ Boolean confidentiality;
+ struct data_key *pkey;
+
+ struct transmit_sc *sc;
+ struct dl_list list; /* list entry in struct transmit_sc::sa_list */
+};
+
+/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sc {
+ struct ieee802_1x_mka_sci sci; /* const SCI sci */
+ Boolean receiving; /* bool receiving (read only) */
+
+ struct os_time created_time; /* Time createdTime */
+
+ struct dl_list list;
+ struct dl_list sa_list;
+};
+
+/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sa {
+ Boolean enable_receive; /* bool enableReceive */
+ Boolean in_use; /* bool inUse (read only) */
+
+ u32 next_pn; /* PN nextPN (read only) */
+ u32 lowest_pn; /* PN lowestPN (read only) */
+ u8 an;
+ struct os_time created_time;
+
+ struct data_key *pkey;
+ struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
+
+ struct dl_list list;
+};
+
struct ieee802_1x_kay_ctx {
/* pointer to arbitrary upper level context */
void *ctx;
@@ -56,34 +140,30 @@ struct ieee802_1x_kay_ctx {
/* abstract wpa driver interface */
int (*macsec_init)(void *ctx, struct macsec_init_params *params);
int (*macsec_deinit)(void *ctx);
+ int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
int (*enable_protect_frames)(void *ctx, Boolean enabled);
+ int (*enable_encrypt)(void *ctx, Boolean enabled);
int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window);
int (*set_current_cipher_suite)(void *ctx, u64 cs);
int (*enable_controlled_port)(void *ctx, Boolean enabled);
- int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an,
- u32 *lowest_pn);
- int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an,
- u32 *next_pn);
- int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn);
- int (*get_available_receive_sc)(void *ctx, u32 *channel);
- int (*create_receive_sc)(void *ctx, u32 channel,
- struct ieee802_1x_mka_sci *sci,
+ int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
+ int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+ int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+ int (*create_receive_sc)(void *ctx, struct receive_sc *sc,
enum validate_frames vf,
enum confidentiality_offset co);
- int (*delete_receive_sc)(void *ctx, u32 channel);
- int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn,
- const u8 *sak);
- int (*enable_receive_sa)(void *ctx, u32 channel, u8 an);
- int (*disable_receive_sa)(void *ctx, u32 channel, u8 an);
- int (*get_available_transmit_sc)(void *ctx, u32 *channel);
- int (*create_transmit_sc)(void *ctx, u32 channel,
- const struct ieee802_1x_mka_sci *sci,
+ int (*delete_receive_sc)(void *ctx, struct receive_sc *sc);
+ int (*create_receive_sa)(void *ctx, struct receive_sa *sa);
+ int (*delete_receive_sa)(void *ctx, struct receive_sa *sa);
+ int (*enable_receive_sa)(void *ctx, struct receive_sa *sa);
+ int (*disable_receive_sa)(void *ctx, struct receive_sa *sa);
+ int (*create_transmit_sc)(void *ctx, struct transmit_sc *sc,
enum confidentiality_offset co);
- int (*delete_transmit_sc)(void *ctx, u32 channel);
- int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn,
- Boolean confidentiality, const u8 *sak);
- int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an);
- int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an);
+ int (*delete_transmit_sc)(void *ctx, struct transmit_sc *sc);
+ int (*create_transmit_sa)(void *ctx, struct transmit_sa *sa);
+ int (*delete_transmit_sa)(void *ctx, struct transmit_sa *sa);
+ int (*enable_transmit_sa)(void *ctx, struct transmit_sa *sa);
+ int (*disable_transmit_sa)(void *ctx, struct transmit_sa *sa);
};
struct ieee802_1x_kay {
@@ -102,6 +182,7 @@ struct ieee802_1x_kay {
enum macsec_cap macsec_capable;
Boolean macsec_desired;
Boolean macsec_protect;
+ Boolean macsec_encrypt;
Boolean macsec_replay_protect;
u32 macsec_replay_window;
enum validate_frames macsec_validate;
@@ -127,12 +208,12 @@ struct ieee802_1x_kay {
int mka_algindex; /* MKA alg table index */
u32 dist_kn;
+ u32 rcvd_keys;
u8 dist_an;
time_t dist_time;
u8 mka_version;
u8 algo_agility[4];
- u32 sc_ch;
u32 pn_exhaustion;
Boolean port_enable;
@@ -151,14 +232,17 @@ struct ieee802_1x_kay {
};
+u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci);
+
struct ieee802_1x_kay *
ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
- const char *ifname, const u8 *addr);
+ u16 port, u8 priority, const char *ifname, const u8 *addr);
void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
struct ieee802_1x_mka_participant *
ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
- struct mka_key_name *ckn, struct mka_key *cak,
+ const struct mka_key_name *ckn,
+ const struct mka_key *cak,
u32 life, enum mka_created_mode mode,
Boolean is_authenticator);
void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay,
@@ -185,5 +269,7 @@ int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki);
int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay);
+int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
+ size_t buflen);
#endif /* IEEE802_1X_KAY_H */
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
index 622282e97c51..bc522d89852b 100644
--- a/src/pae/ieee802_1x_kay_i.h
+++ b/src/pae/ieee802_1x_kay_i.h
@@ -54,88 +54,6 @@ struct ieee802_1x_kay_peer {
struct dl_list list;
};
-struct data_key {
- u8 *key;
- int key_len;
- struct ieee802_1x_mka_ki key_identifier;
- enum confidentiality_offset confidentiality_offset;
- u8 an;
- Boolean transmits;
- Boolean receives;
- struct os_time created_time;
- u32 next_pn;
-
- /* not defined data */
- Boolean rx_latest;
- Boolean tx_latest;
-
- int user; /* FIXME: to indicate if it can be delete safely */
-
- struct dl_list list;
-};
-
-/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct transmit_sc {
- struct ieee802_1x_mka_sci sci; /* const SCI sci */
- Boolean transmitting; /* bool transmitting (read only) */
-
- struct os_time created_time; /* Time createdTime */
-
- u8 encoding_sa; /* AN encodingSA (read only) */
- u8 enciphering_sa; /* AN encipheringSA (read only) */
-
- /* not defined data */
- unsigned int channel;
-
- struct dl_list list;
- struct dl_list sa_list;
-};
-
-/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct transmit_sa {
- Boolean in_use; /* bool inUse (read only) */
- u32 next_pn; /* PN nextPN (read only) */
- struct os_time created_time; /* Time createdTime */
-
- Boolean enable_transmit; /* bool EnableTransmit */
-
- u8 an;
- Boolean confidentiality;
- struct data_key *pkey;
-
- struct transmit_sc *sc;
- struct dl_list list; /* list entry in struct transmit_sc::sa_list */
-};
-
-/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct receive_sc {
- struct ieee802_1x_mka_sci sci; /* const SCI sci */
- Boolean receiving; /* bool receiving (read only) */
-
- struct os_time created_time; /* Time createdTime */
-
- unsigned int channel;
-
- struct dl_list list;
- struct dl_list sa_list;
-};
-
-/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct receive_sa {
- Boolean enable_receive; /* bool enableReceive */
- Boolean in_use; /* bool inUse (read only) */
-
- u32 next_pn; /* PN nextPN (read only) */
- u32 lowest_pn; /* PN lowestPN (read only) */
- u8 an;
- struct os_time created_time;
-
- struct data_key *pkey;
- struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
-
- struct dl_list list;
-};
-
struct macsec_ciphersuite {
u64 id;
char name[32];
@@ -175,6 +93,7 @@ struct ieee802_1x_mka_participant {
Boolean active;
Boolean participant;
Boolean retain;
+ enum mka_created_mode mode;
enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate;
@@ -250,6 +169,22 @@ struct ieee802_1x_mka_hdr {
#define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr)
+/**
+ * struct ieee802_1x_mka_basic_body - Basic Parameter Set (Figure 11-8)
+ * @version: MKA Version Identifier
+ * @priority: Key Server Priority
+ * @length: Parameter set body length
+ * @macsec_capability: MACsec capability, as defined in ieee802_1x_defs.h
+ * @macsec_desired: the participant wants MACsec to be used to protect frames
+ * (9.6.1)
+ * @key_server: the participant has not decided that another participant is or
+ * will be the key server (9.5.1)
+ * @length1: Parameter set body length (cont)
+ * @actor_mi: Actor's Member Identifier
+ * @actor_mn: Actor's Message Number
+ * @algo_agility: Algorithm Agility parameter
+ * @ckn: CAK Name
+ */
struct ieee802_1x_mka_basic_body {
/* octet 1 */
u8 version;
@@ -279,6 +214,14 @@ struct ieee802_1x_mka_basic_body {
u8 ckn[0];
};
+/**
+ * struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List
+ * parameter sets (Figure 11-9)
+ * @type: Parameter set type (1 or 2)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ * @peer: array of (MI, MN) pairs
+ */
struct ieee802_1x_mka_peer_body {
/* octet 1 */
u8 type;
@@ -299,6 +242,28 @@ struct ieee802_1x_mka_peer_body {
/* followed by Peers */
};
+/**
+ * struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure
+ * 11-10)
+ * @type: MKA message type
+ * @lan: latest key AN
+ * @ltx: latest key TX
+ * @lrx: latest key RX
+ * @oan: old key AN
+ * @otx: old key TX
+ * @orx: old key RX
+ * @ptx: plain TX, ie protectFrames is False
+ * @prx: plain RX, ie validateFrames is not Strict
+ * @delay_protect: True if LPNs are being reported sufficiently frequently to
+ * allow the recipient to provide data delay protection. If False, the LPN
+ * can be reported as zero.
+ * @lsrv_mi: latest key server MI
+ * @lkn: latest key number (together with MI, form the KI)
+ * @llpn: latest lowest acceptable PN (LPN)
+ * @osrv_mi: old key server MI
+ * @okn: old key number (together with MI, form the KI)
+ * @olpn: old lowest acceptable PN (LPN)
+ */
struct ieee802_1x_mka_sak_use_body {
/* octet 1 */
u8 type;
@@ -352,7 +317,21 @@ struct ieee802_1x_mka_sak_use_body {
be32 olpn;
};
-
+/**
+ * struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set
+ * (GCM-AES-128, Figure 11-11)
+ * @type: Parameter set type (4)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ * Total parameter body length values:
+ * - 0 for plain text
+ * - 28 for GCM-AES-128
+ * - 36 or more for other cipher suites
+ * @confid_offset: confidentiality offset, as defined in ieee802_1x_defs.h
+ * @dan: distributed AN (0 for plain text)
+ * @kn: Key Number
+ * @sak: AES Key Wrap of SAK (see 9.8)
+ */
struct ieee802_1x_mka_dist_sak_body {
/* octet 1 */
u8 type;
@@ -385,6 +364,41 @@ struct ieee802_1x_mka_dist_sak_body {
u8 sak[0];
};
+/**
+ * struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure
+ * 11-13)
+ * @type: Parameter set type (5)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ * Total parameter body length values:
+ * - 0 for plain text
+ * - 28 for GCM-AES-128
+ * - 36 or more for other cipher suites
+ * @cak: AES Key Wrap of CAK (see 9.8)
+ * @ckn: CAK Name
+ */
+struct ieee802_1x_mka_dist_cak_body {
+ /* octet 1 */
+ u8 type;
+ /* octet 2 */
+ u8 reserve;
+ /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u8 length:4;
+ u8 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u8 reserve1:4;
+ u8 length:4;
+#endif
+ /* octet 4 */
+ u8 length1;
+
+ /* octet 5 - 28 */
+ u8 cak[24];
+
+ /* followed by CAK Name, 29- */
+ u8 ckn[0];
+};
struct ieee802_1x_mka_icv_body {
/* octet 1 */
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
index 2d12911dbfcf..ab5339bb2046 100644
--- a/src/pae/ieee802_1x_secy_ops.c
+++ b/src/pae/ieee802_1x_secy_ops.c
@@ -45,6 +45,26 @@ int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean enabled)
}
+int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, Boolean enabled)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->enable_encrypt) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy enable_encrypt operation not supported");
+ return -1;
+ }
+
+ return ops->enable_encrypt(ops->ctx, enabled);
+}
+
+
int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win)
{
struct ieee802_1x_kay_ctx *ops;
@@ -113,55 +133,48 @@ int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean enabled)
}
-int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
- struct receive_sa *rxsa)
+int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap)
{
struct ieee802_1x_kay_ctx *ops;
- if (!kay || !rxsa) {
+ if (!kay) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
- if (!ops || !ops->get_receive_lowest_pn) {
+ if (!ops || !ops->macsec_get_capability) {
wpa_printf(MSG_ERROR,
- "KaY: secy get_receive_lowest_pn operation not supported");
+ "KaY: secy macsec_get_capability operation not supported");
return -1;
}
- return ops->get_receive_lowest_pn(ops->ctx,
- rxsa->sc->channel,
- rxsa->an,
- &rxsa->lowest_pn);
+ return ops->macsec_get_capability(ops->ctx, cap);
}
-int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
- struct transmit_sa *txsa)
+int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
+ struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
- if (!kay || !txsa) {
+ if (!kay || !rxsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
- if (!ops || !ops->get_transmit_next_pn) {
+ if (!ops || !ops->get_receive_lowest_pn) {
wpa_printf(MSG_ERROR,
"KaY: secy get_receive_lowest_pn operation not supported");
return -1;
}
- return ops->get_transmit_next_pn(ops->ctx,
- txsa->sc->channel,
- txsa->an,
- &txsa->next_pn);
+ return ops->get_receive_lowest_pn(ops->ctx, rxsa);
}
-int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
+int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa)
{
struct ieee802_1x_kay_ctx *ops;
@@ -172,36 +185,34 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
}
ops = kay->ctx;
- if (!ops || !ops->set_transmit_next_pn) {
+ if (!ops || !ops->get_transmit_next_pn) {
wpa_printf(MSG_ERROR,
"KaY: secy get_receive_lowest_pn operation not supported");
return -1;
}
- return ops->set_transmit_next_pn(ops->ctx,
- txsa->sc->channel,
- txsa->an,
- txsa->next_pn);
+ return ops->get_transmit_next_pn(ops->ctx, txsa);
}
-int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel)
+int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa)
{
struct ieee802_1x_kay_ctx *ops;
- if (!kay) {
+ if (!kay || !txsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
- if (!ops || !ops->get_available_receive_sc) {
+ if (!ops || !ops->set_transmit_next_pn) {
wpa_printf(MSG_ERROR,
- "KaY: secy get_available_receive_sc operation not supported");
+ "KaY: secy get_receive_lowest_pn operation not supported");
return -1;
}
- return ops->get_available_receive_sc(ops->ctx, channel);
+ return ops->set_transmit_next_pn(ops->ctx, txsa);
}
@@ -221,8 +232,7 @@ int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
return -1;
}
- return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci,
- kay->vf, kay->co);
+ return ops->create_receive_sc(ops->ctx, rxsc, kay->vf, kay->co);
}
@@ -242,7 +252,7 @@ int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
return -1;
}
- return ops->delete_receive_sc(ops->ctx, rxsc->channel);
+ return ops->delete_receive_sc(ops->ctx, rxsc);
}
@@ -262,12 +272,11 @@ int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
return -1;
}
- return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an,
- rxsa->lowest_pn, rxsa->pkey->key);
+ return ops->create_receive_sa(ops->ctx, rxsa);
}
-int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
@@ -277,19 +286,17 @@ int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
}
ops = kay->ctx;
- if (!ops || !ops->enable_receive_sa) {
+ if (!ops || !ops->delete_receive_sa) {
wpa_printf(MSG_ERROR,
- "KaY: secy enable_receive_sa operation not supported");
+ "KaY: secy delete_receive_sa operation not supported");
return -1;
}
- rxsa->enable_receive = TRUE;
-
- return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+ return ops->delete_receive_sa(ops->ctx, rxsa);
}
-int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
@@ -299,35 +306,37 @@ int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
}
ops = kay->ctx;
- if (!ops || !ops->disable_receive_sa) {
+ if (!ops || !ops->enable_receive_sa) {
wpa_printf(MSG_ERROR,
- "KaY: secy disable_receive_sa operation not supported");
+ "KaY: secy enable_receive_sa operation not supported");
return -1;
}
- rxsa->enable_receive = FALSE;
+ rxsa->enable_receive = TRUE;
- return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+ return ops->enable_receive_sa(ops->ctx, rxsa);
}
-int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel)
+int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
{
struct ieee802_1x_kay_ctx *ops;
- if (!kay) {
+ if (!kay || !rxsa) {
wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
return -1;
}
ops = kay->ctx;
- if (!ops || !ops->get_available_transmit_sc) {
+ if (!ops || !ops->disable_receive_sa) {
wpa_printf(MSG_ERROR,
- "KaY: secy get_available_transmit_sc operation not supported");
+ "KaY: secy disable_receive_sa operation not supported");
return -1;
}
- return ops->get_available_transmit_sc(ops->ctx, channel);
+ rxsa->enable_receive = FALSE;
+
+ return ops->disable_receive_sa(ops->ctx, rxsa);
}
@@ -348,8 +357,7 @@ int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
return -1;
}
- return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci,
- kay->co);
+ return ops->create_transmit_sc(ops->ctx, txsc, kay->co);
}
@@ -370,7 +378,7 @@ int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
return -1;
}
- return ops->delete_transmit_sc(ops->ctx, txsc->channel);
+ return ops->delete_transmit_sc(ops->ctx, txsc);
}
@@ -391,9 +399,28 @@ int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
return -1;
}
- return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an,
- txsa->next_pn, txsa->confidentiality,
- txsa->pkey->key);
+ return ops->create_transmit_sa(ops->ctx, txsa);
+}
+
+
+int secy_delete_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !txsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->delete_transmit_sa) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy delete_transmit_sa operation not supported");
+ return -1;
+ }
+
+ return ops->delete_transmit_sa(ops->ctx, txsa);
}
@@ -416,7 +443,7 @@ int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
txsa->enable_transmit = TRUE;
- return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+ return ops->enable_transmit_sa(ops->ctx, txsa);
}
@@ -439,7 +466,7 @@ int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
txsa->enable_transmit = FALSE;
- return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+ return ops->disable_transmit_sa(ops->ctx, txsa);
}
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
index f5057ee11958..9fb29c3ddfa0 100644
--- a/src/pae/ieee802_1x_secy_ops.h
+++ b/src/pae/ieee802_1x_secy_ops.h
@@ -13,10 +13,6 @@
#include "common/ieee802_1x_defs.h"
struct ieee802_1x_kay_conf;
-struct receive_sa;
-struct transmit_sa;
-struct receive_sc;
-struct transmit_sc;
int secy_init_macsec(struct ieee802_1x_kay *kay);
int secy_deinit_macsec(struct ieee802_1x_kay *kay);
@@ -25,6 +21,7 @@ int secy_deinit_macsec(struct ieee802_1x_kay *kay);
int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
enum validate_frames vf);
int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag);
+int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, Boolean enabled);
int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win);
int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs);
int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
@@ -32,27 +29,29 @@ int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag);
/****** KaY -> SecY *******/
+int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap);
int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
struct receive_sa *rxsa);
int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
-int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel);
int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
int secy_disable_receive_sa(struct ieee802_1x_kay *kay,
struct receive_sa *rxsa);
-int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel);
int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
struct transmit_sc *txsc);
int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
struct transmit_sc *txsc);
int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
+int secy_delete_transmit_sa(struct ieee802_1x_kay *kay,
+ struct transmit_sa *txsa);
int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 407e4f8b9614..07240ea2243d 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -210,7 +210,7 @@ static const struct radius_attr_type radius_attrs[] =
{ RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
- { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
+ { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
RADIUS_ATTR_INT32 },
@@ -250,6 +250,8 @@ static const struct radius_attr_type radius_attrs[] =
{ RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id",
RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_WLAN_REASON_CODE, "WLAN-Reason-Code",
+ RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher",
RADIUS_ATTR_HEXDUMP },
{ RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher",
@@ -631,6 +633,9 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
size_t buf_needed;
struct radius_attr_hdr *attr;
+ if (TEST_FAIL())
+ return NULL;
+
if (data_len > RADIUS_MAX_ATTR_LEN) {
wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
(unsigned long) data_len);
@@ -960,10 +965,9 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
}
len = vhdr->vendor_length - sizeof(*vhdr);
- data = os_malloc(len);
+ data = os_memdup(pos + sizeof(*vhdr), len);
if (data == NULL)
return NULL;
- os_memcpy(data, pos + sizeof(*vhdr), len);
if (alen)
*alen = len;
return data;
@@ -1040,12 +1044,11 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len,
return NULL;
}
- res = os_malloc(plain[0]);
+ res = os_memdup(plain + 1, plain[0]);
if (res == NULL) {
os_free(plain);
return NULL;
}
- os_memcpy(res, plain + 1, plain[0]);
if (reslen)
*reslen = plain[0];
os_free(plain);
@@ -1594,10 +1597,9 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
goto out;
/* alloc writable memory for decryption */
- buf = os_malloc(fdlen);
+ buf = os_memdup(fdata, fdlen);
if (buf == NULL)
goto out;
- os_memcpy(buf, fdata, fdlen);
buflen = fdlen;
/* init pointers */
@@ -1684,12 +1686,11 @@ int radius_copy_class(struct radius_class_data *dst,
dst->count = 0;
for (i = 0; i < src->count; i++) {
- dst->attr[i].data = os_malloc(src->attr[i].len);
+ dst->attr[i].data = os_memdup(src->attr[i].data,
+ src->attr[i].len);
if (dst->attr[i].data == NULL)
break;
dst->count++;
- os_memcpy(dst->attr[i].data, src->attr[i].data,
- src->attr[i].len);
dst->attr[i].len = src->attr[i].len;
}
diff --git a/src/radius/radius.h b/src/radius/radius.h
index cd510d2c88e2..630c0f9d0bc5 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -102,8 +102,13 @@ enum { RADIUS_ATTR_USER_NAME = 1,
RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130,
RADIUS_ATTR_LOCATION_CAPABLE = 131,
RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132,
+ RADIUS_ATTR_GSS_ACCEPTOR_SERVICE_NAME = 164,
+ RADIUS_ATTR_GSS_ACCEPTOR_HOST_NAME = 165,
+ RADIUS_ATTR_GSS_ACCEPTOR_SERVICE_SPECIFICS = 166,
+ RADIUS_ATTR_GSS_ACCEPTOR_REALM_NAME = 167,
RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177,
RADIUS_ATTR_WLAN_HESSID = 181,
+ RADIUS_ATTR_WLAN_REASON_CODE = 185,
RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186,
RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
RADIUS_ATTR_WLAN_AKM_SUITE = 188,
@@ -193,6 +198,11 @@ enum {
RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3,
RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4,
RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5,
+ RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM = 6,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME = 7,
+ RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP = 8,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING = 9,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL = 10,
};
#ifdef _MSC_VER
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 06c804d132fd..a87ee745eb28 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -904,13 +904,13 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
switch (res) {
case RADIUS_RX_PROCESSED:
radius_msg_free(msg);
- /* continue */
+ /* fall through */
case RADIUS_RX_QUEUED:
radius_client_msg_free(req);
return;
case RADIUS_RX_INVALID_AUTHENTICATOR:
invalid_authenticator++;
- /* continue */
+ /* fall through */
case RADIUS_RX_UNKNOWN:
/* continue with next handler */
break;
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index 8a3d7e0324bc..aaa3fc26723a 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -27,6 +27,7 @@ struct radius_das_data {
void *ctx;
enum radius_das_res (*disconnect)(void *ctx,
struct radius_das_attrs *attr);
+ enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr);
};
@@ -161,6 +162,10 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
abuf, from_port);
error = 508;
break;
+ case RADIUS_DAS_COA_FAILED:
+ /* not used with Disconnect-Request */
+ error = 405;
+ break;
case RADIUS_DAS_SUCCESS:
error = 0;
break;
@@ -184,6 +189,195 @@ fail:
}
+static struct radius_msg * radius_das_coa(struct radius_das_data *das,
+ struct radius_msg *msg,
+ const char *abuf, int from_port)
+{
+ struct radius_hdr *hdr;
+ struct radius_msg *reply;
+ u8 allowed[] = {
+ RADIUS_ATTR_USER_NAME,
+ RADIUS_ATTR_NAS_IP_ADDRESS,
+ RADIUS_ATTR_CALLING_STATION_ID,
+ RADIUS_ATTR_NAS_IDENTIFIER,
+ RADIUS_ATTR_ACCT_SESSION_ID,
+ RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+ RADIUS_ATTR_EVENT_TIMESTAMP,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+#ifdef CONFIG_HS20
+ RADIUS_ATTR_VENDOR_SPECIFIC,
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_IPV6
+ RADIUS_ATTR_NAS_IPV6_ADDRESS,
+#endif /* CONFIG_IPV6 */
+ 0
+ };
+ int error = 405;
+ u8 attr;
+ enum radius_das_res res;
+ struct radius_das_attrs attrs;
+ u8 *buf;
+ size_t len;
+ char tmp[100];
+ u8 sta_addr[ETH_ALEN];
+
+ hdr = radius_msg_get_hdr(msg);
+
+ if (!das->coa) {
+ wpa_printf(MSG_INFO, "DAS: CoA not supported");
+ goto fail;
+ }
+
+ attr = radius_msg_find_unlisted_attr(msg, allowed);
+ if (attr) {
+ wpa_printf(MSG_INFO,
+ "DAS: Unsupported attribute %u in CoA-Request from %s:%d",
+ attr, abuf, from_port);
+ error = 401;
+ goto fail;
+ }
+
+ os_memset(&attrs, 0, sizeof(attrs));
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ &buf, &len, NULL) == 0) {
+ if (len != 4) {
+ wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
+ abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.nas_ip_addr = buf;
+ }
+
+#ifdef CONFIG_IPV6
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+ &buf, &len, NULL) == 0) {
+ if (len != 16) {
+ wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
+ abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.nas_ipv6_addr = buf;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ &buf, &len, NULL) == 0) {
+ attrs.nas_identifier = buf;
+ attrs.nas_identifier_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ &buf, &len, NULL) == 0) {
+ if (len >= sizeof(tmp))
+ len = sizeof(tmp) - 1;
+ os_memcpy(tmp, buf, len);
+ tmp[len] = '\0';
+ if (hwaddr_aton2(tmp, sta_addr) < 0) {
+ wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
+ "'%s' from %s:%d", tmp, abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.sta_addr = sta_addr;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+ &buf, &len, NULL) == 0) {
+ attrs.user_name = buf;
+ attrs.user_name_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ &buf, &len, NULL) == 0) {
+ attrs.acct_session_id = buf;
+ attrs.acct_session_id_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+ &buf, &len, NULL) == 0) {
+ attrs.acct_multi_session_id = buf;
+ attrs.acct_multi_session_id_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ &buf, &len, NULL) == 0) {
+ attrs.cui = buf;
+ attrs.cui_len = len;
+ }
+
+#ifdef CONFIG_HS20
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ &buf, &len, NULL) == 0) {
+ if (len < 10 || WPA_GET_BE32(buf) != RADIUS_VENDOR_ID_WFA ||
+ buf[4] != RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING ||
+ buf[5] < 6) {
+ wpa_printf(MSG_INFO,
+ "DAS: Unsupported attribute %u in CoA-Request from %s:%d",
+ attr, abuf, from_port);
+ error = 401;
+ goto fail;
+ }
+ attrs.hs20_t_c_filtering = &buf[6];
+ }
+
+ if (!attrs.hs20_t_c_filtering) {
+ wpa_printf(MSG_INFO,
+ "DAS: No supported authorization change attribute in CoA-Request from %s:%d",
+ abuf, from_port);
+ error = 402;
+ goto fail;
+ }
+#endif /* CONFIG_HS20 */
+
+ res = das->coa(das->ctx, &attrs);
+ switch (res) {
+ case RADIUS_DAS_NAS_MISMATCH:
+ wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
+ abuf, from_port);
+ error = 403;
+ break;
+ case RADIUS_DAS_SESSION_NOT_FOUND:
+ wpa_printf(MSG_INFO,
+ "DAS: Session not found for request from %s:%d",
+ abuf, from_port);
+ error = 503;
+ break;
+ case RADIUS_DAS_MULTI_SESSION_MATCH:
+ wpa_printf(MSG_INFO,
+ "DAS: Multiple sessions match for request from %s:%d",
+ abuf, from_port);
+ error = 508;
+ break;
+ case RADIUS_DAS_COA_FAILED:
+ wpa_printf(MSG_INFO, "DAS: CoA failed for request from %s:%d",
+ abuf, from_port);
+ error = 407;
+ break;
+ case RADIUS_DAS_SUCCESS:
+ error = 0;
+ break;
+ }
+
+fail:
+ reply = radius_msg_new(error ? RADIUS_CODE_COA_NAK :
+ RADIUS_CODE_COA_ACK, hdr->identifier);
+ if (!reply)
+ return NULL;
+
+ if (error &&
+ !radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) {
+ radius_msg_free(reply);
+ return NULL;
+ }
+
+ return reply;
+}
+
+
static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct radius_das_data *das = eloop_ctx;
@@ -219,7 +413,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
len, abuf, from_port);
- if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+ if (das->client_addr.u.v4.s_addr &&
+ das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
return;
}
@@ -270,19 +465,7 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
reply = radius_das_disconnect(das, msg, abuf, from_port);
break;
case RADIUS_CODE_COA_REQUEST:
- /* TODO */
- reply = radius_msg_new(RADIUS_CODE_COA_NAK,
- hdr->identifier);
- if (reply == NULL)
- break;
-
- /* Unsupported Service */
- if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
- 405)) {
- radius_msg_free(reply);
- reply = NULL;
- break;
- }
+ reply = radius_das_coa(das, msg, abuf, from_port);
break;
default:
wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
@@ -369,17 +552,17 @@ radius_das_init(struct radius_das_conf *conf)
conf->require_message_authenticator;
das->ctx = conf->ctx;
das->disconnect = conf->disconnect;
+ das->coa = conf->coa;
os_memcpy(&das->client_addr, conf->client_addr,
sizeof(das->client_addr));
- das->shared_secret = os_malloc(conf->shared_secret_len);
+ das->shared_secret = os_memdup(conf->shared_secret,
+ conf->shared_secret_len);
if (das->shared_secret == NULL) {
radius_das_deinit(das);
return NULL;
}
- os_memcpy(das->shared_secret, conf->shared_secret,
- conf->shared_secret_len);
das->shared_secret_len = conf->shared_secret_len;
das->sock = radius_das_open_socket(conf->port);
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
index 9863fdc1eaca..233d662f631b 100644
--- a/src/radius/radius_das.h
+++ b/src/radius/radius_das.h
@@ -16,6 +16,7 @@ enum radius_das_res {
RADIUS_DAS_NAS_MISMATCH,
RADIUS_DAS_SESSION_NOT_FOUND,
RADIUS_DAS_MULTI_SESSION_MATCH,
+ RADIUS_DAS_COA_FAILED,
};
struct radius_das_attrs {
@@ -35,6 +36,9 @@ struct radius_das_attrs {
size_t acct_multi_session_id_len;
const u8 *cui;
size_t cui_len;
+
+ /* Authorization changes */
+ const u8 *hs20_t_c_filtering;
};
struct radius_das_conf {
@@ -48,6 +52,7 @@ struct radius_das_conf {
void *ctx;
enum radius_das_res (*disconnect)(void *ctx,
struct radius_das_attrs *attr);
+ enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr);
};
struct radius_das_data *
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index 744283c7dc9d..e3afc0d53298 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -26,9 +26,14 @@
#define RADIUS_SESSION_TIMEOUT 60
/**
+ * RADIUS_SESSION_MAINTAIN - Completed session expiration timeout in seconds
+ */
+#define RADIUS_SESSION_MAINTAIN 5
+
+/**
* RADIUS_MAX_SESSION - Maximum number of active sessions
*/
-#define RADIUS_MAX_SESSION 100
+#define RADIUS_MAX_SESSION 1000
/**
* RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages
@@ -75,6 +80,7 @@ struct radius_session {
struct eap_eapol_interface *eap_if;
char *username; /* from User-Name attribute */
char *nas_ip;
+ u8 mac_addr[ETH_ALEN]; /* from Calling-Station-Id attribute */
struct radius_msg *last_msg;
char *last_from_addr;
@@ -87,8 +93,11 @@ struct radius_session {
unsigned int remediation:1;
unsigned int macacl:1;
+ unsigned int t_c_filtering:1;
struct hostapd_radius_attr *accept_attr;
+
+ u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
};
/**
@@ -106,6 +115,14 @@ struct radius_client {
int shared_secret_len;
struct radius_session *sessions;
struct radius_server_counters counters;
+
+ u8 next_dac_identifier;
+ struct radius_msg *pending_dac_coa_req;
+ u8 pending_dac_coa_id;
+ u8 pending_dac_coa_addr[ETH_ALEN];
+ struct radius_msg *pending_dac_disconnect_req;
+ u8 pending_dac_disconnect_id;
+ u8 pending_dac_disconnect_addr[ETH_ALEN];
};
/**
@@ -267,6 +284,8 @@ struct radius_server_data {
unsigned int tls_session_lifetime;
+ unsigned int tls_flags;
+
/**
* wps - Wi-Fi Protected Setup context
*
@@ -339,6 +358,8 @@ struct radius_server_data {
char *subscr_remediation_url;
u8 subscr_remediation_method;
+ char *t_c_server_url;
+
#ifdef CONFIG_SQLITE
sqlite3 *db;
#endif /* CONFIG_SQLITE */
@@ -621,8 +642,8 @@ radius_server_get_new_session(struct radius_server_data *data,
struct radius_client *client,
struct radius_msg *msg, const char *from_addr)
{
- u8 *user;
- size_t user_len;
+ u8 *user, *id;
+ size_t user_len, id_len;
int res;
struct radius_session *sess;
struct eap_config eap_conf;
@@ -657,17 +678,32 @@ radius_server_get_new_session(struct radius_server_data *data,
sess->username = os_malloc(user_len * 4 + 1);
if (sess->username == NULL) {
- radius_server_session_free(data, sess);
+ radius_server_session_remove(data, sess);
return NULL;
}
printf_encode(sess->username, user_len * 4 + 1, user, user_len);
sess->nas_ip = os_strdup(from_addr);
if (sess->nas_ip == NULL) {
- radius_server_session_free(data, sess);
+ radius_server_session_remove(data, sess);
return NULL;
}
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &id,
+ &id_len, NULL) == 0) {
+ char buf[3 * ETH_ALEN];
+
+ os_memset(buf, 0, sizeof(buf));
+ if (id_len >= sizeof(buf))
+ id_len = sizeof(buf) - 1;
+ os_memcpy(buf, id, id_len);
+ if (hwaddr_aton2(buf, sess->mac_addr) < 0)
+ os_memset(sess->mac_addr, 0, ETH_ALEN);
+ else
+ RADIUS_DEBUG("Calling-Station-Id: " MACSTR,
+ MAC2STR(sess->mac_addr));
+ }
+
srv_log(sess, "New session created");
os_memset(&eap_conf, 0, sizeof(eap_conf));
@@ -691,13 +727,14 @@ radius_server_get_new_session(struct radius_server_data *data,
eap_conf.server_id_len = os_strlen(data->server_id);
eap_conf.erp = data->erp;
eap_conf.tls_session_lifetime = data->tls_session_lifetime;
+ eap_conf.tls_flags = data->tls_flags;
radius_server_testing_options(sess, &eap_conf);
sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
&eap_conf);
if (sess->eap == NULL) {
RADIUS_DEBUG("Failed to initialize EAP state machine for the "
"new session");
- radius_server_session_free(data, sess);
+ radius_server_session_remove(data, sess);
return NULL;
}
sess->eap_if = eap_get_interface(sess->eap);
@@ -710,6 +747,125 @@ radius_server_get_new_session(struct radius_server_data *data,
}
+#ifdef CONFIG_HS20
+static void radius_srv_hs20_t_c_pending(struct radius_session *sess)
+{
+#ifdef CONFIG_SQLITE
+ char *sql;
+ char addr[3 * ETH_ALEN], *id_str;
+ const u8 *id;
+ size_t id_len;
+
+ if (!sess->server->db || !sess->eap ||
+ is_zero_ether_addr(sess->mac_addr))
+ return;
+
+ os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sess->mac_addr));
+
+ id = eap_get_identity(sess->eap, &id_len);
+ if (!id)
+ return;
+ id_str = os_malloc(id_len + 1);
+ if (!id_str)
+ return;
+ os_memcpy(id_str, id, id_len);
+ id_str[id_len] = '\0';
+
+ sql = sqlite3_mprintf("INSERT OR REPLACE INTO pending_tc (mac_addr,identity) VALUES (%Q,%Q)",
+ addr, id_str);
+ os_free(id_str);
+ if (!sql)
+ return;
+
+ if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ RADIUS_ERROR("Failed to add pending_tc entry into sqlite database: %s",
+ sqlite3_errmsg(sess->server->db));
+ }
+ sqlite3_free(sql);
+#endif /* CONFIG_SQLITE */
+}
+#endif /* CONFIG_HS20 */
+
+
+static void radius_server_add_session(struct radius_session *sess)
+{
+#ifdef CONFIG_SQLITE
+ char *sql;
+ char addr_txt[ETH_ALEN * 3];
+ struct os_time now;
+
+ if (!sess->server->db)
+ return;
+
+
+ os_snprintf(addr_txt, sizeof(addr_txt), MACSTR,
+ MAC2STR(sess->mac_addr));
+
+ os_get_time(&now);
+ sql = sqlite3_mprintf("INSERT OR REPLACE INTO current_sessions(mac_addr,identity,start_time,nas,hs20_t_c_filtering) VALUES (%Q,%Q,%d,%Q,%u)",
+ addr_txt, sess->username, now.sec,
+ sess->nas_ip, sess->t_c_filtering);
+ if (sql) {
+ if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
+ NULL) != SQLITE_OK) {
+ RADIUS_ERROR("Failed to add current_sessions entry into sqlite database: %s",
+ sqlite3_errmsg(sess->server->db));
+ }
+ sqlite3_free(sql);
+ }
+#endif /* CONFIG_SQLITE */
+}
+
+
+static void db_update_last_msk(struct radius_session *sess, const char *msk)
+{
+#ifdef CONFIG_RADIUS_TEST
+#ifdef CONFIG_SQLITE
+ char *sql = NULL;
+ char *id_str = NULL;
+ const u8 *id;
+ size_t id_len;
+ const char *serial_num;
+
+ if (!sess->server->db)
+ return;
+
+ serial_num = eap_get_serial_num(sess->eap);
+ if (serial_num) {
+ id_len = 5 + os_strlen(serial_num) + 1;
+ id_str = os_malloc(id_len);
+ if (!id_str)
+ return;
+ os_snprintf(id_str, id_len, "cert-%s", serial_num);
+ } else {
+ id = eap_get_identity(sess->eap, &id_len);
+ if (!id)
+ return;
+ id_str = os_malloc(id_len + 1);
+ if (!id_str)
+ return;
+ os_memcpy(id_str, id, id_len);
+ id_str[id_len] = '\0';
+ }
+
+ sql = sqlite3_mprintf("UPDATE users SET last_msk=%Q WHERE identity=%Q",
+ msk, id_str);
+ os_free(id_str);
+ if (!sql)
+ return;
+
+ if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ RADIUS_DEBUG("Failed to update last_msk: %s",
+ sqlite3_errmsg(sess->server->db));
+ }
+ sqlite3_free(sql);
+#endif /* CONFIG_SQLITE */
+#endif /* CONFIG_RADIUS_TEST */
+}
+
+
static struct radius_msg *
radius_server_encapsulate_eap(struct radius_server_data *data,
struct radius_client *client,
@@ -720,6 +876,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
int code;
unsigned int sess_id;
struct radius_hdr *hdr = radius_msg_get_hdr(request);
+ u16 reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
if (sess->eap_if->eapFail) {
sess->eap_if->eapFail = FALSE;
@@ -754,9 +911,18 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
int len;
#ifdef CONFIG_RADIUS_TEST
+ char buf[2 * 64 + 1];
+
+ len = sess->eap_if->eapKeyDataLen;
+ if (len > 64)
+ len = 64;
+ len = wpa_snprintf_hex(buf, sizeof(buf),
+ sess->eap_if->eapKeyData, len);
+ buf[len] = '\0';
+
if (data->dump_msk_file) {
FILE *f;
- char buf[2 * 64 + 1];
+
f = fopen(data->dump_msk_file, "a");
if (f) {
len = sess->eap_if->eapKeyDataLen;
@@ -770,6 +936,8 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
fclose(f);
}
}
+
+ db_update_last_msk(sess, buf);
#endif /* CONFIG_RADIUS_TEST */
if (sess->eap_if->eapKeyDataLen > 64) {
len = 32;
@@ -812,6 +980,61 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
}
}
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
+ u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */
+ const char *url = data->t_c_server_url, *pos;
+ char *url2, *end2, *pos2;
+ size_t url_len;
+
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
+ buf, sizeof(buf))) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
+ radius_msg_free(msg);
+ return NULL;
+ }
+
+ if (!url) {
+ RADIUS_DEBUG("No t_c_server_url configured");
+ radius_msg_free(msg);
+ return NULL;
+ }
+
+ pos = os_strstr(url, "@1@");
+ if (!pos) {
+ RADIUS_DEBUG("No @1@ macro in t_c_server_url");
+ radius_msg_free(msg);
+ return NULL;
+ }
+
+ url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3;
+ url2 = os_malloc(url_len + 1);
+ if (!url2) {
+ RADIUS_DEBUG("Failed to allocate room for T&C Server URL");
+ os_free(url2);
+ radius_msg_free(msg);
+ return NULL;
+ }
+ pos2 = url2;
+ end2 = url2 + url_len + 1;
+ os_memcpy(pos2, url, pos - url);
+ pos2 += pos - url;
+ os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr));
+ pos2 += ETH_ALEN * 3 - 1;
+ os_memcpy(pos2, pos + 3, os_strlen(pos + 3));
+ if (!radius_msg_add_wfa(msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL,
+ (const u8 *) url2, url_len)) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL");
+ os_free(url2);
+ radius_msg_free(msg);
+ return NULL;
+ }
+ os_free(url2);
+
+ radius_srv_hs20_t_c_pending(sess);
+ }
#endif /* CONFIG_HS20 */
if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
@@ -833,12 +1056,24 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
}
}
+ if (code == RADIUS_CODE_ACCESS_REJECT) {
+ if (radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
+ reason) < 0) {
+ RADIUS_DEBUG("Failed to add WLAN-Reason-Code attribute");
+ radius_msg_free(msg);
+ return NULL;
+ }
+ }
+
if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
client->shared_secret_len,
hdr->authenticator) < 0) {
RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
}
+ if (code == RADIUS_CODE_ACCESS_ACCEPT)
+ radius_server_add_session(sess);
+
return msg;
}
@@ -985,6 +1220,51 @@ static int radius_server_reject(struct radius_server_data *data,
}
+static void radius_server_hs20_t_c_check(struct radius_session *sess,
+ struct radius_msg *msg)
+{
+#ifdef CONFIG_HS20
+ u8 *buf, *pos, *end, type, sublen, *timestamp = NULL;
+ size_t len;
+
+ buf = NULL;
+ for (;;) {
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ &buf, &len, buf) < 0)
+ break;
+ if (len < 6)
+ continue;
+ pos = buf;
+ end = buf + len;
+ if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+ continue;
+ pos += 4;
+
+ type = *pos++;
+ sublen = *pos++;
+ if (sublen < 2)
+ continue; /* invalid length */
+ sublen -= 2; /* skip header */
+ if (pos + sublen > end)
+ continue; /* invalid WFA VSA */
+
+ if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) {
+ timestamp = pos;
+ break;
+ }
+ }
+
+ if (!timestamp)
+ return;
+ RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp));
+ if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) {
+ RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering");
+ sess->t_c_filtering = 1;
+ }
+#endif /* CONFIG_HS20 */
+}
+
+
static int radius_server_request(struct radius_server_data *data,
struct radius_msg *msg,
struct sockaddr *from, socklen_t fromlen,
@@ -1057,7 +1337,7 @@ static int radius_server_request(struct radius_server_data *data,
"message");
return -1;
}
-
+
eap = radius_msg_get_eap(msg);
if (eap == NULL && sess->macacl) {
reply = radius_server_macacl(data, client, sess, msg);
@@ -1115,10 +1395,15 @@ static int radius_server_request(struct radius_server_data *data,
if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
is_complete = 1;
- if (sess->eap_if->eapFail)
+ if (sess->eap_if->eapFail) {
srv_log(sess, "EAP authentication failed");
- else if (sess->eap_if->eapSuccess)
+ db_update_last_msk(sess, "FAIL");
+ } else if (sess->eap_if->eapSuccess) {
srv_log(sess, "EAP authentication succeeded");
+ }
+
+ if (sess->eap_if->eapSuccess)
+ radius_server_hs20_t_c_check(sess, msg);
reply = radius_server_encapsulate_eap(data, client, sess, msg);
@@ -1172,7 +1457,7 @@ send_reply:
sess->sess_id);
eloop_cancel_timeout(radius_server_session_remove_timeout,
data, sess);
- eloop_register_timeout(10, 0,
+ eloop_register_timeout(RADIUS_SESSION_MAINTAIN, 0,
radius_server_session_remove_timeout,
data, sess);
}
@@ -1181,6 +1466,116 @@ send_reply:
}
+static void
+radius_server_receive_disconnect_resp(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_msg *msg, int ack)
+{
+ struct radius_hdr *hdr;
+
+ if (!client->pending_dac_disconnect_req) {
+ RADIUS_DEBUG("Ignore unexpected Disconnect response");
+ radius_msg_free(msg);
+ return;
+ }
+
+ hdr = radius_msg_get_hdr(msg);
+ if (hdr->identifier != client->pending_dac_disconnect_id) {
+ RADIUS_DEBUG("Ignore unexpected Disconnect response with unexpected identifier %u (expected %u)",
+ hdr->identifier,
+ client->pending_dac_disconnect_id);
+ radius_msg_free(msg);
+ return;
+ }
+
+ if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
+ client->shared_secret_len,
+ client->pending_dac_disconnect_req, 0)) {
+ RADIUS_DEBUG("Ignore Disconnect response with invalid authenticator");
+ radius_msg_free(msg);
+ return;
+ }
+
+ RADIUS_DEBUG("Disconnect-%s received for " MACSTR,
+ ack ? "ACK" : "NAK",
+ MAC2STR(client->pending_dac_disconnect_addr));
+
+ radius_msg_free(msg);
+ radius_msg_free(client->pending_dac_disconnect_req);
+ client->pending_dac_disconnect_req = NULL;
+}
+
+
+static void radius_server_receive_coa_resp(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_msg *msg, int ack)
+{
+ struct radius_hdr *hdr;
+#ifdef CONFIG_SQLITE
+ char addrtxt[3 * ETH_ALEN];
+ char *sql;
+ int res;
+#endif /* CONFIG_SQLITE */
+
+ if (!client->pending_dac_coa_req) {
+ RADIUS_DEBUG("Ignore unexpected CoA response");
+ radius_msg_free(msg);
+ return;
+ }
+
+ hdr = radius_msg_get_hdr(msg);
+ if (hdr->identifier != client->pending_dac_coa_id) {
+ RADIUS_DEBUG("Ignore unexpected CoA response with unexpected identifier %u (expected %u)",
+ hdr->identifier,
+ client->pending_dac_coa_id);
+ radius_msg_free(msg);
+ return;
+ }
+
+ if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
+ client->shared_secret_len,
+ client->pending_dac_coa_req, 0)) {
+ RADIUS_DEBUG("Ignore CoA response with invalid authenticator");
+ radius_msg_free(msg);
+ return;
+ }
+
+ RADIUS_DEBUG("CoA-%s received for " MACSTR,
+ ack ? "ACK" : "NAK",
+ MAC2STR(client->pending_dac_coa_addr));
+
+ radius_msg_free(msg);
+ radius_msg_free(client->pending_dac_coa_req);
+ client->pending_dac_coa_req = NULL;
+
+#ifdef CONFIG_SQLITE
+ if (!data->db)
+ return;
+
+ os_snprintf(addrtxt, sizeof(addrtxt), MACSTR,
+ MAC2STR(client->pending_dac_coa_addr));
+
+ if (ack) {
+ sql = sqlite3_mprintf("UPDATE current_sessions SET hs20_t_c_filtering=0, waiting_coa_ack=0, coa_ack_received=1 WHERE mac_addr=%Q",
+ addrtxt);
+ } else {
+ sql = sqlite3_mprintf("UPDATE current_sessions SET waiting_coa_ack=0 WHERE mac_addr=%Q",
+ addrtxt);
+ }
+ if (!sql)
+ return;
+
+ res = sqlite3_exec(data->db, sql, NULL, NULL, NULL);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ RADIUS_ERROR("Failed to update current_sessions entry: %s",
+ sqlite3_errmsg(data->db));
+ return;
+ }
+#endif /* CONFIG_SQLITE */
+}
+
+
static void radius_server_receive_auth(int sock, void *eloop_ctx,
void *sock_ctx)
{
@@ -1261,6 +1656,26 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx,
radius_msg_dump(msg);
}
+ if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_ACK) {
+ radius_server_receive_disconnect_resp(data, client, msg, 1);
+ return;
+ }
+
+ if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_NAK) {
+ radius_server_receive_disconnect_resp(data, client, msg, 0);
+ return;
+ }
+
+ if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_ACK) {
+ radius_server_receive_coa_resp(data, client, msg, 1);
+ return;
+ }
+
+ if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_NAK) {
+ radius_server_receive_coa_resp(data, client, msg, 0);
+ return;
+ }
+
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) {
RADIUS_DEBUG("Unexpected RADIUS code %d",
radius_msg_get_hdr(msg)->code);
@@ -1521,6 +1936,8 @@ static void radius_server_free_clients(struct radius_server_data *data,
radius_server_free_sessions(data, prev->sessions);
os_free(prev->shared_secret);
+ radius_msg_free(prev->pending_dac_coa_req);
+ radius_msg_free(prev->pending_dac_disconnect_req);
os_free(prev);
}
}
@@ -1749,6 +2166,7 @@ radius_server_init(struct radius_server_conf *conf)
data->erp = conf->erp;
data->erp_domain = conf->erp_domain;
data->tls_session_lifetime = conf->tls_session_lifetime;
+ data->tls_flags = conf->tls_flags;
if (conf->subscr_remediation_url) {
data->subscr_remediation_url =
@@ -1756,6 +2174,9 @@ radius_server_init(struct radius_server_conf *conf)
}
data->subscr_remediation_method = conf->subscr_remediation_method;
+ if (conf->t_c_server_url)
+ data->t_c_server_url = os_strdup(conf->t_c_server_url);
+
#ifdef CONFIG_SQLITE
if (conf->sqlite_file) {
if (sqlite3_open(conf->sqlite_file, &data->db)) {
@@ -1872,6 +2293,7 @@ void radius_server_deinit(struct radius_server_data *data)
os_free(data->dump_msk_file);
#endif /* CONFIG_RADIUS_TEST */
os_free(data->subscr_remediation_url);
+ os_free(data->t_c_server_url);
#ifdef CONFIG_SQLITE
if (data->db)
@@ -2040,6 +2462,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity,
sess->accept_attr = user->accept_attr;
sess->remediation = user->remediation;
sess->macacl = user->macacl;
+ sess->t_c_timestamp = user->t_c_timestamp;
}
if (ret) {
@@ -2166,3 +2589,212 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
radius_msg_free(msg);
}
+
+
+#ifdef CONFIG_SQLITE
+
+struct db_session_fields {
+ char *identity;
+ char *nas;
+ int hs20_t_c_filtering;
+ int waiting_coa_ack;
+ int coa_ack_received;
+};
+
+
+static int get_db_session_fields(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct db_session_fields *fields = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (!argv[i])
+ continue;
+
+ RADIUS_DEBUG("Session DB: %s=%s", col[i], argv[i]);
+
+ if (os_strcmp(col[i], "identity") == 0) {
+ os_free(fields->identity);
+ fields->identity = os_strdup(argv[i]);
+ } else if (os_strcmp(col[i], "nas") == 0) {
+ os_free(fields->nas);
+ fields->nas = os_strdup(argv[i]);
+ } else if (os_strcmp(col[i], "hs20_t_c_filtering") == 0) {
+ fields->hs20_t_c_filtering = atoi(argv[i]);
+ } else if (os_strcmp(col[i], "waiting_coa_ack") == 0) {
+ fields->waiting_coa_ack = atoi(argv[i]);
+ } else if (os_strcmp(col[i], "coa_ack_received") == 0) {
+ fields->coa_ack_received = atoi(argv[i]);
+ }
+ }
+
+ return 0;
+}
+
+
+static void free_db_session_fields(struct db_session_fields *fields)
+{
+ os_free(fields->identity);
+ fields->identity = NULL;
+ os_free(fields->nas);
+ fields->nas = NULL;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
+int radius_server_dac_request(struct radius_server_data *data, const char *req)
+{
+#ifdef CONFIG_SQLITE
+ char *sql;
+ int res;
+ int disconnect;
+ const char *pos = req;
+ u8 addr[ETH_ALEN];
+ char addrtxt[3 * ETH_ALEN];
+ int t_c_clear = 0;
+ struct db_session_fields fields;
+ struct sockaddr_in das;
+ struct radius_client *client;
+ struct radius_msg *msg;
+ struct wpabuf *buf;
+ u8 identifier;
+ struct os_time now;
+
+ if (!data)
+ return -1;
+
+ /* req: <disconnect|coa> <MAC Address> [t_c_clear] */
+
+ if (os_strncmp(pos, "disconnect ", 11) == 0) {
+ disconnect = 1;
+ pos += 11;
+ } else if (os_strncmp(req, "coa ", 4) == 0) {
+ disconnect = 0;
+ pos += 4;
+ } else {
+ return -1;
+ }
+
+ if (hwaddr_aton(pos, addr))
+ return -1;
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ if (os_strstr(pos, "t_c_clear"))
+ t_c_clear = 1;
+ }
+
+ if (!disconnect && !t_c_clear) {
+ RADIUS_ERROR("DAC request for CoA without any authorization change");
+ return -1;
+ }
+
+ if (!data->db) {
+ RADIUS_ERROR("SQLite database not in use");
+ return -1;
+ }
+
+ os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(addr));
+
+ sql = sqlite3_mprintf("SELECT * FROM current_sessions WHERE mac_addr=%Q",
+ addrtxt);
+ if (!sql)
+ return -1;
+
+ os_memset(&fields, 0, sizeof(fields));
+ res = sqlite3_exec(data->db, sql, get_db_session_fields, &fields, NULL);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ RADIUS_ERROR("Failed to find matching current_sessions entry from sqlite database: %s",
+ sqlite3_errmsg(data->db));
+ free_db_session_fields(&fields);
+ return -1;
+ }
+
+ if (!fields.nas) {
+ RADIUS_ERROR("No NAS information found from current_sessions");
+ free_db_session_fields(&fields);
+ return -1;
+ }
+
+ os_memset(&das, 0, sizeof(das));
+ das.sin_family = AF_INET;
+ das.sin_addr.s_addr = inet_addr(fields.nas);
+ das.sin_port = htons(3799);
+
+ free_db_session_fields(&fields);
+
+ client = radius_server_get_client(data, &das.sin_addr, 0);
+ if (!client) {
+ RADIUS_ERROR("No NAS information available to protect the packet");
+ return -1;
+ }
+
+ identifier = client->next_dac_identifier++;
+
+ msg = radius_msg_new(disconnect ? RADIUS_CODE_DISCONNECT_REQUEST :
+ RADIUS_CODE_COA_REQUEST, identifier);
+ if (!msg)
+ return -1;
+
+ os_snprintf(addrtxt, sizeof(addrtxt), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) addrtxt, os_strlen(addrtxt))) {
+ RADIUS_ERROR("Could not add Calling-Station-Id");
+ radius_msg_free(msg);
+ return -1;
+ }
+
+ if (!disconnect && t_c_clear) {
+ u8 val[4] = { 0x00, 0x00, 0x00, 0x00 }; /* E=0 */
+
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
+ val, sizeof(val))) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
+ radius_msg_free(msg);
+ return -1;
+ }
+ }
+
+ os_get_time(&now);
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+ now.sec)) {
+ RADIUS_ERROR("Failed to add Event-Timestamp attribute");
+ radius_msg_free(msg);
+ return -1;
+ }
+
+ radius_msg_finish_acct(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len);
+
+ if (wpa_debug_level <= MSG_MSGDUMP)
+ radius_msg_dump(msg);
+
+ buf = radius_msg_get_buf(msg);
+ if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
+ (struct sockaddr *) &das, sizeof(das)) < 0) {
+ RADIUS_ERROR("Failed to send packet - sendto: %s",
+ strerror(errno));
+ radius_msg_free(msg);
+ return -1;
+ }
+
+ if (disconnect) {
+ radius_msg_free(client->pending_dac_disconnect_req);
+ client->pending_dac_disconnect_req = msg;
+ client->pending_dac_disconnect_id = identifier;
+ os_memcpy(client->pending_dac_disconnect_addr, addr, ETH_ALEN);
+ } else {
+ radius_msg_free(client->pending_dac_coa_req);
+ client->pending_dac_coa_req = msg;
+ client->pending_dac_coa_id = identifier;
+ os_memcpy(client->pending_dac_coa_addr, addr, ETH_ALEN);
+ }
+
+ return 0;
+#else /* CONFIG_SQLITE */
+ return -1;
+#endif /* CONFIG_SQLITE */
+}
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 7a25802c8152..167bbf5b2881 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -172,6 +172,8 @@ struct radius_server_conf {
unsigned int tls_session_lifetime;
+ unsigned int tls_flags;
+
/**
* wps - Wi-Fi Protected Setup context
*
@@ -231,6 +233,8 @@ struct radius_server_conf {
char *subscr_remediation_url;
u8 subscr_remediation_method;
+
+ char *t_c_server_url;
};
@@ -244,5 +248,6 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
size_t buflen);
void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx);
+int radius_server_dac_request(struct radius_server_data *data, const char *req);
#endif /* RADIUS_SERVER_H */
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index d5e61fe72dc8..c2d81f279195 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -10,7 +10,6 @@ include ../lib.rules
CFLAGS += -DCONFIG_IEEE80211W
CFLAGS += -DCONFIG_IEEE80211R
-CFLAGS += -DCONFIG_PEERKEY
CFLAGS += -DCONFIG_TDLS
CFLAGS += -DCONFIG_WNM
CFLAGS += -DIEEE8021X_EAPOL
@@ -18,7 +17,6 @@ CFLAGS += -DIEEE8021X_EAPOL
LIB_OBJS= \
pmksa_cache.o \
wpa_ft.o \
- peerkey.o \
tdls.o \
preauth.o \
wpa.o \
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
deleted file mode 100644
index 79764d94b902..000000000000
--- a/src/rsn_supp/peerkey.c
+++ /dev/null
@@ -1,1155 +0,0 @@
-/*
- * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2015, 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"
-
-#ifdef CONFIG_PEERKEY
-
-#include "common.h"
-#include "eloop.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "crypto/random.h"
-#include "common/ieee802_11_defs.h"
-#include "wpa.h"
-#include "wpa_i.h"
-#include "wpa_ie.h"
-#include "peerkey.h"
-
-
-static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
-{
- os_memcpy(pos, ie, ie_len);
- return pos + ie_len;
-}
-
-
-static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
-{
- *pos++ = WLAN_EID_VENDOR_SPECIFIC;
- *pos++ = RSN_SELECTOR_LEN + data_len;
- RSN_SELECTOR_PUT(pos, kde);
- pos += RSN_SELECTOR_LEN;
- os_memcpy(pos, data, data_len);
- pos += data_len;
- return pos;
-}
-
-
-static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-#if 0
- struct wpa_sm *sm = eloop_ctx;
- struct wpa_peerkey *peerkey = timeout_ctx;
-#endif
- /* TODO: time out SMK and any STK that was generated using this SMK */
-}
-
-
-static void wpa_supplicant_peerkey_free(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey)
-{
- eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
- os_free(peerkey);
-}
-
-
-static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
- const u8 *peer,
- u16 mui, u16 error_type, int ver)
-{
- size_t rlen;
- struct wpa_eapol_key *err;
- struct wpa_eapol_key_192 *err192;
- struct rsn_error_kde error;
- u8 *rbuf, *pos;
- size_t kde_len;
- u16 key_info;
-
- kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
- if (peer)
- kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
-
- rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
- NULL, sizeof(*err) + kde_len, &rlen,
- (void *) &err);
- if (rbuf == NULL)
- return -1;
- err192 = (struct wpa_eapol_key_192 *) err;
-
- err->type = EAPOL_KEY_TYPE_RSN;
- key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR |
- WPA_KEY_INFO_REQUEST;
- WPA_PUT_BE16(err->key_info, key_info);
- WPA_PUT_BE16(err->key_length, 0);
- os_memcpy(err->replay_counter, sm->request_counter,
- WPA_REPLAY_COUNTER_LEN);
- inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
-
- WPA_PUT_BE16(err->key_data_length, (u16) kde_len);
- pos = (u8 *) (err + 1);
-
- if (peer) {
- /* Peer MAC Address KDE */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
- }
-
- /* Error KDE */
- error.mui = host_to_be16(mui);
- error.error_type = host_to_be16(error_type);
- wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error));
-
- if (peer) {
- wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer "
- MACSTR " mui %d error_type %d)",
- MAC2STR(peer), mui, error_type);
- } else {
- wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error "
- "(mui %d error_type %d)", mui, error_type);
- }
-
- wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst,
- ETH_P_EAPOL, rbuf, rlen, err192->key_mic);
-
- return 0;
-}
-
-
-static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
- const unsigned char *src_addr,
- const struct wpa_eapol_key *key,
- int ver, struct wpa_peerkey *peerkey)
-{
- size_t rlen;
- struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
- u8 *rbuf, *pos;
- size_t kde_len;
- u16 key_info;
-
- /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
- kde_len = peerkey->rsnie_p_len +
- 2 + RSN_SELECTOR_LEN + ETH_ALEN +
- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
-
- rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
- NULL, sizeof(*reply) + kde_len, &rlen,
- (void *) &reply);
- if (rbuf == NULL)
- return -1;
- reply192 = (struct wpa_eapol_key_192 *) reply;
-
- reply->type = EAPOL_KEY_TYPE_RSN;
- key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_SECURE;
- WPA_PUT_BE16(reply->key_info, key_info);
- WPA_PUT_BE16(reply->key_length, 0);
- os_memcpy(reply->replay_counter, key->replay_counter,
- WPA_REPLAY_COUNTER_LEN);
-
- os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
-
- WPA_PUT_BE16(reply->key_data_length, (u16) kde_len);
- pos = (u8 *) (reply + 1);
-
- /* Peer RSN IE */
- pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
-
- /* Initiator MAC Address KDE */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN);
-
- /* Initiator Nonce */
- wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
-
- wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
- wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr,
- ETH_P_EAPOL, rbuf, rlen, reply192->key_mic);
-
- return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m2(
- struct wpa_sm *sm, const unsigned char *src_addr,
- const struct wpa_eapol_key *key, size_t extra_len, int ver)
-{
- struct wpa_peerkey *peerkey;
- struct wpa_eapol_ie_parse kde;
- struct wpa_ie_data ie;
- int cipher;
- struct rsn_ie_hdr *hdr;
- u8 *pos;
-
- wpa_printf(MSG_DEBUG, "RSN: Received SMK M2");
-
- if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
- wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for "
- "the current network");
- return -1;
- }
-
- if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
- 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2");
- return -1;
- }
-
- if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
- kde.mac_addr_len < ETH_ALEN) {
- wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
- "SMK M2");
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR,
- MAC2STR(kde.mac_addr));
-
- if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) {
- wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK "
- "M2");
- return -1;
- }
-
- if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2");
- return -1;
- }
-
- cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
- sm->allowed_pairwise_cipher, 0);
- if (cipher < 0) {
- wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
- wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
- STK_MUI_SMK, STK_ERR_CPHR_NS,
- ver);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
- wpa_cipher_txt(cipher));
-
- /* TODO: find existing entry and if found, use that instead of adding
- * a new one; how to handle the case where both ends initiate at the
- * same time? */
- peerkey = os_zalloc(sizeof(*peerkey));
- if (peerkey == NULL)
- return -1;
- os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN);
- os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
- os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
- peerkey->rsnie_i_len = kde.rsn_ie_len;
- peerkey->cipher = cipher;
- peerkey->akmp = ie.key_mgmt;
-
- if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Failed to get random data for PNonce");
- wpa_supplicant_peerkey_free(sm, peerkey);
- return -1;
- }
-
- hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p;
- hdr->elem_id = WLAN_EID_RSN;
- WPA_PUT_LE16(hdr->version, RSN_VERSION);
- pos = (u8 *) (hdr + 1);
- /* Group Suite can be anything for SMK RSN IE; receiver will just
- * ignore it. */
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- pos += RSN_SELECTOR_LEN;
- /* Include only the selected cipher in pairwise cipher suite */
- WPA_PUT_LE16(pos, 1);
- pos += 2;
- RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher));
- pos += RSN_SELECTOR_LEN;
-
- hdr->len = (pos - peerkey->rsnie_p) - 2;
- peerkey->rsnie_p_len = pos - peerkey->rsnie_p;
- wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
- peerkey->rsnie_p, peerkey->rsnie_p_len);
-
- wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey);
-
- peerkey->next = sm->peerkey;
- sm->peerkey = peerkey;
-
- return 0;
-}
-
-
-/**
- * rsn_smkid - Derive SMK identifier
- * @smk: Station master key (32 bytes)
- * @pnonce: Peer Nonce
- * @mac_p: Peer MAC address
- * @inonce: Initiator Nonce
- * @mac_i: Initiator MAC address
- * @akmp: Negotiated AKM
- *
- * 8.5.1.4 Station to station (STK) key hierarchy
- * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
- */
-static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
- const u8 *inonce, const u8 *mac_i, u8 *smkid,
- int akmp)
-{
- char *title = "SMK Name";
- const u8 *addr[5];
- const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
- ETH_ALEN };
- unsigned char hash[SHA256_MAC_LEN];
-
- addr[0] = (u8 *) title;
- addr[1] = pnonce;
- addr[2] = mac_p;
- addr[3] = inonce;
- addr[4] = mac_i;
-
-#ifdef CONFIG_IEEE80211W
- if (wpa_key_mgmt_sha256(akmp))
- hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
- else
-#endif /* CONFIG_IEEE80211W */
- hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
- os_memcpy(smkid, hash, PMKID_LEN);
-}
-
-
-static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey)
-{
- size_t mlen;
- struct wpa_eapol_key *msg;
- u8 *mbuf;
- size_t kde_len;
- u16 key_info, ver;
-
- kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
-
- mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
- sizeof(*msg) + kde_len, &mlen,
- (void *) &msg);
- if (mbuf == NULL)
- return;
-
- msg->type = EAPOL_KEY_TYPE_RSN;
-
- if (peerkey->cipher != WPA_CIPHER_TKIP)
- ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
- else
- ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
- key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
- WPA_PUT_BE16(msg->key_info, key_info);
-
- if (peerkey->cipher != WPA_CIPHER_TKIP)
- WPA_PUT_BE16(msg->key_length, 16);
- else
- WPA_PUT_BE16(msg->key_length, 32);
-
- os_memcpy(msg->replay_counter, peerkey->replay_counter,
- WPA_REPLAY_COUNTER_LEN);
- inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
-
- WPA_PUT_BE16(msg->key_data_length, kde_len);
- wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
- peerkey->smkid, PMKID_LEN);
-
- if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "RSN: Failed to get random data for INonce (STK)");
- os_free(mbuf);
- return;
- }
- wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake",
- peerkey->inonce, WPA_NONCE_LEN);
- os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
-
- wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
- MAC2STR(peerkey->addr));
- wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL,
- mbuf, mlen, NULL);
-}
-
-
-static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey)
-{
- size_t mlen;
- struct wpa_eapol_key *msg;
- u8 *mbuf, *pos;
- size_t kde_len;
- u16 key_info, ver;
- be32 lifetime;
-
- kde_len = peerkey->rsnie_i_len +
- 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
-
- mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
- sizeof(*msg) + kde_len, &mlen,
- (void *) &msg);
- if (mbuf == NULL)
- return;
-
- msg->type = EAPOL_KEY_TYPE_RSN;
-
- if (peerkey->cipher != WPA_CIPHER_TKIP)
- ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
- else
- ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
- key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK |
- WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
- WPA_PUT_BE16(msg->key_info, key_info);
-
- if (peerkey->cipher != WPA_CIPHER_TKIP)
- WPA_PUT_BE16(msg->key_length, 16);
- else
- WPA_PUT_BE16(msg->key_length, 32);
-
- os_memcpy(msg->replay_counter, peerkey->replay_counter,
- WPA_REPLAY_COUNTER_LEN);
- inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
-
- WPA_PUT_BE16(msg->key_data_length, kde_len);
- pos = (u8 *) (msg + 1);
- pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
- lifetime = host_to_be32(peerkey->lifetime);
- wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
- (u8 *) &lifetime, sizeof(lifetime));
-
- os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
-
- wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
- MAC2STR(peerkey->addr));
- wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver,
- peerkey->addr, ETH_P_EAPOL, mbuf, mlen,
- msg->key_mic);
-}
-
-
-static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey,
- struct wpa_eapol_ie_parse *kde)
-{
- wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")",
- MAC2STR(kde->mac_addr));
-
- if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0)
- {
- wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not "
- "match with the one used in SMK M3");
- return -1;
- }
-
- if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
- wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not "
- "match with the one received in SMK M2");
- return -1;
- }
-
- return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
- const unsigned char *src_addr,
- const struct wpa_eapol_key *key,
- int ver,
- struct wpa_peerkey *peerkey,
- struct wpa_eapol_ie_parse *kde)
-{
- int cipher;
- struct wpa_ie_data ie;
-
- wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")",
- MAC2STR(kde->mac_addr));
- if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN ||
- wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) {
- wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5");
- /* TODO: abort negotiation */
- return -1;
- }
-
- if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
- wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does "
- "not match with INonce used in SMK M1");
- return -1;
- }
-
- if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0)
- {
- wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not "
- "match with the one used in SMK M1");
- return -1;
- }
-
- os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len);
- peerkey->rsnie_p_len = kde->rsn_ie_len;
- os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
-
- cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
- sm->allowed_pairwise_cipher, 0);
- if (cipher < 0) {
- wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
- "unacceptable cipher", MAC2STR(kde->mac_addr));
- wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
- STK_MUI_SMK, STK_ERR_CPHR_NS,
- ver);
- /* TODO: abort negotiation */
- return -1;
- }
- wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
- wpa_cipher_txt(cipher));
- peerkey->cipher = cipher;
-
- return 0;
-}
-
-
-static int wpa_supplicant_process_smk_m45(
- struct wpa_sm *sm, const unsigned char *src_addr,
- const struct wpa_eapol_key *key, size_t extra_len, int ver)
-{
- struct wpa_peerkey *peerkey;
- struct wpa_eapol_ie_parse kde;
- u32 lifetime;
-
- if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
- wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
- "the current network");
- return -1;
- }
-
- if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
- 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
- return -1;
- }
-
- if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
- kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN ||
- kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN ||
- kde.lifetime == NULL || kde.lifetime_len < 4) {
- wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or "
- "Lifetime KDE in SMK M4/M5");
- return -1;
- }
-
- for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
- if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 &&
- os_memcmp(peerkey->initiator ? peerkey->inonce :
- peerkey->pnonce,
- key->key_nonce, WPA_NONCE_LEN) == 0)
- break;
- }
- if (peerkey == NULL) {
- wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found "
- "for SMK M4/M5: peer " MACSTR,
- MAC2STR(kde.mac_addr));
- return -1;
- }
-
- if (peerkey->initiator) {
- if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver,
- peerkey, &kde) < 0)
- return -1;
- } else {
- if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0)
- return -1;
- }
-
- os_memcpy(peerkey->smk, kde.smk, PMK_LEN);
- peerkey->smk_complete = 1;
- wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN);
- lifetime = WPA_GET_BE32(kde.lifetime);
- wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
- if (lifetime > 1000000000)
- lifetime = 1000000000; /* avoid overflowing eloop time */
- peerkey->lifetime = lifetime;
- eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
- sm, peerkey);
-
- if (peerkey->initiator) {
- rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
- peerkey->inonce, sm->own_addr, peerkey->smkid,
- peerkey->akmp);
- wpa_supplicant_send_stk_1_of_4(sm, peerkey);
- } else {
- rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
- peerkey->inonce, peerkey->addr, peerkey->smkid,
- peerkey->akmp);
- }
- wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
-
- return 0;
-}
-
-
-static int wpa_supplicant_process_smk_error(
- struct wpa_sm *sm, const unsigned char *src_addr,
- const struct wpa_eapol_key *key, size_t extra_len)
-{
- struct wpa_eapol_ie_parse kde;
- struct rsn_error_kde error;
- u8 peer[ETH_ALEN];
- u16 error_type;
-
- wpa_printf(MSG_DEBUG, "RSN: Received SMK Error");
-
- if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
- wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
- "the current network");
- return -1;
- }
-
- if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
- 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
- return -1;
- }
-
- if (kde.error == NULL || kde.error_len < sizeof(error)) {
- wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error");
- return -1;
- }
-
- if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN)
- os_memcpy(peer, kde.mac_addr, ETH_ALEN);
- else
- os_memset(peer, 0, ETH_ALEN);
- os_memcpy(&error, kde.error, sizeof(error));
- error_type = be_to_host16(error.error_type);
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "RSN: SMK Error KDE received: MUI %d error_type %d peer "
- MACSTR,
- be_to_host16(error.mui), error_type,
- MAC2STR(peer));
-
- if (kde.mac_addr &&
- (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN ||
- error_type == STK_ERR_CPHR_NS)) {
- struct wpa_peerkey *peerkey;
-
- for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
- if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) ==
- 0)
- break;
- }
- if (peerkey == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake "
- "found for SMK Error");
- return -1;
- }
- /* TODO: abort SMK/STK handshake and remove all related keys */
- }
-
- return 0;
-}
-
-
-static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey,
- const struct wpa_eapol_key *key,
- u16 ver, const u8 *key_data,
- size_t key_data_len)
-{
- struct wpa_eapol_ie_parse ie;
- size_t kde_buf_len;
- struct wpa_ptk *stk;
- u8 buf[8], *kde_buf, *pos;
- be32 lifetime;
-
- wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
- os_memset(&ie, 0, sizeof(ie));
-
- /* RSN: msg 1/4 should contain SMKID for the selected SMK */
- wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
- if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 ||
- ie.pmkid == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
- return;
- }
- if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
- wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
- ie.pmkid, PMKID_LEN);
- return;
- }
-
- if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "RSN: Failed to get random data for PNonce");
- return;
- }
- wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce",
- peerkey->pnonce, WPA_NONCE_LEN);
-
- /* Calculate STK which will be stored as a temporary STK until it has
- * been verified when processing message 3/4. */
- stk = &peerkey->tstk;
- wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
- sm->own_addr, peerkey->addr,
- peerkey->pnonce, key->key_nonce,
- stk, peerkey->akmp, peerkey->cipher);
- /* Supplicant: swap tx/rx Mic keys */
- os_memcpy(buf, &stk->tk[16], 8);
- os_memcpy(&stk->tk[16], &stk->tk[24], 8);
- os_memcpy(&stk->tk[24], buf, 8);
- peerkey->tstk_set = 1;
-
- kde_buf_len = peerkey->rsnie_p_len +
- 2 + RSN_SELECTOR_LEN + sizeof(lifetime) +
- 2 + RSN_SELECTOR_LEN + PMKID_LEN;
- kde_buf = os_malloc(kde_buf_len);
- if (kde_buf == NULL)
- return;
- pos = kde_buf;
- pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
- lifetime = host_to_be32(peerkey->lifetime);
- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
- (u8 *) &lifetime, sizeof(lifetime));
- wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN);
-
- if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver,
- peerkey->pnonce, kde_buf, kde_buf_len,
- stk)) {
- os_free(kde_buf);
- return;
- }
- os_free(kde_buf);
-
- os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
-}
-
-
-static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey,
- struct wpa_eapol_ie_parse *kde)
-{
- u32 lifetime;
-
- if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
- return;
-
- lifetime = WPA_GET_BE32(kde->lifetime);
-
- if (lifetime >= peerkey->lifetime) {
- wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds "
- "which is larger than or equal to own value %u "
- "seconds - ignored", lifetime, peerkey->lifetime);
- return;
- }
-
- wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds "
- "(own was %u seconds) - updated",
- lifetime, peerkey->lifetime);
- peerkey->lifetime = lifetime;
-
- eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
- eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
- sm, peerkey);
-}
-
-
-static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey,
- const struct wpa_eapol_key *key,
- u16 ver, const u8 *key_data,
- size_t key_data_len)
-{
- struct wpa_eapol_ie_parse kde;
-
- wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
- os_memset(&kde, 0, sizeof(kde));
-
- /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
- * from the peer. It may also include Lifetime KDE. */
- wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len);
- if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 ||
- kde.pmkid == NULL || kde.rsn_ie == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
- return;
- }
-
- if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
- wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
- kde.pmkid, PMKID_LEN);
- return;
- }
-
- if (kde.rsn_ie_len != peerkey->rsnie_p_len ||
- os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) {
- wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK "
- "handshakes did not match");
- wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake",
- peerkey->rsnie_p, peerkey->rsnie_p_len);
- wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake",
- kde.rsn_ie, kde.rsn_ie_len);
- return;
- }
-
- wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
-
- wpa_supplicant_send_stk_3_of_4(sm, peerkey);
- os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN);
-}
-
-
-static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey,
- const struct wpa_eapol_key *key,
- u16 ver, const u8 *key_data,
- size_t key_data_len)
-{
- struct wpa_eapol_ie_parse kde;
- size_t key_len;
- const u8 *_key;
- u8 key_buf[32], rsc[6];
-
- wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
- os_memset(&kde, 0, sizeof(kde));
-
- /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
- * Lifetime KDE. */
- wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len);
- if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
- wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
- "STK 3/4");
- return;
- }
-
- if (kde.rsn_ie_len != peerkey->rsnie_i_len ||
- os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) {
- wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK "
- "handshakes did not match");
- wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK "
- "handshake",
- peerkey->rsnie_i, peerkey->rsnie_i_len);
- wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK "
- "handshake",
- kde.rsn_ie, kde.rsn_ie_len);
- return;
- }
-
- if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
- wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK "
- "4-Way Handshake differs from 3 of STK 4-Way "
- "Handshake - drop packet (src=" MACSTR ")",
- MAC2STR(peerkey->addr));
- return;
- }
-
- wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
-
- if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
- WPA_GET_BE16(key->key_info),
- &peerkey->stk))
- return;
-
- _key = peerkey->stk.tk;
- if (peerkey->cipher == WPA_CIPHER_TKIP) {
- /* Swap Tx/Rx keys for Michael MIC */
- os_memcpy(key_buf, _key, 16);
- os_memcpy(key_buf + 16, _key + 24, 8);
- os_memcpy(key_buf + 24, _key + 16, 8);
- _key = key_buf;
- key_len = 32;
- } else
- key_len = 16;
-
- os_memset(rsc, 0, 6);
- if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
- rsc, sizeof(rsc), _key, key_len) < 0) {
- os_memset(key_buf, 0, sizeof(key_buf));
- wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
- "driver.");
- return;
- }
- os_memset(key_buf, 0, sizeof(key_buf));
-}
-
-
-static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey,
- const struct wpa_eapol_key *key,
- u16 ver)
-{
- u8 rsc[6];
-
- wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
-
- os_memset(rsc, 0, 6);
- if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
- rsc, sizeof(rsc), peerkey->stk.tk,
- peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
- wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
- "driver.");
- return;
- }
-}
-
-
-/**
- * peerkey_verify_eapol_key_mic - Verify PeerKey MIC
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @peerkey: Pointer to the PeerKey data for the peer
- * @key: Pointer to the EAPOL-Key frame header
- * @ver: Version bits from EAPOL-Key Key Info
- * @buf: Pointer to the beginning of EAPOL-Key frame
- * @len: Length of the EAPOL-Key frame
- * Returns: 0 on success, -1 on failure
- */
-int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey,
- struct wpa_eapol_key_192 *key, u16 ver,
- const u8 *buf, size_t len)
-{
- u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
- size_t mic_len = 16;
- int ok = 0;
-
- if (peerkey->initiator && !peerkey->stk_set) {
- wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
- sm->own_addr, peerkey->addr,
- peerkey->inonce, key->key_nonce,
- &peerkey->stk, peerkey->akmp, peerkey->cipher);
- peerkey->stk_set = 1;
- }
-
- os_memcpy(mic, key->key_mic, mic_len);
- if (peerkey->tstk_set) {
- os_memset(key->key_mic, 0, mic_len);
- wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
- sm->key_mgmt, ver, buf, len, key->key_mic);
- if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
- wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
- "when using TSTK - ignoring TSTK");
- } else {
- ok = 1;
- peerkey->tstk_set = 0;
- peerkey->stk_set = 1;
- os_memcpy(&peerkey->stk, &peerkey->tstk,
- sizeof(peerkey->stk));
- os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk));
- }
- }
-
- if (!ok && peerkey->stk_set) {
- os_memset(key->key_mic, 0, mic_len);
- wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
- sm->key_mgmt, ver, buf, len, key->key_mic);
- if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
- wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
- "- dropping packet");
- return -1;
- }
- ok = 1;
- }
-
- if (!ok) {
- wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC "
- "- dropping packet");
- return -1;
- }
-
- os_memcpy(peerkey->replay_counter, key->replay_counter,
- WPA_REPLAY_COUNTER_LEN);
- peerkey->replay_counter_set = 1;
- return 0;
-}
-
-
-/**
- * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1)
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @peer: MAC address of the peer STA
- * Returns: 0 on success, or -1 on failure
- *
- * Send an EAPOL-Key Request to the current authenticator to start STK
- * handshake with the peer.
- */
-int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
-{
- size_t rlen, kde_len;
- struct wpa_eapol_key *req;
- int key_info, ver;
- u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos;
- u16 count;
- struct rsn_ie_hdr *hdr;
- struct wpa_peerkey *peerkey;
- struct wpa_ie_data ie;
-
- if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled)
- return -1;
-
- if (sm->ap_rsn_ie &&
- wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 &&
- !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) {
- wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK");
- return -1;
- }
-
- if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
- ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
- else
- ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
-
- if (wpa_sm_get_bssid(sm, bssid) < 0) {
- wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
- "SMK M1");
- return -1;
- }
-
- /* TODO: find existing entry and if found, use that instead of adding
- * a new one */
- peerkey = os_zalloc(sizeof(*peerkey));
- if (peerkey == NULL)
- return -1;
- peerkey->initiator = 1;
- os_memcpy(peerkey->addr, peer, ETH_ALEN);
- peerkey->akmp = sm->key_mgmt;
-
- /* SMK M1:
- * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
- * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE))
- */
-
- hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i;
- hdr->elem_id = WLAN_EID_RSN;
- WPA_PUT_LE16(hdr->version, RSN_VERSION);
- pos = (u8 *) (hdr + 1);
- /* Group Suite can be anything for SMK RSN IE; receiver will just
- * ignore it. */
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- pos += RSN_SELECTOR_LEN;
- count_pos = pos;
- pos += 2;
-
- count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher);
- pos += count * RSN_SELECTOR_LEN;
- WPA_PUT_LE16(count_pos, count);
-
- hdr->len = (pos - peerkey->rsnie_i) - 2;
- peerkey->rsnie_i_len = pos - peerkey->rsnie_i;
- wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
- peerkey->rsnie_i, peerkey->rsnie_i_len);
-
- kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
-
- rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
- sizeof(*req) + kde_len, &rlen,
- (void *) &req);
- if (rbuf == NULL) {
- wpa_supplicant_peerkey_free(sm, peerkey);
- return -1;
- }
-
- req->type = EAPOL_KEY_TYPE_RSN;
- key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver;
- WPA_PUT_BE16(req->key_info, key_info);
- WPA_PUT_BE16(req->key_length, 0);
- os_memcpy(req->replay_counter, sm->request_counter,
- WPA_REPLAY_COUNTER_LEN);
- inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
-
- if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Failed to get random data for INonce");
- os_free(rbuf);
- wpa_supplicant_peerkey_free(sm, peerkey);
- return -1;
- }
- os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
- req->key_nonce, WPA_NONCE_LEN);
-
- WPA_PUT_BE16(req->key_data_length, (u16) kde_len);
- pos = (u8 *) (req + 1);
-
- /* Initiator RSN IE */
- pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
- /* Peer MAC address KDE */
- wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
-
- wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
- MACSTR ")", MAC2STR(peer));
- wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
- ETH_P_EAPOL, rbuf, rlen, req->key_mic);
-
- peerkey->next = sm->peerkey;
- sm->peerkey = peerkey;
-
- return 0;
-}
-
-
-/**
- * peerkey_deinit - Free PeerKey values
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- */
-void peerkey_deinit(struct wpa_sm *sm)
-{
- struct wpa_peerkey *prev, *peerkey = sm->peerkey;
- while (peerkey) {
- prev = peerkey;
- peerkey = peerkey->next;
- wpa_supplicant_peerkey_free(sm, prev);
- }
- sm->peerkey = NULL;
-}
-
-
-void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
- struct wpa_eapol_key *key, u16 key_info, u16 ver,
- const u8 *key_data, size_t key_data_len)
-{
- if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
- (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
- /* 3/4 STK 4-Way Handshake */
- wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver,
- key_data, key_data_len);
- } else if (key_info & WPA_KEY_INFO_ACK) {
- /* 1/4 STK 4-Way Handshake */
- wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver,
- key_data, key_data_len);
- } else if (key_info & WPA_KEY_INFO_SECURE) {
- /* 4/4 STK 4-Way Handshake */
- wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
- } else {
- /* 2/4 STK 4-Way Handshake */
- wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver,
- key_data, key_data_len);
- }
-}
-
-
-void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
- struct wpa_eapol_key *key, size_t extra_len,
- u16 key_info, u16 ver)
-{
- if (key_info & WPA_KEY_INFO_ERROR) {
- /* SMK Error */
- wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len);
- } else if (key_info & WPA_KEY_INFO_ACK) {
- /* SMK M2 */
- wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len,
- ver);
- } else {
- /* SMK M4 or M5 */
- wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len,
- ver);
- }
-}
-
-#endif /* CONFIG_PEERKEY */
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
deleted file mode 100644
index 6ccd948baace..000000000000
--- a/src/rsn_supp/peerkey.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef PEERKEY_H
-#define PEERKEY_H
-
-#define PEERKEY_MAX_IE_LEN 80
-struct wpa_peerkey {
- struct wpa_peerkey *next;
- int initiator; /* whether this end was initator for SMK handshake */
- u8 addr[ETH_ALEN]; /* other end MAC address */
- u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
- u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */
- u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */
- size_t rsnie_i_len;
- u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */
- size_t rsnie_p_len;
- u8 smk[PMK_LEN];
- int smk_complete;
- u8 smkid[PMKID_LEN];
- u32 lifetime;
- int cipher; /* Selected cipher (WPA_CIPHER_*) */
- u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
- int replay_counter_set;
- int akmp;
-
- struct wpa_ptk stk, tstk;
- int stk_set, tstk_set;
-};
-
-
-#ifdef CONFIG_PEERKEY
-
-int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey,
- struct wpa_eapol_key_192 *key, u16 ver,
- const u8 *buf, size_t len);
-void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
- struct wpa_eapol_key *key, u16 key_info, u16 ver,
- const u8 *key_data, size_t key_data_len);
-void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
- struct wpa_eapol_key *key, size_t extra_len,
- u16 key_info, u16 ver);
-void peerkey_deinit(struct wpa_sm *sm);
-
-#else /* CONFIG_PEERKEY */
-
-static inline int
-peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
- struct wpa_peerkey *peerkey,
- struct wpa_eapol_key *key, u16 ver,
- const u8 *buf, size_t len)
-{
- return -1;
-}
-
-static inline void
-peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
- struct wpa_eapol_key *key, u16 key_info, u16 ver,
- const u8 *key_data, size_t key_data_len)
-{
-}
-
-static inline void
-peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
- struct wpa_eapol_key *key, size_t extra_len,
- u16 key_info, u16 ver)
-{
-}
-
-static inline void peerkey_deinit(struct wpa_sm *sm)
-{
-}
-
-#endif /* CONFIG_PEERKEY */
-
-#endif /* PEERKEY_H */
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 3d8d12223c26..fdd522087dbc 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -43,7 +43,10 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry,
enum pmksa_free_reason reason)
{
- wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+ wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
+ entry->pmkid,
+ entry->fils_cache_id_set ? entry->fils_cache_id :
+ NULL);
pmksa->pmksa_count--;
pmksa->free_cb(entry, pmksa->ctx, reason);
_pmksa_cache_free_entry(entry);
@@ -93,7 +96,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
- pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL);
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0);
if (entry) {
sec = pmksa->pmksa->reauth_time - now.sec;
if (sec < 0)
@@ -116,6 +119,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
* @spa: Supplicant address
* @network_ctx: Network configuration context for this PMK
* @akmp: WPA_KEY_MGMT_* used in key derivation
+ * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised
* Returns: Pointer to the added PMKSA cache entry or %NULL on error
*
* This function create a PMKSA entry for a new PMK and adds it to the PMKSA
@@ -126,9 +130,10 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *kck, size_t kck_len,
- const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+ const u8 *cache_id)
{
- struct rsn_pmksa_cache_entry *entry, *pos, *prev;
+ struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
if (pmk_len > PMK_LEN_MAX)
@@ -149,24 +154,38 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
else if (wpa_key_mgmt_suite_b(akmp))
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
else
- rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
- wpa_key_mgmt_sha256(akmp));
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
os_get_reltime(&now);
entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
entry->akmp = akmp;
+ if (cache_id) {
+ entry->fils_cache_id_set = 1;
+ os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN);
+ }
os_memcpy(entry->aa, aa, ETH_ALEN);
entry->network_ctx = network_ctx;
+ return pmksa_cache_add_entry(pmksa, entry);
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *pos, *prev;
+
/* Replace an old entry for the same Authenticator (if found) with the
* new entry */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
- if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
- if (pos->pmk_len == pmk_len &&
- os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 &&
+ if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) {
+ if (pos->pmk_len == entry->pmk_len &&
+ os_memcmp_const(pos->pmk, entry->pmk,
+ entry->pmk_len) == 0 &&
os_memcmp_const(pos->pmkid, entry->pmkid,
PMKID_LEN) == 0) {
wpa_printf(MSG_DEBUG, "WPA: reusing previous "
@@ -192,8 +211,8 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
"the current AP and any PMKSA cache entry "
"that was based on the old PMK");
if (!pos->opportunistic)
- pmksa_cache_flush(pmksa, network_ctx, pos->pmk,
- pos->pmk_len);
+ pmksa_cache_flush(pmksa, entry->network_ctx,
+ pos->pmk, pos->pmk_len);
pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
break;
}
@@ -244,8 +263,10 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
}
pmksa->pmksa_count++;
wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
- " network_ctx=%p", MAC2STR(entry->aa), network_ctx);
- wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+ " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx);
+ wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
+ entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
+ entry->pmk, entry->pmk_len);
return entry;
}
@@ -320,17 +341,20 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
* @aa: Authenticator address or %NULL to match any
* @pmkid: PMKID or %NULL to match any
* @network_ctx: Network context or %NULL to match any
+ * @akmp: Specific AKMP to search for or 0 for any
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
*/
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *pmkid,
- const void *network_ctx)
+ const void *network_ctx,
+ int akmp)
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
while (entry) {
if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
(pmkid == NULL ||
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
+ (!akmp || akmp == entry->akmp) &&
(network_ctx == NULL || network_ctx == entry->network_ctx))
return entry;
entry = entry->next;
@@ -345,16 +369,19 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
const u8 *aa)
{
struct rsn_pmksa_cache_entry *new_entry;
+ os_time_t old_expiration = old_entry->expiration;
new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
NULL, NULL, 0,
aa, pmksa->sm->own_addr,
- old_entry->network_ctx, old_entry->akmp);
+ old_entry->network_ctx, old_entry->akmp,
+ old_entry->fils_cache_id_set ?
+ old_entry->fils_cache_id : NULL);
if (new_entry == NULL)
return NULL;
/* TODO: reorder entries based on expiration time? */
- new_entry->expiration = old_entry->expiration;
+ new_entry->expiration = old_expiration;
new_entry->opportunistic = 1;
return new_entry;
@@ -366,6 +393,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @network_ctx: Network configuration context
* @aa: Authenticator address for the new AP
+ * @akmp: Specific AKMP to search for or 0 for any
* Returns: Pointer to a new PMKSA cache entry or %NULL if not available
*
* Try to create a new PMKSA cache entry opportunistically by guessing that the
@@ -374,7 +402,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
- const u8 *aa)
+ const u8 *aa, int akmp)
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
@@ -382,7 +410,8 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
if (network_ctx == NULL)
return NULL;
while (entry) {
- if (entry->network_ctx == network_ctx) {
+ if (entry->network_ctx == network_ctx &&
+ (!akmp || entry->akmp == akmp)) {
entry = pmksa_cache_clone_entry(pmksa, entry, aa);
if (entry) {
wpa_printf(MSG_DEBUG, "RSN: added "
@@ -397,6 +426,24 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
}
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa,
+ const void *network_ctx, const u8 *cache_id)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ if (network_ctx == entry->network_ctx &&
+ entry->fils_cache_id_set &&
+ os_memcmp(cache_id, entry->fils_cache_id,
+ FILS_CACHE_ID_LEN) == 0)
+ return entry;
+ }
+
+ return NULL;
+}
+
+
/**
* pmksa_cache_get_current - Get the current used PMKSA entry
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -429,33 +476,44 @@ void pmksa_cache_clear_current(struct wpa_sm *sm)
* @bssid: BSSID for PMKSA or %NULL if not used
* @network_ctx: Network configuration context
* @try_opportunistic: Whether to allow opportunistic PMKSA caching
+ * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used
* Returns: 0 if PMKSA was found or -1 if no matching entry was found
*/
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid, void *network_ctx,
- int try_opportunistic)
+ int try_opportunistic, const u8 *fils_cache_id,
+ int akmp)
{
struct rsn_pmksa_cache *pmksa = sm->pmksa;
wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
- "try_opportunistic=%d", network_ctx, try_opportunistic);
+ "try_opportunistic=%d akmp=0x%x",
+ network_ctx, try_opportunistic, akmp);
if (pmkid)
wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
pmkid, PMKID_LEN);
if (bssid)
wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
MAC2STR(bssid));
+ if (fils_cache_id)
+ wpa_printf(MSG_DEBUG,
+ "RSN: Search for FILS Cache Identifier %02x%02x",
+ fils_cache_id[0], fils_cache_id[1]);
sm->cur_pmksa = NULL;
if (pmkid)
sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
- network_ctx);
+ network_ctx, akmp);
if (sm->cur_pmksa == NULL && bssid)
sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
- network_ctx);
+ network_ctx, akmp);
if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
network_ctx,
- bssid);
+ bssid, akmp);
+ if (sm->cur_pmksa == NULL && fils_cache_id)
+ sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa,
+ network_ctx,
+ fils_cache_id);
if (sm->cur_pmksa) {
wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
sm->cur_pmksa->pmkid, PMKID_LEN);
@@ -482,11 +540,20 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
char *pos = buf;
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
+ int cache_id_used = 0;
+
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ if (entry->fils_cache_id_set) {
+ cache_id_used = 1;
+ break;
+ }
+ }
os_get_reltime(&now);
ret = os_snprintf(pos, buf + len - pos,
"Index / AA / PMKID / expiration (in seconds) / "
- "opportunistic\n");
+ "opportunistic%s\n",
+ cache_id_used ? " / FILS Cache Identifier" : "");
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
@@ -501,18 +568,36 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
pos += ret;
pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
PMKID_LEN);
- ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+ ret = os_snprintf(pos, buf + len - pos, " %d %d",
(int) (entry->expiration - now.sec),
entry->opportunistic);
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
+ if (entry->fils_cache_id_set) {
+ ret = os_snprintf(pos, buf + len - pos, " %02x%02x",
+ entry->fils_cache_id[0],
+ entry->fils_cache_id[1]);
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ ret = os_snprintf(pos, buf + len - pos, "\n");
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
entry = entry->next;
}
return pos - buf;
}
+struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
+{
+ return pmksa->pmksa;
+}
+
+
/**
* pmksa_cache_init - Initialize PMKSA cache
* @free_cb: Callback function to be called when a PMKSA cache entry is freed
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index daede6dac7fe..6c49fa9248fb 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -21,6 +21,14 @@ struct rsn_pmksa_cache_entry {
int akmp; /* WPA_KEY_MGMT_* */
u8 aa[ETH_ALEN];
+ /*
+ * If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA
+ * cache entry is applicable to all BSSs (any BSSID/aa[]) that
+ * advertise the same FILS Cache Identifier within the same ESS.
+ */
+ u8 fils_cache_id[2];
+ unsigned int fils_cache_id_set:1;
+
os_time_t reauth_time;
/**
@@ -53,20 +61,27 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *pmkid,
- const void *network_ctx);
+ const void *network_ctx,
+ int akmp);
int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *kck, size_t kck_len,
- const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+ const u8 *cache_id);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
void pmksa_cache_clear_current(struct wpa_sm *sm);
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid, void *network_ctx,
- int try_opportunistic);
+ int try_opportunistic, const u8 *fils_cache_id,
+ int akmp);
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
- void *network_ctx, const u8 *aa);
+ void *network_ctx, const u8 *aa, int akmp);
void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *pmk, size_t pmk_len);
@@ -86,7 +101,7 @@ static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
static inline struct rsn_pmksa_cache_entry *
pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid,
- const void *network_ctx)
+ const void *network_ctx, int akmp)
{
return NULL;
}
@@ -104,9 +119,23 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
}
static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
+{
+ return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *kck, size_t kck_len,
- const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+ const u8 *cache_id)
{
return NULL;
}
@@ -118,7 +147,9 @@ static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid,
void *network_ctx,
- int try_opportunistic)
+ int try_opportunistic,
+ const u8 *fils_cache_id,
+ int akmp)
{
return -1;
}
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index 4c9a4fb8b14c..d0c43f464e27 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -97,7 +97,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol,
NULL, 0,
sm->preauth_bssid, sm->own_addr,
sm->network_ctx,
- WPA_KEY_MGMT_IEEE8021X);
+ WPA_KEY_MGMT_IEEE8021X, NULL);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: failed to get master session key from "
@@ -323,7 +323,7 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm)
dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
struct rsn_pmksa_candidate, list) {
struct rsn_pmksa_cache_entry *p = NULL;
- p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL);
+ p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL, 0);
if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
(p == NULL || p->opportunistic)) {
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
@@ -342,7 +342,8 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm)
/* Some drivers (e.g., NDIS) expect to get notified about the
* PMKIDs again, so report the existing data now. */
if (p) {
- wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid);
+ wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid,
+ NULL, p->pmk, p->pmk_len);
}
dl_list_del(&candidate->list);
@@ -371,7 +372,7 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
if (sm->network_ctx && sm->proactive_key_caching)
pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx,
- bssid);
+ bssid, 0);
if (!preauth) {
wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
@@ -482,7 +483,7 @@ void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
return;
- pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL);
+ pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL, 0);
if (pmksa && (!pmksa->opportunistic ||
!(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
return;
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 9eb973860049..345b0c84d871 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -35,6 +35,7 @@
#define TDLS_TESTING_DECLINE_RESP BIT(9)
#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
#define TDLS_TESTING_WRONG_MIC BIT(11)
+#define TDLS_TESTING_DOUBLE_TPK_M2 BIT(12)
unsigned int tdls_testing = 0;
#endif /* CONFIG_TDLS_TESTING */
@@ -303,10 +304,9 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
peer->sm_tmr.peer_capab = peer_capab;
peer->sm_tmr.buf_len = msg_len;
os_free(peer->sm_tmr.buf);
- peer->sm_tmr.buf = os_malloc(msg_len);
+ peer->sm_tmr.buf = os_memdup(msg, msg_len);
if (peer->sm_tmr.buf == NULL)
return -1;
- os_memcpy(peer->sm_tmr.buf, msg, msg_len);
wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
"(action_code=%u)", action_code);
@@ -413,8 +413,9 @@ static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
size_t len[2];
u8 data[3 * ETH_ALEN];
- /* IEEE Std 802.11z-2010 8.5.9.1:
- * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+ /* IEEE Std 802.11-2016 12.7.9.2:
+ * TPK-Key-Input = Hash(min(SNonce, ANonce) || max(SNonce, ANonce))
+ * Hash = SHA-256 for TDLS
*/
len[0] = WPA_NONCE_LEN;
len[1] = WPA_NONCE_LEN;
@@ -432,11 +433,8 @@ static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
key_input, SHA256_MAC_LEN);
/*
- * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
- * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
- * TODO: is N_KEY really included in KDF Context and if so, in which
- * presentation format (little endian 16-bit?) is it used? It gets
- * added by the KDF anyway..
+ * TPK = KDF-Hash-Length(TPK-Key-Input, "TDLS PMK",
+ * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID)
*/
if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
@@ -1220,9 +1218,10 @@ skip_ies:
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
+ struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
+
wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
"Link Identifier");
- struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
wpa_tdls_linkid(sm, peer, l);
l->bssid[5] ^= 0x01;
pos += sizeof(*l);
@@ -2126,6 +2125,13 @@ skip_add_peer:
goto error;
}
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_DOUBLE_TPK_M2) {
+ wpa_printf(MSG_INFO, "TDLS: Testing - Send another TPK M2");
+ wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer);
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
return 0;
error:
@@ -2153,11 +2159,11 @@ static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
sm, peer);
#ifdef CONFIG_TDLS_TESTING
- if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
- wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK "
- "expiration");
- eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
- }
+ if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Testing - disable TPK expiration");
+ eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+ }
#endif /* CONFIG_TDLS_TESTING */
}
@@ -2912,14 +2918,14 @@ void wpa_tdls_disassoc(struct wpa_sm *sm)
static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
{
/* bit 38 - TDLS Prohibited */
- return !!(elems->ext_capab[2 + 4] & 0x40);
+ return !!(elems->ext_capab[4] & 0x40);
}
static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
{
/* bit 39 - TDLS Channel Switch Prohibited */
- return !!(elems->ext_capab[2 + 4] & 0x80);
+ return !!(elems->ext_capab[4] & 0x80);
}
@@ -2932,7 +2938,7 @@ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
if (ies == NULL ||
ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
- elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+ elems.ext_capab == NULL || elems.ext_capab_len < 5)
return;
sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
@@ -2951,7 +2957,7 @@ void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
if (ies == NULL ||
ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
- elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+ elems.ext_capab == NULL || elems.ext_capab_len < 5)
return;
if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index dcd75272151f..e0c913074c63 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - WPA state machine and EAPOL-Key processing
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
* Copyright(c) 2015 Intel Deutschland GmbH
*
* This software may be distributed under the terms of the BSD license.
@@ -10,10 +10,17 @@
#include "includes.h"
#include "common.h"
+#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
+#include "crypto/aes_siv.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eap_common/eap_defs.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "wpa.h"
#include "eloop.h"
@@ -21,7 +28,6 @@
#include "pmksa_cache.h"
#include "wpa_i.h"
#include "wpa_ie.h"
-#include "peerkey.h"
static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -30,8 +36,7 @@ static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
/**
* wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
* @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @kck: Key Confirmation Key (KCK, part of PTK)
- * @kck_len: KCK length in octets
+ * @ptk: PTK for Key Confirmation/Encryption Key
* @ver: Version field from Key Info
* @dest: Destination address for the frame
* @proto: Ethertype (usually ETH_P_EAPOL)
@@ -40,13 +45,16 @@ static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
* @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
* Returns: >= 0 on success, < 0 on failure
*/
-int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic)
{
int ret = -1;
- size_t mic_len = wpa_mic_len(sm->key_mgmt);
+ size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
+ wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR
+ " ver=%d mic_len=%d key_mgmt=0x%x",
+ MAC2STR(dest), ver, (int) mic_len, sm->key_mgmt);
if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
/*
* Association event was not yet received; try to fetch
@@ -64,16 +72,89 @@ int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
MAC2STR(dest));
}
}
- if (key_mic &&
- wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len,
- key_mic)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
- "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
- ver, sm->key_mgmt);
+
+ if (mic_len) {
+ if (key_mic && (!ptk || !ptk->kck_len))
+ goto out;
+
+ if (key_mic &&
+ wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver,
+ msg, msg_len, key_mic)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
+ ver, sm->key_mgmt);
+ goto out;
+ }
+ if (ptk)
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KCK",
+ ptk->kck, ptk->kck_len);
+ wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC",
+ key_mic, mic_len);
+ } else {
+#ifdef CONFIG_FILS
+ /* AEAD cipher - Key MIC field not used */
+ struct ieee802_1x_hdr *s_hdr, *hdr;
+ struct wpa_eapol_key *s_key, *key;
+ u8 *buf, *s_key_data, *key_data;
+ size_t buf_len = msg_len + AES_BLOCK_SIZE;
+ size_t key_data_len;
+ u16 eapol_len;
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ if (!ptk || !ptk->kek_len)
+ goto out;
+
+ key_data_len = msg_len - sizeof(struct ieee802_1x_hdr) -
+ sizeof(struct wpa_eapol_key) - 2;
+
+ buf = os_malloc(buf_len);
+ if (!buf)
+ goto out;
+
+ os_memcpy(buf, msg, msg_len);
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ key_data = ((u8 *) (key + 1)) + 2;
+
+ /* Update EAPOL header to include AES-SIV overhead */
+ eapol_len = be_to_host16(hdr->length);
+ eapol_len += AES_BLOCK_SIZE;
+ hdr->length = host_to_be16(eapol_len);
+
+ /* Update Key Data Length field to include AES-SIV overhead */
+ WPA_PUT_BE16((u8 *) (key + 1), AES_BLOCK_SIZE + key_data_len);
+
+ s_hdr = (struct ieee802_1x_hdr *) msg;
+ s_key = (struct wpa_eapol_key *) (s_hdr + 1);
+ s_key_data = ((u8 *) (s_key + 1)) + 2;
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Plaintext Key Data",
+ s_key_data, key_data_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = buf;
+ aad_len[0] = key_data - buf;
+ if (aes_siv_encrypt(ptk->kek, ptk->kek_len,
+ s_key_data, key_data_len,
+ 1, aad, aad_len, key_data) < 0) {
+ os_free(buf);
+ goto out;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+ key_data, AES_BLOCK_SIZE + key_data_len);
+
+ os_free(msg);
+ msg = buf;
+ msg_len = buf_len;
+#else /* CONFIG_FILS */
goto out;
+#endif /* CONFIG_FILS */
}
- wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
- wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
+
wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
eapol_sm_notify_tx_eapol_key(sm->eapol);
@@ -97,12 +178,10 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
int key_info, ver;
- u8 bssid[ETH_ALEN], *rbuf, *key_mic;
+ u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
- if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->key_mgmt))
+ if (wpa_use_akm_defined(sm->key_mgmt))
ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
wpa_key_mgmt_sha256(sm->key_mgmt))
@@ -118,20 +197,21 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
return;
}
- mic_len = wpa_mic_len(sm->key_mgmt);
- hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
+ hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen, &rlen, (void *) &reply);
if (rbuf == NULL)
return;
- reply192 = (struct wpa_eapol_key_192 *) reply;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info = WPA_KEY_INFO_REQUEST | ver;
if (sm->ptk_set)
- key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ key_info |= WPA_KEY_INFO_SECURE;
+ if (sm->ptk_set && mic_len)
+ key_info |= WPA_KEY_INFO_MIC;
if (error)
key_info |= WPA_KEY_INFO_ERROR;
if (pairwise)
@@ -142,21 +222,19 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
WPA_REPLAY_COUNTER_LEN);
inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
- if (mic_len == 24)
- WPA_PUT_BE16(reply192->key_data_length, 0);
- else
- WPA_PUT_BE16(reply->key_data_length, 0);
+ mic = (u8 *) (reply + 1);
+ WPA_PUT_BE16(mic + mic_len, 0);
if (!(key_info & WPA_KEY_INFO_MIC))
key_mic = NULL;
else
- key_mic = reply192->key_mic; /* same offset in reply */
+ key_mic = mic;
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Sending EAPOL-Key Request (error=%d "
"pairwise=%d ptk_set=%d len=%lu)",
error, pairwise, sm->ptk_set, (unsigned long) rlen);
- wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
- ETH_P_EAPOL, rbuf, rlen, key_mic);
+ wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
+ key_mic);
}
@@ -190,7 +268,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
* event before receiving this 1/4 message, so try to find a
* matching PMKSA cache entry here. */
sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
- NULL);
+ NULL, 0);
if (sm->cur_pmksa) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: found matching PMKID from PMKSA cache");
@@ -210,11 +288,23 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
eapol_sm_notify_cached(sm->eapol);
#ifdef CONFIG_IEEE80211R
sm->xxkey_len = 0;
+#ifdef CONFIG_SAE
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE &&
+ sm->pmk_len == PMK_LEN) {
+ /* Need to allow FT key derivation to proceed with
+ * PMK from SAE being used as the XXKey in cases where
+ * the PMKID in msg 1/4 matches the PMKSA entry that was
+ * just added based on SAE authentication for the
+ * initial mobility domain association. */
+ os_memcpy(sm->xxkey, sm->pmk, sm->pmk_len);
+ sm->xxkey_len = sm->pmk_len;
+ }
+#endif /* CONFIG_SAE */
#endif /* CONFIG_IEEE80211R */
} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
int res, pmk_len;
- if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ if (wpa_key_mgmt_sha384(sm->key_mgmt))
pmk_len = PMK_LEN_SUITE_B_192;
else
pmk_len = PMK_LEN;
@@ -233,14 +323,28 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
u8 buf[2 * PMK_LEN];
if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
{
- os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
- sm->xxkey_len = PMK_LEN;
+ if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+ os_memcpy(sm->xxkey, buf,
+ SHA384_MAC_LEN);
+ sm->xxkey_len = SHA384_MAC_LEN;
+ } else {
+ os_memcpy(sm->xxkey, buf + PMK_LEN,
+ PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ }
os_memset(buf, 0, sizeof(buf));
}
#endif /* CONFIG_IEEE80211R */
}
if (res == 0) {
struct rsn_pmksa_cache_entry *sa = NULL;
+ const u8 *fils_cache_id = NULL;
+
+#ifdef CONFIG_FILS
+ if (sm->fils_cache_id_set)
+ fils_cache_id = sm->fils_cache_id;
+#endif /* CONFIG_FILS */
+
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
"machines", sm->pmk, pmk_len);
sm->pmk_len = pmk_len;
@@ -253,11 +357,12 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
NULL, 0,
src_addr, sm->own_addr,
sm->network_ctx,
- sm->key_mgmt);
+ sm->key_mgmt,
+ fils_cache_id);
}
if (!sm->cur_pmksa && pmkid &&
- pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
- {
+ pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL,
+ 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: the new PMK matches with the "
"PMKID");
@@ -341,9 +446,9 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
u8 *rbuf, *key_mic;
u8 *rsn_ie_buf = NULL;
+ u16 key_info;
if (wpa_ie == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
@@ -383,8 +488,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
- mic_len = wpa_mic_len(sm->key_mgmt);
- hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
+ hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
NULL, hdrlen + wpa_ie_len,
&rlen, (void *) &reply);
@@ -392,13 +497,16 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
os_free(rsn_ie_buf);
return -1;
}
- reply192 = (struct wpa_eapol_key_192 *) reply;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
- WPA_PUT_BE16(reply->key_info,
- ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
+ key_info = ver | WPA_KEY_INFO_KEY_TYPE;
+ if (mic_len)
+ key_info |= WPA_KEY_INFO_MIC;
+ else
+ key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+ WPA_PUT_BE16(reply->key_info, key_info);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
WPA_PUT_BE16(reply->key_length, 0);
else
@@ -408,21 +516,16 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
WPA_REPLAY_COUNTER_LEN);
- key_mic = reply192->key_mic; /* same offset for reply and reply192 */
- if (mic_len == 24) {
- WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len);
- os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len);
- } else {
- WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
- os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
- }
+ key_mic = (u8 *) (reply + 1);
+ WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */
+ os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */
os_free(rsn_ie_buf);
os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
- return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
- ETH_P_EAPOL, rbuf, rlen, key_mic);
+ return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+ key_mic);
}
@@ -500,7 +603,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
/* Calculate PTK which will be stored as a temporary PTK until it has
* been verified when processing message 3/4. */
ptk = &sm->tptk;
- wpa_derive_ptk(sm, src_addr, key, ptk);
+ if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
+ goto failed;
if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
u8 buf[8];
/* Supplicant: swap tx/rx Mic keys */
@@ -571,7 +675,9 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
eapol_sm_notify_portValid(sm->eapol, TRUE);
- if (wpa_key_mgmt_wpa_psk(sm->key_mgmt))
+ if (wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
+ sm->key_mgmt == WPA_KEY_MGMT_DPP ||
+ sm->key_mgmt == WPA_KEY_MGMT_OWE)
eapol_sm_notify_eap_success(sm->eapol, TRUE);
/*
* Start preauthentication after a short wait to avoid a
@@ -638,6 +744,11 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
alg = wpa_cipher_to_alg(sm->pairwise_cipher);
keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+ if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+ wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu",
+ keylen, (long unsigned int) sm->ptk.tk_len);
+ return -1;
+ }
rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
@@ -658,6 +769,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
/* TK is not needed anymore in supplicant */
os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+ sm->ptk.tk_len = 0;
sm->ptk.installed = 1;
if (sm->wpa_ptk_rekey) {
@@ -895,7 +1007,7 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
}
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
- "WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x",
+ "WPA: IGTK keyid %d pn " COMPACT_MACSTR,
keyidx, MAC2STR(igtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len);
if (keyidx > 4095) {
@@ -1203,22 +1315,24 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
u8 *rbuf, *key_mic;
- mic_len = wpa_mic_len(sm->key_mgmt);
- hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
+ hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen, &rlen, (void *) &reply);
if (rbuf == NULL)
return -1;
- reply192 = (struct wpa_eapol_key_192 *) reply;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info &= WPA_KEY_INFO_SECURE;
- key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
+ key_info |= ver | WPA_KEY_INFO_KEY_TYPE;
+ if (mic_len)
+ key_info |= WPA_KEY_INFO_MIC;
+ else
+ key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
WPA_PUT_BE16(reply->key_info, key_info);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
WPA_PUT_BE16(reply->key_length, 0);
@@ -1227,15 +1341,12 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
- key_mic = reply192->key_mic; /* same offset for reply and reply192 */
- if (mic_len == 24)
- WPA_PUT_BE16(reply192->key_data_length, 0);
- else
- WPA_PUT_BE16(reply->key_data_length, 0);
+ key_mic = (u8 *) (reply + 1);
+ WPA_PUT_BE16(key_mic + mic_len, 0);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
- return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
- ETH_P_EAPOL, rbuf, rlen, key_mic);
+ return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+ key_mic);
}
@@ -1350,13 +1461,19 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
if (ie.gtk)
wpa_sm_set_rekey_offload(sm);
- if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ /* Add PMKSA cache entry for Suite B AKMs here since PMKID can be
+ * calculated only after KCK has been derived. Though, do not replace an
+ * existing PMKSA entry after each 4-way handshake (i.e., new KCK/PMKID)
+ * to avoid unnecessary changes of PMKID while continuing to use the
+ * same PMK. */
+ if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ !sm->cur_pmksa) {
struct rsn_pmksa_cache_entry *sa;
sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
sm->ptk.kck, sm->ptk.kck_len,
sm->bssid, sm->own_addr,
- sm->network_ctx, sm->key_mgmt);
+ sm->network_ctx, sm->key_mgmt, NULL);
if (!sm->cur_pmksa)
sm->cur_pmksa = sa;
}
@@ -1378,7 +1495,8 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
int maxkeylen;
struct wpa_eapol_ie_parse ie;
- wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data",
+ keydata, keydatalen);
if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
return -1;
if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
@@ -1512,22 +1630,24 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
- struct wpa_eapol_key_192 *reply192;
u8 *rbuf, *key_mic;
- mic_len = wpa_mic_len(sm->key_mgmt);
- hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+ mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
+ hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen, &rlen, (void *) &reply);
if (rbuf == NULL)
return -1;
- reply192 = (struct wpa_eapol_key_192 *) reply;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
- key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ key_info |= ver | WPA_KEY_INFO_SECURE;
+ if (mic_len)
+ key_info |= WPA_KEY_INFO_MIC;
+ else
+ key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
WPA_PUT_BE16(reply->key_info, key_info);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
WPA_PUT_BE16(reply->key_length, 0);
@@ -1536,15 +1656,12 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
- key_mic = reply192->key_mic; /* same offset for reply and reply192 */
- if (mic_len == 24)
- WPA_PUT_BE16(reply192->key_data_length, 0);
- else
- WPA_PUT_BE16(reply->key_data_length, 0);
+ key_mic = (u8 *) (reply + 1);
+ WPA_PUT_BE16(key_mic + mic_len, 0);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
- return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver,
- sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic);
+ return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL,
+ rbuf, rlen, key_mic);
}
@@ -1559,7 +1676,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
struct wpa_gtk_data gd;
const u8 *key_rsc;
- if (!sm->msg_3_of_4_ok) {
+ if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Group Key Handshake started prior to completion of 4-way handshake");
goto failed;
@@ -1620,20 +1737,21 @@ failed:
static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
- struct wpa_eapol_key_192 *key,
+ struct wpa_eapol_key *key,
u16 ver,
const u8 *buf, size_t len)
{
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
int ok = 0;
- size_t mic_len = wpa_mic_len(sm->key_mgmt);
+ size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
- os_memcpy(mic, key->key_mic, mic_len);
+ os_memcpy(mic, key + 1, mic_len);
if (sm->tptk_set) {
- os_memset(key->key_mic, 0, mic_len);
- wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
- ver, buf, len, key->key_mic);
- if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+ os_memset(key + 1, 0, mic_len);
+ if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len,
+ sm->key_mgmt,
+ ver, buf, len, (u8 *) (key + 1)) < 0 ||
+ os_memcmp_const(mic, key + 1, mic_len) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC "
"when using TPTK - ignoring TPTK");
@@ -1643,14 +1761,23 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
sm->ptk_set = 1;
os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ /*
+ * This assures the same TPTK in sm->tptk can never be
+ * copied twice to sm->ptk as the new PTK. In
+ * combination with the installed flag in the wpa_ptk
+ * struct, this assures the same PTK is only installed
+ * once.
+ */
+ sm->renew_snonce = 1;
}
}
if (!ok && sm->ptk_set) {
- os_memset(key->key_mic, 0, mic_len);
- wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
- ver, buf, len, key->key_mic);
- if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+ os_memset(key + 1, 0, mic_len);
+ if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len,
+ sm->key_mgmt,
+ ver, buf, len, (u8 *) (key + 1)) < 0 ||
+ os_memcmp_const(mic, key + 1, mic_len) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC - "
"dropping packet");
@@ -1675,7 +1802,8 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
- struct wpa_eapol_key *key, u16 ver,
+ struct wpa_eapol_key *key,
+ size_t mic_len, u16 ver,
u8 *key_data, size_t *key_data_len)
{
wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
@@ -1696,6 +1824,8 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
return -1;
#else /* CONFIG_NO_RC4 */
u8 ek[32];
+
+ wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4");
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
@@ -1708,9 +1838,12 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
#endif /* CONFIG_NO_RC4 */
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
- sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ wpa_use_aes_key_wrap(sm->key_mgmt)) {
u8 *buf;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Decrypt Key Data using AES-UNWRAP (KEK length %u)",
+ (unsigned int) sm->ptk.kek_len);
if (*key_data_len < 8 || *key_data_len % 8) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported AES-WRAP len %u",
@@ -1734,7 +1867,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
}
os_memcpy(key_data, buf, *key_data_len);
bin_clear_free(buf, *key_data_len);
- WPA_PUT_BE16(key->key_data_length, *key_data_len);
+ WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported key_info type %d", ver);
@@ -1797,6 +1930,76 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm,
}
+#ifdef CONFIG_FILS
+static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len,
+ size_t *key_data_len)
+{
+ struct wpa_ptk *ptk;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u8 *pos, *tmp;
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ if (*key_data_len < AES_BLOCK_SIZE) {
+ wpa_printf(MSG_INFO, "No room for AES-SIV data in the frame");
+ return -1;
+ }
+
+ if (sm->tptk_set)
+ ptk = &sm->tptk;
+ else if (sm->ptk_set)
+ ptk = &sm->ptk;
+ else
+ return -1;
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ pos = (u8 *) (key + 1);
+ pos += 2; /* Pointing at the Encrypted Key Data field */
+
+ tmp = os_malloc(*key_data_len);
+ if (!tmp)
+ return -1;
+
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = buf;
+ aad_len[0] = pos - buf;
+ if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, *key_data_len,
+ 1, aad, aad_len, tmp) < 0) {
+ wpa_printf(MSG_INFO, "Invalid AES-SIV data in the frame");
+ bin_clear_free(tmp, *key_data_len);
+ return -1;
+ }
+
+ /* AEAD decryption and validation completed successfully */
+ (*key_data_len) -= AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+ tmp, *key_data_len);
+
+ /* Replace Key Data field with the decrypted version */
+ os_memcpy(pos, tmp, *key_data_len);
+ pos -= 2; /* Key Data Length field */
+ WPA_PUT_BE16(pos, *key_data_len);
+ bin_clear_free(tmp, *key_data_len);
+
+ if (sm->tptk_set) {
+ sm->tptk_set = 0;
+ sm->ptk_set = 1;
+ os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ }
+
+ os_memcpy(sm->rx_replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ sm->rx_replay_counter_set = 1;
+
+ return 0;
+}
+#endif /* CONFIG_FILS */
+
+
/**
* wpa_sm_rx_eapol - Process received WPA EAPOL frames
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -1819,20 +2022,18 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
size_t plen, data_len, key_data_len;
const struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- struct wpa_eapol_key_192 *key192;
u16 key_info, ver;
u8 *tmp = NULL;
int ret = -1;
- struct wpa_peerkey *peerkey = NULL;
- u8 *key_data;
+ u8 *mic, *key_data;
size_t mic_len, keyhdrlen;
#ifdef CONFIG_IEEE80211R
sm->ft_completed = 0;
#endif /* CONFIG_IEEE80211R */
- mic_len = wpa_mic_len(sm->key_mgmt);
- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+ mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
+ keyhdrlen = sizeof(*key) + mic_len + 2;
if (len < sizeof(*hdr) + keyhdrlen) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -1879,17 +2080,12 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
* Make a copy of the frame since we need to modify the buffer during
* MAC validation and Key Data decryption.
*/
- tmp = os_malloc(data_len);
+ tmp = os_memdup(buf, data_len);
if (tmp == NULL)
goto out;
- os_memcpy(tmp, buf, data_len);
key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
- key192 = (struct wpa_eapol_key_192 *)
- (tmp + sizeof(struct ieee802_1x_hdr));
- if (mic_len == 24)
- key_data = (u8 *) (key192 + 1);
- else
- key_data = (u8 *) (key + 1);
+ mic = (u8 *) (key + 1);
+ key_data = mic + mic_len + 2;
if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
{
@@ -1900,11 +2096,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
goto out;
}
- if (mic_len == 24)
- key_data_len = WPA_GET_BE16(key192->key_data_length);
- else
- key_data_len = WPA_GET_BE16(key->key_data_length);
- wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len);
+ key_data_len = WPA_GET_BE16(mic + mic_len);
+ wpa_eapol_key_dump(sm, key, key_data_len, mic, mic_len);
if (key_data_len > plen - keyhdrlen) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
@@ -1922,23 +2115,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
- !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
- sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
+ !wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Unsupported EAPOL-Key descriptor version %d",
ver);
goto out;
}
- if (sm->key_mgmt == WPA_KEY_MGMT_OSEN &&
- ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "OSEN: Unsupported EAPOL-Key descriptor version %d",
- ver);
- goto out;
- }
-
- if (wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ if (wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
@@ -1949,7 +2133,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
/* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
- if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+ !wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"FT: AP did not use AES-128-CMAC");
goto out;
@@ -1959,8 +2144,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
#ifdef CONFIG_IEEE80211W
if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
- sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
- !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ !wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: AP did not use the "
"negotiated AES-128-CMAC");
@@ -1969,7 +2153,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
} else
#endif /* CONFIG_IEEE80211W */
if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
- !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ !wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: CCMP is used, but EAPOL-Key "
@@ -1989,7 +2173,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
} else
goto out;
} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
- !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ !wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: GCMP is used, but EAPOL-Key "
@@ -1997,44 +2181,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
goto out;
}
-#ifdef CONFIG_PEERKEY
- for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
- if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
- break;
- }
-
- if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
- if (!peerkey->initiator && peerkey->replay_counter_set &&
- os_memcmp(key->replay_counter, peerkey->replay_counter,
- WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "RSN: EAPOL-Key Replay Counter did not "
- "increase (STK) - dropping packet");
- goto out;
- } else if (peerkey->initiator) {
- u8 _tmp[WPA_REPLAY_COUNTER_LEN];
- os_memcpy(_tmp, key->replay_counter,
- WPA_REPLAY_COUNTER_LEN);
- inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
- if (os_memcmp(_tmp, peerkey->replay_counter,
- WPA_REPLAY_COUNTER_LEN) != 0) {
- wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
- "RSN: EAPOL-Key Replay "
- "Counter did not match (STK) - "
- "dropping packet");
- goto out;
- }
- }
- }
-
- if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "RSN: Ack bit in key_info from STK peer");
- goto out;
- }
-#endif /* CONFIG_PEERKEY */
-
- if (!peerkey && sm->rx_replay_counter_set &&
+ if (sm->rx_replay_counter_set &&
os_memcmp(key->replay_counter, sm->rx_replay_counter,
WPA_REPLAY_COUNTER_LEN) <= 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -2043,11 +2190,13 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
goto out;
}
- if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
-#ifdef CONFIG_PEERKEY
- && (peerkey == NULL || !peerkey->initiator)
-#endif /* CONFIG_PEERKEY */
- ) {
+ if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported SMK bit in key_info");
+ goto out;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_ACK)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: No Ack bit in key_info");
goto out;
@@ -2059,19 +2208,19 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
goto out;
}
- if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
- wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
+ if ((key_info & WPA_KEY_INFO_MIC) &&
+ wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
goto out;
-#ifdef CONFIG_PEERKEY
- if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
- peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp,
- data_len))
- goto out;
-#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_FILS
+ if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len))
+ goto out;
+ }
+#endif /* CONFIG_FILS */
if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
- (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
/*
* Only decrypt the Key Data field if the frame's authenticity
* was verified. When using AES-SIV (FILS), the MIC flag is not
@@ -2083,7 +2232,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
"WPA: Ignore EAPOL-Key with encrypted but unauthenticated data");
goto out;
}
- if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
+ if (wpa_supplicant_decrypt_key_data(sm, key, mic_len,
+ ver, key_data,
&key_data_len))
goto out;
}
@@ -2095,11 +2245,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
"non-zero key index");
goto out;
}
- if (peerkey) {
- /* PeerKey 4-Way Handshake */
- peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
- key_data, key_data_len);
- } else if (key_info & WPA_KEY_INFO_MIC) {
+ if (key_info & (WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ENCR_KEY_DATA)) {
/* 3/4 4-Way Handshake */
wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
key_data_len);
@@ -2109,19 +2256,16 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
ver, key_data,
key_data_len);
}
- } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
- /* PeerKey SMK Handshake */
- peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info,
- ver);
} else {
- if (key_info & WPA_KEY_INFO_MIC) {
+ if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
+ (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
/* 1/2 Group Key Handshake */
wpa_supplicant_process_1_of_2(sm, src_addr, key,
key_data, key_data_len,
ver);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: EAPOL-Key (Group) without Mic bit - "
+ "WPA: EAPOL-Key (Group) without Mic/Encr bit - "
"dropped");
}
}
@@ -2296,6 +2440,7 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
}
if (deauth) {
+ sm->pmk_len = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
}
@@ -2353,13 +2498,21 @@ void wpa_sm_deinit(struct wpa_sm *sm)
os_free(sm->ap_rsn_ie);
wpa_sm_drop_sa(sm);
os_free(sm->ctx);
- peerkey_deinit(sm);
#ifdef CONFIG_IEEE80211R
os_free(sm->assoc_resp_ies);
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_TESTING_OPTIONS
wpabuf_free(sm->test_assoc_ie);
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_FILS_SK_PFS
+ crypto_ecdh_deinit(sm->fils_ecdh);
+#endif /* CONFIG_FILS_SK_PFS */
+#ifdef CONFIG_FILS
+ wpabuf_free(sm->fils_ft_ies);
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ crypto_ecdh_deinit(sm->owe_ecdh);
+#endif /* CONFIG_OWE */
os_free(sm);
}
@@ -2403,6 +2556,16 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
clear_keys = 0;
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_FILS
+ if (sm->fils_completed) {
+ /*
+ * Clear portValid to kick EAPOL state machine to re-enter
+ * AUTHENTICATED state to get the EAPOL port Authorized.
+ */
+ wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
+ clear_keys = 0;
+ }
+#endif /* CONFIG_FILS */
if (clear_keys) {
/*
@@ -2443,7 +2606,6 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
{
eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
- peerkey_deinit(sm);
rsn_preauth_deinit(sm);
pmksa_cache_clear_current(sm);
if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
@@ -2451,6 +2613,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
#ifdef CONFIG_TDLS
wpa_tdls_disassoc(sm);
#endif /* CONFIG_TDLS */
+#ifdef CONFIG_FILS
+ sm->fils_completed = 0;
+#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
sm->ft_reassoc_completed = 0;
#endif /* CONFIG_IEEE80211R */
@@ -2459,6 +2624,7 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
wpa_sm_drop_sa(sm);
sm->msg_3_of_4_ok = 0;
+ os_memset(sm->bssid, 0, ETH_ALEN);
}
@@ -2478,6 +2644,8 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
if (sm == NULL)
return;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on external data",
+ pmk, pmk_len);
sm->pmk_len = pmk_len;
os_memcpy(sm->pmk, pmk, pmk_len);
@@ -2490,7 +2658,7 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
if (bssid) {
pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
bssid, sm->own_addr,
- sm->network_ctx, sm->key_mgmt);
+ sm->network_ctx, sm->key_mgmt, NULL);
}
}
@@ -2508,11 +2676,15 @@ void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
return;
if (sm->cur_pmksa) {
+ wpa_hexdump_key(MSG_DEBUG,
+ "WPA: Set PMK based on current PMKSA",
+ sm->cur_pmksa->pmk, sm->cur_pmksa->pmk_len);
sm->pmk_len = sm->cur_pmksa->pmk_len;
os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
} else {
- sm->pmk_len = PMK_LEN;
- os_memset(sm->pmk, 0, PMK_LEN);
+ wpa_printf(MSG_DEBUG, "WPA: No current PMKSA - clear PMK");
+ sm->pmk_len = 0;
+ os_memset(sm->pmk, 0, PMK_LEN_MAX);
}
}
@@ -2560,7 +2732,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
if (config) {
sm->network_ctx = config->network_ctx;
- sm->peerkey_enabled = config->peerkey_enabled;
sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
sm->proactive_key_caching = config->proactive_key_caching;
sm->eap_workaround = config->eap_workaround;
@@ -2573,9 +2744,17 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
sm->p2p = config->p2p;
sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation;
+#ifdef CONFIG_FILS
+ if (config->fils_cache_id) {
+ sm->fils_cache_id_set = 1;
+ os_memcpy(sm->fils_cache_id, config->fils_cache_id,
+ FILS_CACHE_ID_LEN);
+ } else {
+ sm->fils_cache_id_set = 0;
+ }
+#endif /* CONFIG_FILS */
} else {
sm->network_ctx = NULL;
- sm->peerkey_enabled = 0;
sm->allowed_pairwise_cipher = 0;
sm->proactive_key_caching = 0;
sm->eap_workaround = 0;
@@ -2728,9 +2907,12 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
>= 0 &&
rsn.capabilities & (WPA_CAPABILITY_MFPR |
WPA_CAPABILITY_MFPC)) {
- ret = os_snprintf(pos, end - pos, "pmf=%d\n",
+ ret = os_snprintf(pos, end - pos, "pmf=%d\n"
+ "mgmt_group_cipher=%s\n",
(rsn.capabilities &
- WPA_CAPABILITY_MFPR) ? 2 : 1);
+ WPA_CAPABILITY_MFPR) ? 2 : 1,
+ wpa_cipher_txt(
+ sm->mgmt_group_cipher));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
@@ -2796,12 +2978,15 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
* the correct version of the IE even if PMKSA caching is
* aborted (which would remove PMKID from IE generation).
*/
- sm->assoc_wpa_ie = os_malloc(*wpa_ie_len);
+ sm->assoc_wpa_ie = os_memdup(wpa_ie, *wpa_ie_len);
if (sm->assoc_wpa_ie == NULL)
return -1;
- os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
sm->assoc_wpa_ie_len = *wpa_ie_len;
+ } else {
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: Leave previously set WPA IE default",
+ sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
}
return 0;
@@ -2832,11 +3017,10 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
sm->assoc_wpa_ie_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
- sm->assoc_wpa_ie = os_malloc(len);
+ sm->assoc_wpa_ie = os_memdup(ie, len);
if (sm->assoc_wpa_ie == NULL)
return -1;
- os_memcpy(sm->assoc_wpa_ie, ie, len);
sm->assoc_wpa_ie_len = len;
}
@@ -2867,11 +3051,10 @@ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
sm->ap_wpa_ie_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
- sm->ap_wpa_ie = os_malloc(len);
+ sm->ap_wpa_ie = os_memdup(ie, len);
if (sm->ap_wpa_ie == NULL)
return -1;
- os_memcpy(sm->ap_wpa_ie, ie, len);
sm->ap_wpa_ie_len = len;
}
@@ -2902,11 +3085,10 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
sm->ap_rsn_ie_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
- sm->ap_rsn_ie = os_malloc(len);
+ sm->ap_rsn_ie = os_memdup(ie, len);
if (sm->ap_rsn_ie == NULL)
return -1;
- os_memcpy(sm->ap_rsn_ie, ie, len);
sm->ap_rsn_ie_len = len;
}
@@ -2945,11 +3127,43 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
}
+struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm)
+{
+ return pmksa_cache_head(sm->pmksa);
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry * entry)
+{
+ return pmksa_cache_add_entry(sm->pmksa, entry);
+}
+
+
+void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+ const u8 *pmkid, const u8 *bssid,
+ const u8 *fils_cache_id)
+{
+ sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
+ bssid, sm->own_addr, sm->network_ctx,
+ sm->key_mgmt, fils_cache_id);
+}
+
+
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+ const void *network_ctx)
+{
+ return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL;
+}
+
+
void wpa_sm_drop_sa(struct wpa_sm *sm)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
sm->ptk_set = 0;
sm->tptk_set = 0;
+ sm->pmk_len = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
os_memset(&sm->ptk, 0, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
@@ -2961,8 +3175,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+ sm->xxkey_len = 0;
os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
+ sm->pmk_r0_len = 0;
os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+ sm->pmk_r1_len = 0;
#endif /* CONFIG_IEEE80211R */
}
@@ -3047,27 +3264,6 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
#endif /* CONFIG_WNM */
-#ifdef CONFIG_PEERKEY
-int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
- const u8 *buf, size_t len)
-{
- struct wpa_peerkey *peerkey;
-
- for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
- if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
- break;
- }
-
- if (!peerkey)
- return 0;
-
- wpa_sm_rx_eapol(sm, src_addr, buf, len);
-
- return 1;
-}
-#endif /* CONFIG_PEERKEY */
-
-
#ifdef CONFIG_P2P
int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
@@ -3112,9 +3308,1130 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
#ifdef CONFIG_TESTING_OPTIONS
+
void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf)
{
wpabuf_free(sm->test_assoc_ie);
sm->test_assoc_ie = buf;
}
+
+
+const u8 * wpa_sm_get_anonce(struct wpa_sm *sm)
+{
+ return sm->anonce;
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
+
+
+unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm)
+{
+ return sm->key_mgmt;
+}
+
+
+#ifdef CONFIG_FILS
+
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
+{
+ struct wpabuf *buf = NULL;
+ struct wpabuf *erp_msg;
+ struct wpabuf *pub = NULL;
+
+ erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
+ if (!erp_msg && !sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Neither ERP EAP-Initiate/Re-auth nor PMKSA cache entry is available - skip FILS");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)",
+ erp_msg != NULL, sm->cur_pmksa != NULL);
+
+ sm->fils_completed = 0;
+
+ if (!sm->assoc_wpa_ie) {
+ wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS");
+ goto fail;
+ }
+
+ if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 ||
+ random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0)
+ goto fail;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce",
+ sm->fils_nonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
+ sm->fils_session, FILS_SESSION_LEN);
+
+#ifdef CONFIG_FILS_SK_PFS
+ sm->fils_dh_group = dh_group;
+ if (dh_group) {
+ crypto_ecdh_deinit(sm->fils_ecdh);
+ sm->fils_ecdh = crypto_ecdh_init(dh_group);
+ if (!sm->fils_ecdh) {
+ wpa_printf(MSG_INFO,
+ "FILS: Could not initialize ECDH with group %d",
+ dh_group);
+ goto fail;
+ }
+ pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+ if (!pub)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)",
+ pub);
+ sm->fils_dh_elem_len = wpabuf_len(pub);
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len +
+ (pub ? wpabuf_len(pub) : 0));
+ if (!buf)
+ goto fail;
+
+ /* Fields following the Authentication algorithm number field */
+
+ /* Authentication Transaction seq# */
+ wpabuf_put_le16(buf, 1);
+
+ /* Status Code */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ /* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+ if (dh_group) {
+ /* Finite Cyclic Group */
+ wpabuf_put_le16(buf, dh_group);
+ /* Element */
+ wpabuf_put_buf(buf, pub);
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ /* RSNE */
+ wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame",
+ sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
+ wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
+
+ if (md) {
+ /* MDE when using FILS for FT initial association */
+ struct rsn_mdie *mdie;
+
+ wpabuf_put_u8(buf, WLAN_EID_MOBILITY_DOMAIN);
+ wpabuf_put_u8(buf, sizeof(*mdie));
+ mdie = wpabuf_put(buf, sizeof(*mdie));
+ os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
+ mdie->ft_capab = 0;
+ }
+
+ /* FILS Nonce */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+ wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN);
+
+ /* FILS Session */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
+
+ /* FILS Wrapped Data */
+ sm->fils_erp_pmkid_set = 0;
+ if (erp_msg) {
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_WRAPPED_DATA);
+ wpabuf_put_buf(buf, erp_msg);
+ /* Calculate pending PMKID here so that we do not need to
+ * maintain a copy of the EAP-Initiate/Reauth message. */
+ if (fils_pmkid_erp(sm->key_mgmt, wpabuf_head(erp_msg),
+ wpabuf_len(erp_msg),
+ sm->fils_erp_pmkid) == 0)
+ sm->fils_erp_pmkid_set = 1;
+ }
+
+ wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame",
+ buf);
+
+fail:
+ wpabuf_free(erp_msg);
+ wpabuf_free(pub);
+ return buf;
+}
+
+
+int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
+ size_t len)
+{
+ const u8 *pos, *end;
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsn;
+ int pmkid_match = 0;
+ u8 ick[FILS_ICK_MAX_LEN];
+ size_t ick_len;
+ int res;
+ struct wpabuf *dh_ss = NULL;
+ const u8 *g_sta = NULL;
+ size_t g_sta_len = 0;
+ const u8 *g_ap = NULL;
+ size_t g_ap_len = 0;
+ struct wpabuf *pub = NULL;
+
+ os_memcpy(sm->bssid, bssid, ETH_ALEN);
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
+ data, len);
+ pos = data;
+ end = data + len;
+
+ /* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+ if (sm->fils_dh_group) {
+ u16 group;
+
+ /* Using FILS PFS */
+
+ /* Finite Cyclic Group */
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No room for Finite Cyclic Group");
+ goto fail;
+ }
+ group = WPA_GET_LE16(pos);
+ pos += 2;
+ if (group != sm->fils_dh_group) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)",
+ group, sm->fils_dh_group);
+ goto fail;
+ }
+
+ /* Element */
+ if ((size_t) (end - pos) < sm->fils_dh_elem_len) {
+ wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+ goto fail;
+ }
+
+ if (!sm->fils_ecdh) {
+ wpa_printf(MSG_DEBUG, "FILS: No ECDH state available");
+ goto fail;
+ }
+ dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos,
+ sm->fils_dh_elem_len);
+ if (!dh_ss) {
+ wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+ goto fail;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss);
+ g_ap = pos;
+ g_ap_len = sm->fils_dh_elem_len;
+ pos += sm->fils_dh_elem_len;
+ }
+#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");
+ 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 RSN element");
+ goto fail;
+ }
+
+ if (!elems.fils_nonce) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+ goto fail;
+ }
+ os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+ struct wpa_ft_ies parse;
+
+ if (!elems.mdie || !elems.ftie) {
+ wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE");
+ goto fail;
+ }
+
+ if (wpa_ft_parse_ies(pos, end - pos, &parse,
+ wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
+ goto fail;
+ }
+
+ if (!parse.r0kh_id) {
+ wpa_printf(MSG_DEBUG,
+ "FILS+FT: No R0KH-ID subelem in FTE");
+ goto fail;
+ }
+ os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+ sm->r0kh_id_len = parse.r0kh_id_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+
+ if (!parse.r1kh_id) {
+ wpa_printf(MSG_DEBUG,
+ "FILS+FT: No R1KH-ID subelem in FTE");
+ goto fail;
+ }
+ os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID",
+ sm->r1kh_id, FT_R1KH_ID_LEN);
+
+ /* TODO: Check MDE and FTE payload */
+
+ wpabuf_free(sm->fils_ft_ies);
+ sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len +
+ 2 + elems.ftie_len);
+ if (!sm->fils_ft_ies)
+ goto fail;
+ wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2,
+ 2 + elems.mdie_len);
+ wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2,
+ 2 + elems.ftie_len);
+ } else {
+ wpabuf_free(sm->fils_ft_ies);
+ sm->fils_ft_ies = NULL;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ /* PMKID List */
+ if (rsn.pmkid && rsn.num_pmkid > 0) {
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+ rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+ if (rsn.num_pmkid != 1) {
+ wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
+ if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
+ sm->cur_pmksa->pmkid, PMKID_LEN);
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG,
+ "FILS: Matching PMKID - continue using PMKSA caching");
+ pmkid_match = 1;
+ }
+ if (!pmkid_match && sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No PMKID match - cannot use cached PMKSA entry");
+ sm->cur_pmksa = NULL;
+ }
+
+ /* FILS Session */
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+ FILS_SESSION_LEN);
+ if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN)
+ != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+ sm->fils_session, FILS_SESSION_LEN);
+ goto fail;
+ }
+
+ /* FILS Wrapped Data */
+ if (!sm->cur_pmksa && elems.fils_wrapped_data) {
+ u8 rmsk[ERP_MAX_KEY_LEN];
+ size_t rmsk_len;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+ elems.fils_wrapped_data,
+ elems.fils_wrapped_data_len);
+ eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data,
+ elems.fils_wrapped_data_len);
+ if (eapol_sm_failed(sm->eapol))
+ goto fail;
+
+ rmsk_len = ERP_MAX_KEY_LEN;
+ res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
+ if (res == PMK_LEN) {
+ rmsk_len = PMK_LEN;
+ res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
+ }
+ if (res)
+ goto fail;
+
+ res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len,
+ sm->fils_nonce, sm->fils_anonce,
+ dh_ss ? wpabuf_head(dh_ss) : NULL,
+ dh_ss ? wpabuf_len(dh_ss) : 0,
+ sm->pmk, &sm->pmk_len);
+ os_memset(rmsk, 0, sizeof(rmsk));
+
+ /* Don't use DHss in PTK derivation if PMKSA caching is not
+ * used. */
+ wpabuf_clear_free(dh_ss);
+ dh_ss = NULL;
+
+ if (res)
+ goto fail;
+
+ if (!sm->fils_erp_pmkid_set) {
+ wpa_printf(MSG_DEBUG, "FILS: PMKID not available");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid,
+ PMKID_LEN);
+ wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result");
+ sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
+ sm->fils_erp_pmkid, NULL, 0,
+ sm->bssid, sm->own_addr,
+ sm->network_ctx, sm->key_mgmt,
+ NULL);
+ }
+
+ if (!sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No remaining options to continue FILS authentication");
+ goto fail;
+ }
+
+ if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
+ sm->fils_nonce, sm->fils_anonce,
+ dh_ss ? wpabuf_head(dh_ss) : NULL,
+ dh_ss ? wpabuf_len(dh_ss) : 0,
+ &sm->ptk, ick, &ick_len,
+ sm->key_mgmt, sm->pairwise_cipher,
+ sm->fils_ft, &sm->fils_ft_len) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
+ goto fail;
+ }
+
+ wpabuf_clear_free(dh_ss);
+ dh_ss = NULL;
+
+ sm->ptk_set = 1;
+ sm->tptk_set = 0;
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+
+#ifdef CONFIG_FILS_SK_PFS
+ if (sm->fils_dh_group) {
+ if (!sm->fils_ecdh) {
+ wpa_printf(MSG_INFO, "FILS: ECDH not initialized");
+ goto fail;
+ }
+ pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+ if (!pub)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "FILS: gSTA", pub);
+ g_sta = wpabuf_head(pub);
+ g_sta_len = wpabuf_len(pub);
+ if (!g_ap) {
+ wpa_printf(MSG_INFO, "FILS: gAP not available");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce,
+ sm->fils_anonce, sm->own_addr, sm->bssid,
+ g_sta, g_sta_len, g_ap, g_ap_len,
+ sm->key_mgmt, sm->fils_key_auth_sta,
+ sm->fils_key_auth_ap,
+ &sm->fils_key_auth_len);
+ wpabuf_free(pub);
+ os_memset(ick, 0, sizeof(ick));
+ return res;
+fail:
+ wpabuf_free(pub);
+ wpabuf_clear_free(dh_ss);
+ return -1;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
+{
+ struct rsn_ie_hdr *rsnie;
+ u16 capab;
+ u8 *pos;
+ int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+
+ /* RSNIE[PMKR0Name/PMKR1Name] */
+ rsnie = wpabuf_put(buf, sizeof(*rsnie));
+ rsnie->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+
+ /* Group Suite Selector */
+ if (!wpa_cipher_valid_group(sm->group_cipher)) {
+ wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+ sm->group_cipher);
+ return -1;
+ }
+ pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+ sm->group_cipher));
+
+ /* Pairwise Suite Count */
+ wpabuf_put_le16(buf, 1);
+
+ /* Pairwise Suite List */
+ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+ wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+ sm->pairwise_cipher);
+ return -1;
+ }
+ pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+ sm->pairwise_cipher));
+
+ /* Authenticated Key Management Suite Count */
+ wpabuf_put_le16(buf, 1);
+
+ /* Authenticated Key Management Suite List */
+ pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+ else {
+ wpa_printf(MSG_WARNING,
+ "FILS+FT: Invalid key management type (%d)",
+ sm->key_mgmt);
+ return -1;
+ }
+
+ /* RSN Capabilities */
+ capab = 0;
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+ capab |= WPA_CAPABILITY_MFPC;
+#endif /* CONFIG_IEEE80211W */
+ wpabuf_put_le16(buf, capab);
+
+ /* PMKID Count */
+ wpabuf_put_le16(buf, 1);
+
+ /* PMKID List [PMKR1Name] */
+ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)",
+ sm->fils_ft, sm->fils_ft_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len);
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID",
+ sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+ if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid,
+ sm->ssid_len, sm->mobility_domain,
+ sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+ sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) {
+ wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
+ return -1;
+ }
+ sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
+ sm->pmk_r0, sm->pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
+ sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+ wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
+ MAC2STR(sm->r1kh_id));
+ pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
+ if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
+ pos, use_sha384) < 0) {
+ wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN);
+
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ /* Management Group Cipher Suite */
+ pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2;
+ return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
+ size_t *kek_len, const u8 **snonce,
+ const u8 **anonce,
+ const struct wpabuf **hlp,
+ unsigned int num_hlp)
+{
+ struct wpabuf *buf;
+ size_t len;
+ unsigned int i;
+
+ len = 1000;
+#ifdef CONFIG_IEEE80211R
+ if (sm->fils_ft_ies)
+ len += wpabuf_len(sm->fils_ft_ies);
+ if (wpa_key_mgmt_ft(sm->key_mgmt))
+ len += 256;
+#endif /* CONFIG_IEEE80211R */
+ for (i = 0; hlp && i < num_hlp; i++)
+ len += 10 + wpabuf_len(hlp[i]);
+ buf = wpabuf_alloc(len);
+ if (!buf)
+ return NULL;
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
+ /* MDE and FTE when using FILS+FT */
+ wpabuf_put_buf(buf, sm->fils_ft_ies);
+ /* RSNE with PMKR1Name in PMKID field */
+ if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ /* FILS Session */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
+
+ /* Everything after FILS Session element gets encrypted in the driver
+ * with KEK. The buffer returned from here is the plaintext version. */
+
+ /* TODO: FILS Public Key */
+
+ /* FILS Key Confirm */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(buf, 1 + sm->fils_key_auth_len); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+ wpabuf_put_data(buf, sm->fils_key_auth_sta, sm->fils_key_auth_len);
+
+ /* FILS HLP Container */
+ for (i = 0; hlp && i < num_hlp; i++) {
+ const u8 *pos = wpabuf_head(hlp[i]);
+ size_t left = wpabuf_len(hlp[i]);
+
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+ if (left <= 254)
+ len = 1 + left;
+ else
+ len = 255;
+ wpabuf_put_u8(buf, len); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_HLP_CONTAINER);
+ /* Destination MAC Address, Source MAC Address, HLP Packet.
+ * HLP Packet is in MSDU format (i.e., included the LLC/SNAP
+ * header when LPD is used). */
+ wpabuf_put_data(buf, pos, len - 1);
+ pos += len - 1;
+ left -= len - 1;
+ while (left) {
+ wpabuf_put_u8(buf, WLAN_EID_FRAGMENT);
+ len = left > 255 ? 255 : left;
+ wpabuf_put_u8(buf, len);
+ wpabuf_put_data(buf, pos, len);
+ pos += len;
+ left -= len;
+ }
+ }
+
+ /* TODO: FILS IP Address Assignment */
+
+ wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf);
+
+ *kek = sm->ptk.kek;
+ *kek_len = sm->ptk.kek_len;
+ wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len);
+ *snonce = sm->fils_nonce;
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD",
+ *snonce, FILS_NONCE_LEN);
+ *anonce = sm->fils_anonce;
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD",
+ *anonce, FILS_NONCE_LEN);
+
+ return buf;
+}
+
+
+static void fils_process_hlp_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
+{
+ const u8 *pos, *end;
+
+ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP response", resp, len);
+ if (len < 2 * ETH_ALEN)
+ return;
+ pos = resp + 2 * ETH_ALEN;
+ end = resp + len;
+ if (end - pos >= 6 &&
+ os_memcmp(pos, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
+ pos += 6; /* Remove SNAP/LLC header */
+ wpa_sm_fils_hlp_rx(sm, resp, resp + ETH_ALEN, pos, end - pos);
+}
+
+
+static void fils_process_hlp_container(struct wpa_sm *sm, const u8 *pos,
+ size_t len)
+{
+ const u8 *end = pos + len;
+ u8 *tmp, *tmp_pos;
+
+ /* Check if there are any FILS HLP Container elements */
+ while (end - pos >= 2) {
+ if (2 + pos[1] > end - pos)
+ return;
+ 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; /* No FILS HLP Container elements */
+
+ tmp = os_malloc(end - pos);
+ if (!tmp)
+ return;
+
+ 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];
+ }
+
+ fils_process_hlp_resp(sm, tmp, tmp_pos - tmp);
+ }
+
+ os_free(tmp);
+}
+
+
+int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *end, *ie_start;
+ struct ieee802_11_elems elems;
+ int keylen, rsclen;
+ enum wpa_alg alg;
+ struct wpa_gtk_data gd;
+ int maxkeylen;
+ struct wpa_eapol_ie_parse kde;
+
+ if (!sm || !sm->ptk_set) {
+ wpa_printf(MSG_DEBUG, "FILS: No KEK available");
+ return -1;
+ }
+
+ if (!wpa_key_mgmt_fils(sm->key_mgmt)) {
+ wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM");
+ return -1;
+ }
+
+ if (sm->fils_completed) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Association has already been completed for this FILS authentication - ignore unexpected retransmission");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame",
+ resp, len);
+
+ mgmt = (const struct ieee80211_mgmt *) resp;
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp))
+ return -1;
+
+ end = resp + len;
+ /* Same offset for Association Response and Reassociation Response */
+ ie_start = mgmt->u.assoc_resp.variable;
+
+ if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to parse decrypted elements");
+ goto fail;
+ }
+
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ return -1;
+ }
+ if (os_memcmp(elems.fils_session, sm->fils_session,
+ FILS_SESSION_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+ elems.fils_session, FILS_SESSION_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+ sm->fils_session, FILS_SESSION_LEN);
+ }
+
+ /* TODO: FILS Public Key */
+
+ if (!elems.fils_key_confirm) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+ goto fail;
+ }
+ if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Unexpected Key-Auth length %d (expected %d)",
+ elems.fils_key_confirm_len,
+ (int) sm->fils_key_auth_len);
+ goto fail;
+ }
+ if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap,
+ sm->fils_key_auth_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+ elems.fils_key_confirm,
+ elems.fils_key_confirm_len);
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+ sm->fils_key_auth_ap, sm->fils_key_auth_len);
+ goto fail;
+ }
+
+ /* Key Delivery */
+ if (!elems.key_delivery) {
+ wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element");
+ goto fail;
+ }
+
+ /* Parse GTK and set the key to the driver */
+ os_memset(&gd, 0, sizeof(gd));
+ if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN,
+ elems.key_delivery_len - WPA_KEY_RSC_LEN,
+ &kde) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs");
+ goto fail;
+ }
+ if (!kde.gtk) {
+ wpa_printf(MSG_DEBUG, "FILS: No GTK KDE");
+ goto fail;
+ }
+ maxkeylen = gd.gtk_len = kde.gtk_len - 2;
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gd.gtk_len, maxkeylen,
+ &gd.key_rsc_len, &gd.alg))
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len);
+ gd.keyidx = kde.gtk[0] & 0x3;
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(kde.gtk[0] & BIT(2)));
+ if (kde.gtk_len - 2 > sizeof(gd.gtk)) {
+ wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)",
+ (unsigned long) kde.gtk_len - 2);
+ goto fail;
+ }
+ os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2);
+
+ wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver");
+ if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK");
+ goto fail;
+ }
+
+ if (ieee80211w_set_keys(sm, &kde) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK");
+ goto fail;
+ }
+
+ alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+ keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+ if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+ wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu",
+ keylen, (long unsigned int) sm->ptk.tk_len);
+ goto fail;
+ }
+ rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
+ sm->ptk.tk, keylen);
+ if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
+ sm->ptk.tk, keylen) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+ MACSTR ")",
+ alg, keylen, MAC2STR(sm->bssid));
+ goto fail;
+ }
+
+ /* TODO: TK could be cleared after auth frame exchange now that driver
+ * takes care of association frame encryption/decryption. */
+ /* TK is not needed anymore in supplicant */
+ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+ sm->ptk.tk_len = 0;
+ sm->ptk.installed = 1;
+
+ /* FILS HLP Container */
+ fils_process_hlp_container(sm, ie_start, end - ie_start);
+
+ /* TODO: FILS IP Address Assignment */
+
+ wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully");
+ sm->fils_completed = 1;
+
+ return 0;
+fail:
+ return -1;
+}
+
+
+void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set)
+{
+ if (sm)
+ sm->fils_completed = !!set;
+}
+
+#endif /* CONFIG_FILS */
+
+
+int wpa_fils_is_completed(struct wpa_sm *sm)
+{
+#ifdef CONFIG_FILS
+ return sm && sm->fils_completed;
+#else /* CONFIG_FILS */
+ return 0;
+#endif /* CONFIG_FILS */
+}
+
+
+#ifdef CONFIG_OWE
+
+struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group)
+{
+ struct wpabuf *ie = NULL, *pub = NULL;
+ size_t prime_len;
+
+ if (group == 19)
+ prime_len = 32;
+ else if (group == 20)
+ prime_len = 48;
+ else if (group == 21)
+ prime_len = 66;
+ else
+ return NULL;
+
+ crypto_ecdh_deinit(sm->owe_ecdh);
+ sm->owe_ecdh = crypto_ecdh_init(group);
+ if (!sm->owe_ecdh)
+ goto fail;
+ sm->owe_group = group;
+ pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
+ pub = wpabuf_zeropad(pub, prime_len);
+ if (!pub)
+ goto fail;
+
+ ie = wpabuf_alloc(5 + wpabuf_len(pub));
+ if (!ie)
+ goto fail;
+ wpabuf_put_u8(ie, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub));
+ wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM);
+ wpabuf_put_le16(ie, group);
+ wpabuf_put_buf(ie, pub);
+ wpabuf_free(pub);
+ wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element",
+ ie);
+
+ return ie;
+fail:
+ wpabuf_free(pub);
+ crypto_ecdh_deinit(sm->owe_ecdh);
+ sm->owe_ecdh = NULL;
+ return NULL;
+}
+
+
+int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *resp_ies, size_t resp_ies_len)
+{
+ struct ieee802_11_elems elems;
+ u16 group;
+ struct wpabuf *secret, *pub, *hkey;
+ int res;
+ u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+ const char *info = "OWE Key Generation";
+ const u8 *addr[2];
+ size_t len[2];
+ size_t hash_len, prime_len;
+ struct wpa_ie_data data;
+
+ if (!resp_ies ||
+ ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_INFO,
+ "OWE: Could not parse Association Response frame elements");
+ return -1;
+ }
+
+ if (sm->cur_pmksa && elems.rsn_ie &&
+ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, 2 + elems.rsn_ie_len,
+ &data) == 0 &&
+ data.num_pmkid == 1 && data.pmkid &&
+ os_memcmp(sm->cur_pmksa->pmkid, data.pmkid, PMKID_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "OWE: Use PMKSA caching");
+ wpa_sm_set_pmk_from_pmksa(sm);
+ return 0;
+ }
+
+ if (!elems.owe_dh) {
+ wpa_printf(MSG_INFO,
+ "OWE: No Diffie-Hellman Parameter element found in Association Response frame");
+ return -1;
+ }
+
+ group = WPA_GET_LE16(elems.owe_dh);
+ if (group != sm->owe_group) {
+ wpa_printf(MSG_INFO,
+ "OWE: Unexpected Diffie-Hellman group in response: %u",
+ group);
+ return -1;
+ }
+
+ if (!sm->owe_ecdh) {
+ wpa_printf(MSG_INFO, "OWE: No ECDH state available");
+ return -1;
+ }
+
+ if (group == 19)
+ prime_len = 32;
+ else if (group == 20)
+ prime_len = 48;
+ else if (group == 21)
+ prime_len = 66;
+ else
+ return -1;
+
+ secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0,
+ elems.owe_dh + 2,
+ elems.owe_dh_len - 2);
+ secret = wpabuf_zeropad(secret, prime_len);
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+ return -1;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+ /* prk = HKDF-extract(C | A | group, z) */
+
+ pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
+ if (!pub) {
+ wpabuf_clear_free(secret);
+ return -1;
+ }
+
+ /* PMKID = Truncate-128(Hash(C | A)) */
+ addr[0] = wpabuf_head(pub);
+ len[0] = wpabuf_len(pub);
+ addr[1] = elems.owe_dh + 2;
+ len[1] = elems.owe_dh_len - 2;
+ if (group == 19) {
+ res = sha256_vector(2, addr, len, pmkid);
+ hash_len = SHA256_MAC_LEN;
+ } else if (group == 20) {
+ res = sha384_vector(2, addr, len, pmkid);
+ hash_len = SHA384_MAC_LEN;
+ } else if (group == 21) {
+ res = sha512_vector(2, addr, len, pmkid);
+ hash_len = SHA512_MAC_LEN;
+ } else {
+ res = -1;
+ hash_len = 0;
+ }
+ pub = wpabuf_zeropad(pub, prime_len);
+ if (res < 0 || !pub) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return -1;
+ }
+
+ hkey = wpabuf_alloc(wpabuf_len(pub) + elems.owe_dh_len - 2 + 2);
+ if (!hkey) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return -1;
+ }
+
+ wpabuf_put_buf(hkey, pub); /* C */
+ wpabuf_free(pub);
+ wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */
+ wpabuf_put_le16(hkey, sm->owe_group); /* group */
+ if (group == 19)
+ res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ else if (group == 20)
+ res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ else if (group == 21)
+ res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ wpabuf_clear_free(hkey);
+ wpabuf_clear_free(secret);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+ /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+ if (group == 19)
+ res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sm->pmk, hash_len);
+ else if (group == 20)
+ res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sm->pmk, hash_len);
+ else if (group == 21)
+ res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sm->pmk, hash_len);
+ os_memset(prk, 0, SHA512_MAC_LEN);
+ if (res < 0) {
+ sm->pmk_len = 0;
+ return -1;
+ }
+ sm->pmk_len = hash_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len);
+ wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+ pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0,
+ bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt,
+ NULL);
+
+ return 0;
+}
+
+#endif /* CONFIG_OWE */
+
+
+void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id)
+{
+#ifdef CONFIG_FILS
+ if (sm && fils_cache_id) {
+ sm->fils_cache_id_set = 1;
+ os_memcpy(sm->fils_cache_id, fils_cache_id, FILS_CACHE_ID_LEN);
+ }
+#endif /* CONFIG_FILS */
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 0b7477f31bc7..21f4b17815e9 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -25,7 +25,7 @@ struct wpa_sm_ctx {
void (*set_state)(void *ctx, enum wpa_states state);
enum wpa_states (*get_state)(void *ctx);
- void (*deauthenticate)(void * ctx, int reason_code);
+ void (*deauthenticate)(void * ctx, int reason_code);
int (*set_key)(void *ctx, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
@@ -38,8 +38,11 @@ struct wpa_sm_ctx {
void (*cancel_auth_timeout)(void *ctx);
u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
size_t *msg_len, void **data_pos);
- int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
- int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+ int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
+ const u8 *pmkid, const u8 *fils_cache_id,
+ const u8 *pmk, size_t pmk_len);
+ int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
+ const u8 *pmkid, const u8 *fils_cache_id);
void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
const struct wpa_config_blob * (*get_config_blob)(void *ctx,
const char *name);
@@ -77,6 +80,8 @@ struct wpa_sm_ctx {
const u8 *kck, size_t kck_len,
const u8 *replay_ctr);
int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
+ void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src,
+ const u8 *pkt, size_t pkt_len);
};
@@ -95,7 +100,6 @@ enum wpa_sm_conf_params {
struct rsn_supp_config {
void *network_ctx;
- int peerkey_enabled;
int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
int proactive_key_caching;
int eap_workaround;
@@ -105,6 +109,7 @@ struct rsn_supp_config {
int wpa_ptk_rekey;
int p2p;
int wpa_rsc_relaxation;
+ const u8 *fils_cache_id;
};
#ifndef CONFIG_NO_WPA
@@ -147,6 +152,15 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len);
int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
+struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm);
+struct rsn_pmksa_cache_entry *
+wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry * entry);
+void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+ const u8 *pmkid, const u8 *bssid,
+ const u8 *fils_cache_id);
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+ const void *network_ctx);
void wpa_sm_drop_sa(struct wpa_sm *sm);
int wpa_sm_has_ptk(struct wpa_sm *sm);
@@ -160,6 +174,7 @@ void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
const u8 *ptk_kck, size_t ptk_kck_len,
const u8 *ptk_kek, size_t ptk_kek_len);
+int wpa_fils_is_completed(struct wpa_sm *sm);
#else /* CONFIG_NO_WPA */
@@ -327,29 +342,20 @@ static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
{
}
-#endif /* CONFIG_NO_WPA */
-
-#ifdef CONFIG_PEERKEY
-int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
-int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
- const u8 *buf, size_t len);
-#else /* CONFIG_PEERKEY */
-static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
-{
- return -1;
-}
-
-static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
- const u8 *buf, size_t len)
+static inline int wpa_fils_is_completed(struct wpa_sm *sm)
{
return 0;
}
-#endif /* CONFIG_PEERKEY */
+
+#endif /* CONFIG_NO_WPA */
#ifdef CONFIG_IEEE80211R
int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len);
int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie);
+int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len,
+ const u8 *mdie);
+const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm);
int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
int ft_action, const u8 *target_ap,
const u8 *ric_ies, size_t ric_ies_len);
@@ -374,6 +380,12 @@ static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm,
return 0;
}
+static inline int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len,
+ const u8 *mdie)
+{
+ return 0;
+}
+
static inline int
wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
int ft_action, const u8 *target_ap)
@@ -425,5 +437,24 @@ extern unsigned int tdls_testing;
int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
+const u8 * wpa_sm_get_anonce(struct wpa_sm *sm);
+unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm);
+
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md);
+int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
+ size_t len);
+struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
+ size_t *kek_len, const u8 **snonce,
+ const u8 **anonce,
+ const struct wpabuf **hlp,
+ unsigned int num_hlp);
+int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len);
+
+struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group);
+int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *resp_ies, size_t resp_ies_len);
+
+void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
+void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index d45bb4585e50..b8d60e3208d0 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +10,7 @@
#include "common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/sha384.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
@@ -23,6 +24,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
{
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *anonce = key->key_nonce;
+ int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
if (sm->xxkey_len == 0) {
wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -30,21 +32,26 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
return -1;
}
- wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
- sm->ssid_len, sm->mobility_domain,
- sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
- sm->pmk_r0, sm->pmk_r0_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+ sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+ if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
+ sm->ssid_len, sm->mobility_domain,
+ sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+ sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, sm->pmk_r0_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
sm->pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
- sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ sm->pmk_r1_len = sm->pmk_r0_len;
+ if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
+ sm->r1kh_id, sm->own_addr, sm->pmk_r1,
+ sm->pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
WPA_PMK_NAME_LEN);
- return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
- sm->bssid, sm->pmk_r1_name, ptk, ptk_name,
- sm->key_mgmt, sm->pairwise_cipher);
+ return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
+ sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
+ ptk_name, sm->key_mgmt, sm->pairwise_cipher);
}
@@ -58,11 +65,13 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
{
struct wpa_ft_ies ft;
+ int use_sha384;
if (sm == NULL)
return 0;
- if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0)
+ use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+ if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0)
return -1;
if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
@@ -146,16 +155,17 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
const u8 *ap_mdie)
{
size_t buf_len;
- u8 *buf, *pos, *ftie_len, *ftie_pos;
+ u8 *buf, *pos, *ftie_len, *ftie_pos, *fte_mic, *elem_count;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
struct rsn_ie_hdr *rsnie;
u16 capab;
+ int mdie_len;
sm->ft_completed = 0;
sm->ft_reassoc_completed = 0;
- buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+ buf_len = 2 + sizeof(struct rsn_mdie) + 2 +
+ sizeof(struct rsn_ftie_sha384) +
2 + sm->r0kh_id_len + ric_ies_len + 100;
buf = os_zalloc(buf_len);
if (buf == NULL)
@@ -201,10 +211,20 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
/* Authenticated Key Management Suite List */
if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+#ifdef CONFIG_SHA384
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+#endif /* CONFIG_SHA384 */
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+#ifdef CONFIG_FILS
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+#endif /* CONFIG_FILS */
else {
wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
sm->key_mgmt);
@@ -216,7 +236,10 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
/* RSN Capabilities */
capab = 0;
#ifdef CONFIG_IEEE80211W
- if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ||
+ sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 ||
+ sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256 ||
+ sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256)
capab |= WPA_CAPABILITY_MFPC;
#endif /* CONFIG_IEEE80211W */
WPA_PUT_LE16(pos, capab);
@@ -231,34 +254,63 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
pos += WPA_PMK_NAME_LEN;
#ifdef CONFIG_IEEE80211W
- if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
- /* Management Group Cipher Suite */
+ /* Management Group Cipher Suite */
+ switch (sm->mgmt_group_cipher) {
+ case WPA_CIPHER_AES_128_CMAC:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
pos += RSN_SELECTOR_LEN;
+ break;
+ case WPA_CIPHER_BIP_GMAC_128:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
+ pos += RSN_SELECTOR_LEN;
+ break;
+ case WPA_CIPHER_BIP_GMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
+ pos += RSN_SELECTOR_LEN;
+ break;
+ case WPA_CIPHER_BIP_CMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
+ pos += RSN_SELECTOR_LEN;
+ break;
}
#endif /* CONFIG_IEEE80211W */
rsnie->len = (pos - (u8 *) rsnie) - 2;
/* MDIE */
- *pos++ = WLAN_EID_MOBILITY_DOMAIN;
- *pos++ = sizeof(*mdie);
- mdie = (struct rsn_mdie *) pos;
- pos += sizeof(*mdie);
- os_memcpy(mdie->mobility_domain, sm->mobility_domain,
- MOBILITY_DOMAIN_ID_LEN);
- mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
- sm->mdie_ft_capab;
+ mdie_len = wpa_ft_add_mdie(sm, pos, buf_len - (pos - buf), ap_mdie);
+ if (mdie_len <= 0) {
+ os_free(buf);
+ return NULL;
+ }
+ mdie = (struct rsn_mdie *) (pos + 2);
+ pos += mdie_len;
/* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
ftie_pos = pos;
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ftie_len = pos++;
- ftie = (struct rsn_ftie *) pos;
- pos += sizeof(*ftie);
- os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
- if (anonce)
- os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) pos;
+ fte_mic = ftie->mic;
+ elem_count = &ftie->mic_control[1];
+ pos += sizeof(*ftie);
+ os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+ if (anonce)
+ os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) pos;
+ fte_mic = ftie->mic;
+ elem_count = &ftie->mic_control[1];
+ pos += sizeof(*ftie);
+ os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+ if (anonce)
+ os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ }
if (kck) {
/* R1KH-ID sub-element in third FT message */
*pos++ = FTIE_SUBELEM_R1KH_ID;
@@ -292,13 +344,12 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
* RIC-Request (if present)
*/
/* Information element count */
- ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
- ric_ies_len);
+ *elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len);
if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
((u8 *) mdie) - 2, 2 + sizeof(*mdie),
ftie_pos, 2 + *ftie_len,
(u8 *) rsnie, 2 + rsnie->len, ric_ies,
- ric_ies_len, ftie->mic) < 0) {
+ ric_ies_len, fte_mic) < 0) {
wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
os_free(buf);
return NULL;
@@ -367,6 +418,37 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
}
+int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *buf, size_t buf_len,
+ const u8 *ap_mdie)
+{
+ u8 *pos = buf;
+ struct rsn_mdie *mdie;
+
+ if (buf_len < 2 + sizeof(*mdie)) {
+ wpa_printf(MSG_INFO,
+ "FT: Failed to add MDIE: short buffer, length=%zu",
+ buf_len);
+ return 0;
+ }
+
+ *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+ *pos++ = sizeof(*mdie);
+ mdie = (struct rsn_mdie *) pos;
+ os_memcpy(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+ mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
+ sm->mdie_ft_capab;
+
+ return 2 + sizeof(*mdie);
+}
+
+
+const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm)
+{
+ return sm->mobility_domain;
+}
+
+
int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
int ft_action, const u8 *target_ap,
const u8 *ric_ies, size_t ric_ies_len)
@@ -375,10 +457,13 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
size_t ft_ies_len;
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
u8 ptk_name[WPA_PMK_NAME_LEN];
int ret;
const u8 *bssid;
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+ const u8 *anonce, *snonce;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
@@ -404,7 +489,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
return -1;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
@@ -417,16 +502,34 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
return -1;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return -1;
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
}
- if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- ftie->snonce, WPA_NONCE_LEN);
+ snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
return -1;
@@ -465,23 +568,34 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN);
- os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN);
- wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
- sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
+ os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN);
+ if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
+ sm->r1kh_id, sm->own_addr, sm->pmk_r1,
+ sm->pmk_r1_name) < 0)
+ return -1;
+ sm->pmk_r1_len = sm->pmk_r0_len;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
bssid = target_ap;
- if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce,
- sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk,
- ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0)
+ if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
+ anonce, sm->own_addr, bssid,
+ sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
+ sm->pairwise_cipher) < 0)
return -1;
- ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
+ if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+ kck = sm->ptk.kck2;
+ kck_len = sm->ptk.kck2_len;
+ } else {
+ kck = sm->ptk.kck;
+ kck_len = sm->ptk.kck_len;
+ }
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce,
sm->pmk_r1_name,
- sm->ptk.kck, sm->ptk.kck_len, bssid,
+ kck, kck_len, bssid,
ric_ies, ric_ies_len,
parse.mdie ? parse.mdie - 2 : NULL);
if (ft_ies) {
@@ -544,6 +658,16 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
int keyidx;
enum wpa_alg alg;
size_t gtk_len, keylen, rsc_len;
+ const u8 *kek;
+ size_t kek_len;
+
+ if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+ kek = sm->ptk.kek2;
+ kek_len = sm->ptk.kek2_len;
+ } else {
+ kek = sm->ptk.kek;
+ kek_len = sm->ptk.kek_len;
+ }
if (gtk_elem == NULL) {
wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
@@ -560,8 +684,7 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
return -1;
}
gtk_len = gtk_elem_len - 19;
- if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11,
- gtk)) {
+ if (aes_unwrap(kek, kek_len, gtk_len / 8, gtk_elem + 11, gtk)) {
wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
"decrypt GTK");
return -1;
@@ -615,10 +738,24 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
size_t igtk_elem_len)
{
- u8 igtk[WPA_IGTK_LEN];
+ u8 igtk[WPA_IGTK_MAX_LEN];
+ size_t igtk_len;
u16 keyidx;
+ const u8 *kek;
+ size_t kek_len;
- if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+ if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+ kek = sm->ptk.kek2;
+ kek_len = sm->ptk.kek2_len;
+ } else {
+ kek = sm->ptk.kek;
+ kek_len = sm->ptk.kek_len;
+ }
+
+ if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
+ sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
+ sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
+ sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256)
return 0;
if (igtk_elem == NULL) {
@@ -629,19 +766,19 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
igtk_elem, igtk_elem_len);
- if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) {
+ igtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ if (igtk_elem_len != 2 + 6 + 1 + igtk_len + 8) {
wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem "
"length %lu", (unsigned long) igtk_elem_len);
return -1;
}
- if (igtk_elem[8] != WPA_IGTK_LEN) {
+ if (igtk_elem[8] != igtk_len) {
wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length "
"%d", igtk_elem[8]);
return -1;
}
- if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8,
- igtk_elem + 9, igtk)) {
+ if (aes_unwrap(kek, kek_len, igtk_len / 8, igtk_elem + 9, igtk)) {
wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
"decrypt IGTK");
return -1;
@@ -652,13 +789,16 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
keyidx = WPA_GET_LE16(igtk_elem);
wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
- WPA_IGTK_LEN);
- if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0,
- igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) {
+ igtk_len);
+ if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr, keyidx, 0,
+ igtk_elem + 2, 6, igtk, igtk_len) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
"driver.");
+ os_memset(igtk, 0, sizeof(igtk));
return -1;
}
+ os_memset(igtk, 0, sizeof(igtk));
return 0;
}
@@ -670,9 +810,13 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
{
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
unsigned int count;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+ const u8 *anonce, *snonce, *fte_mic;
+ u8 fte_elem_count;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
@@ -687,7 +831,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
return 0;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
@@ -700,25 +844,47 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
return -1;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return -1;
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
}
- if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- ftie->snonce, WPA_NONCE_LEN);
+ snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
return -1;
}
- if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- ftie->anonce, WPA_NONCE_LEN);
+ anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->anonce, WPA_NONCE_LEN);
return -1;
@@ -763,14 +929,22 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
count = 3;
if (parse.ric)
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
- if (ftie->mic_control[1] != count) {
+ if (fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- ftie->mic_control[1], count);
+ fte_elem_count, count);
return -1;
}
- if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6,
+ if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+ kck = sm->ptk.kck2;
+ kck_len = sm->ptk.kck2_len;
+ } else {
+ kck = sm->ptk.kck;
+ kck_len = sm->ptk.kck_len;
+ }
+
+ if (wpa_ft_mic(kck, kck_len, sm->own_addr, src_addr, 6,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
@@ -780,9 +954,9 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
return -1;
}
- if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
+ if (os_memcmp_const(mic, fte_mic, 16) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
- wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
return -1;
}
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 56f88dcdd899..b94b17a85a3a 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -1,6 +1,6 @@
/*
* Internal WPA/RSN supplicant state machine definitions
- * Copyright (c) 2004-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.
@@ -11,7 +11,6 @@
#include "utils/list.h"
-struct wpa_peerkey;
struct wpa_tdls_peer;
struct wpa_eapol_key;
@@ -57,7 +56,6 @@ struct wpa_sm {
int fast_reauth; /* whether EAP fast re-authentication is enabled */
void *network_ctx;
- int peerkey_enabled;
int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
int proactive_key_caching;
int eap_workaround;
@@ -94,9 +92,6 @@ struct wpa_sm {
u8 *ap_wpa_ie, *ap_rsn_ie;
size_t ap_wpa_ie_len, ap_rsn_ie_len;
-#ifdef CONFIG_PEERKEY
- struct wpa_peerkey *peerkey;
-#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_TDLS
struct wpa_tdls_peer *tdls;
int tdls_prohibited;
@@ -117,11 +112,14 @@ struct wpa_sm {
#endif /* CONFIG_TDLS */
#ifdef CONFIG_IEEE80211R
- u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+ u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
+ * first 384 bits of MSK */
size_t xxkey_len;
- u8 pmk_r0[PMK_LEN];
+ u8 pmk_r0[PMK_LEN_MAX];
+ size_t pmk_r0_len;
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 pmk_r1[PMK_LEN];
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
@@ -144,6 +142,31 @@ struct wpa_sm {
#ifdef CONFIG_TESTING_OPTIONS
struct wpabuf *test_assoc_ie;
#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_FILS
+ u8 fils_nonce[FILS_NONCE_LEN];
+ u8 fils_session[FILS_SESSION_LEN];
+ u8 fils_anonce[FILS_NONCE_LEN];
+ u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+ u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+ size_t fils_key_auth_len;
+ unsigned int fils_completed:1;
+ unsigned int fils_erp_pmkid_set:1;
+ unsigned int fils_cache_id_set:1;
+ u8 fils_erp_pmkid[PMKID_LEN];
+ u8 fils_cache_id[FILS_CACHE_ID_LEN];
+ struct crypto_ecdh *fils_ecdh;
+ int fils_dh_group;
+ size_t fils_dh_elem_len;
+ struct wpabuf *fils_ft_ies;
+ u8 fils_ft[FILS_FT_MAX_LEN];
+ size_t fils_ft_len;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ struct crypto_ecdh *owe_ecdh;
+ u16 owe_group;
+#endif /* CONFIG_OWE */
};
@@ -215,18 +238,23 @@ static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type,
msg_len, data_pos);
}
-static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid,
- const u8 *pmkid)
+static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, void *network_ctx,
+ const u8 *bssid, const u8 *pmkid,
+ const u8 *cache_id, const u8 *pmk,
+ size_t pmk_len)
{
WPA_ASSERT(sm->ctx->add_pmkid);
- return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid);
+ return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
+ cache_id, pmk, pmk_len);
}
-static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid,
- const u8 *pmkid)
+static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx,
+ const u8 *bssid, const u8 *pmkid,
+ const u8 *cache_id)
{
WPA_ASSERT(sm->ctx->remove_pmkid);
- return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid);
+ return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
+ cache_id);
}
static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
@@ -359,7 +387,16 @@ static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
}
-int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+static inline void wpa_sm_fils_hlp_rx(struct wpa_sm *sm,
+ const u8 *dst, const u8 *src,
+ const u8 *pkt, size_t pkt_len)
+{
+ if (sm->ctx->fils_hlp_rx)
+ sm->ctx->fils_hlp_rx(sm->ctx->ctx, dst, src, pkt, pkt_len);
+}
+
+
+int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic);
int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index c44844ec583b..a3410d15447a 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -1,6 +1,6 @@
/*
* wpa_supplicant - WPA/RSN IE and KDE processing
- * Copyright (c) 2003-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.
@@ -161,6 +161,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
#ifdef CONFIG_IEEE80211R
} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+#ifdef CONFIG_SHA384
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+#endif /* CONFIG_SHA384 */
} else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
#endif /* CONFIG_IEEE80211R */
@@ -180,6 +184,30 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+#ifdef CONFIG_FILS
+ } else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+ } else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+#ifdef CONFIG_IEEE80211R
+ } else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+ } else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ } else if (key_mgmt & WPA_KEY_MGMT_OWE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ } else if (key_mgmt & WPA_KEY_MGMT_DPP) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ } else if (key_mgmt & WPA_KEY_MGMT_OSEN) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+#endif /* CONFIG_HS20 */
} else {
wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
key_mgmt);
@@ -405,44 +433,6 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
return 0;
}
-#ifdef CONFIG_PEERKEY
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
- ie->smk = pos + 2 + RSN_SELECTOR_LEN;
- ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
- wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
- pos, pos[1] + 2);
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
- ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
- ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
- wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
- pos, pos[1] + 2);
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
- ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
- ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
- wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
- pos, pos[1] + 2);
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
- ie->error = pos + 2 + RSN_SELECTOR_LEN;
- ie->error_len = pos[1] - RSN_SELECTOR_LEN;
- wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
- pos, pos[1] + 2);
- return 0;
- }
-#endif /* CONFIG_PEERKEY */
-
#ifdef CONFIG_IEEE80211W
if (pos[1] > RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index fe95af0abc51..0e72af56029e 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -21,16 +21,6 @@ struct wpa_eapol_ie_parse {
size_t gtk_len;
const u8 *mac_addr;
size_t mac_addr_len;
-#ifdef CONFIG_PEERKEY
- const u8 *smk;
- size_t smk_len;
- const u8 *nonce;
- size_t nonce_len;
- const u8 *lifetime;
- size_t lifetime_len;
- const u8 *error;
- size_t error_len;
-#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_IEEE80211W
const u8 *igtk;
size_t igtk_len;
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
index 8bc824f20dcd..4f7a14823d72 100644
--- a/src/tls/libtommath.c
+++ b/src/tls/libtommath.c
@@ -116,7 +116,7 @@ typedef int mp_err;
#define MP_PREC 32 /* default digits of precision */
#else
#define MP_PREC 8 /* default digits of precision */
- #endif
+ #endif
#endif
/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
@@ -274,8 +274,8 @@ static int s_mp_add (mp_int * a, mp_int * b, mp_int * c)
*tmpc++ &= MP_MASK;
}
- /* now copy higher words if any, that is in A+B
- * if A or B has more digits add those in
+ /* now copy higher words if any, that is in A+B
+ * if A or B has more digits add those in
*/
if (min != max) {
for (; i < max; i++) {
@@ -499,29 +499,29 @@ static int mp_mul (mp_int * a, mp_int * b, mp_int * c)
#ifdef BN_MP_TOOM_MUL_C
if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) {
res = mp_toom_mul(a, b, c);
- } else
+ } else
#endif
#ifdef BN_MP_KARATSUBA_MUL_C
/* use Karatsuba? */
if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
res = mp_karatsuba_mul (a, b, c);
- } else
+ } else
#endif
{
/* can we use the fast multiplier?
*
- * The fast multiplier can be used if the output will
- * have less than MP_WARRAY digits and the number of
+ * The fast multiplier can be used if the output will
+ * have less than MP_WARRAY digits and the number of
* digits won't affect carry propagation
*/
#ifdef BN_FAST_S_MP_MUL_DIGS_C
int digs = a->used + b->used + 1;
if ((digs < MP_WARRAY) &&
- MIN(a->used, b->used) <=
+ MIN(a->used, b->used) <=
(1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
res = fast_s_mp_mul_digs (a, b, c, digs);
- } else
+ } else
#endif
#ifdef BN_S_MP_MUL_DIGS_C
res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */
@@ -629,7 +629,7 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
err = mp_exptmod(&tmpG, &tmpX, P, Y);
mp_clear_multi(&tmpG, &tmpX, NULL);
return err;
-#else
+#else
#error mp_exptmod would always fail
/* no invmod */
return MP_VAL;
@@ -658,7 +658,7 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
dr = mp_reduce_is_2k(P) << 1;
}
#endif
-
+
/* if the modulus is odd or dr != 0 use the montgomery method */
#ifdef BN_MP_EXPTMOD_FAST_C
if (mp_isodd (P) == 1 || dr != 0) {
@@ -693,7 +693,7 @@ static int mp_cmp (mp_int * a, mp_int * b)
return MP_GT;
}
}
-
+
/* compare digits */
if (a->sign == MP_NEG) {
/* if negative compare opposite direction */
@@ -779,7 +779,7 @@ static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
}
/* init temps */
- if ((res = mp_init_multi(&x, &y, &u, &v,
+ if ((res = mp_init_multi(&x, &y, &u, &v,
&A, &B, &C, &D, NULL)) != MP_OKAY) {
return res;
}
@@ -906,14 +906,14 @@ top:
goto LBL_ERR;
}
}
-
+
/* too big */
while (mp_cmp_mag(&C, b) != MP_LT) {
if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
goto LBL_ERR;
}
}
-
+
/* C is now the inverse */
mp_exch (&C, c);
res = MP_OKAY;
@@ -933,7 +933,7 @@ static int mp_cmp_mag (mp_int * a, mp_int * b)
if (a->used > b->used) {
return MP_GT;
}
-
+
if (a->used < b->used) {
return MP_LT;
}
@@ -1199,8 +1199,8 @@ static void mp_rshd (mp_int * a, int b)
/* top [offset into digits] */
top = a->dp + b;
- /* this is implemented as a sliding window where
- * the window is b-digits long and digits from
+ /* this is implemented as a sliding window where
+ * the window is b-digits long and digits from
* the top of the window are copied to the bottom
*
* e.g.
@@ -1218,13 +1218,13 @@ static void mp_rshd (mp_int * a, int b)
*bottom++ = 0;
}
}
-
+
/* remove excess digits */
a->used -= b;
}
-/* swap the elements of two integers, for cases where you can't simply swap the
+/* swap the elements of two integers, for cases where you can't simply swap the
* mp_int pointers around
*/
static void mp_exch (mp_int * a, mp_int * b)
@@ -1237,7 +1237,7 @@ static void mp_exch (mp_int * a, mp_int * b)
}
-/* trim unused digits
+/* trim unused digits
*
* This is used to ensure that leading zero digits are
* trimed and the leading "used" digit will be non-zero
@@ -1298,7 +1298,7 @@ static int mp_grow (mp_int * a, int size)
#ifdef BN_MP_ABS_C
-/* b = |a|
+/* b = |a|
*
* Simple function copies the input and fixes the sign to positive
*/
@@ -1434,7 +1434,7 @@ static int mp_mul_2d (mp_int * a, int b, mp_int * c)
/* set the carry to the carry bits of the current word */
r = rr;
}
-
+
/* set final carry */
if (r != 0) {
c->dp[(c->used)++] = r;
@@ -1446,7 +1446,7 @@ static int mp_mul_2d (mp_int * a, int b, mp_int * c)
#ifdef BN_MP_INIT_MULTI_C
-static int mp_init_multi(mp_int *mp, ...)
+static int mp_init_multi(mp_int *mp, ...)
{
mp_err res = MP_OKAY; /* Assume ok until proven otherwise */
int n = 0; /* Number of ok inits */
@@ -1460,11 +1460,11 @@ static int mp_init_multi(mp_int *mp, ...)
succeeded in init-ing, then return error.
*/
va_list clean_args;
-
+
/* end the current list */
va_end(args);
-
- /* now start cleaning up */
+
+ /* now start cleaning up */
cur_arg = mp;
va_start(clean_args, mp);
while (n--) {
@@ -1484,7 +1484,7 @@ static int mp_init_multi(mp_int *mp, ...)
#ifdef BN_MP_CLEAR_MULTI_C
-static void mp_clear_multi(mp_int *mp, ...)
+static void mp_clear_multi(mp_int *mp, ...)
{
mp_int* next_mp = mp;
va_list args;
@@ -1558,7 +1558,7 @@ static int mp_count_bits (mp_int * a)
/* get number of digits and add that */
r = (a->used - 1) * DIGIT_BIT;
-
+
/* take the last digit and count the bits in it */
q = a->dp[a->used - 1];
while (q > ((mp_digit) 0)) {
@@ -1628,7 +1628,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
}
return res;
}
-
+
/* init our temps */
if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) {
return res;
@@ -1638,7 +1638,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
mp_set(&tq, 1);
n = mp_count_bits(a) - mp_count_bits(b);
if (((res = mp_abs(a, &ta)) != MP_OKAY) ||
- ((res = mp_abs(b, &tb)) != MP_OKAY) ||
+ ((res = mp_abs(b, &tb)) != MP_OKAY) ||
((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) ||
((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) {
goto LBL_ERR;
@@ -1675,17 +1675,17 @@ LBL_ERR:
#else
-/* integer signed division.
+/* integer signed division.
* c*b + d == a [e.g. a/b, c=quotient, d=remainder]
* HAC pp.598 Algorithm 14.20
*
- * Note that the description in HAC is horribly
- * incomplete. For example, it doesn't consider
- * the case where digits are removed from 'x' in
- * the inner loop. It also doesn't consider the
+ * Note that the description in HAC is horribly
+ * incomplete. For example, it doesn't consider
+ * the case where digits are removed from 'x' in
+ * the inner loop. It also doesn't consider the
* case that y has fewer than three digits, etc..
*
- * The overall algorithm is as described as
+ * The overall algorithm is as described as
* 14.20 from HAC but fixed to treat these cases.
*/
static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
@@ -1775,7 +1775,7 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
continue;
}
- /* step 3.1 if xi == yt then set q{i-t-1} to b-1,
+ /* step 3.1 if xi == yt then set q{i-t-1} to b-1,
* otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
if (x.dp[i] == y.dp[t]) {
q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
@@ -1789,10 +1789,10 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK));
}
- /* while (q{i-t-1} * (yt * b + y{t-1})) >
- xi * b**2 + xi-1 * b + xi-2
-
- do q{i-t-1} -= 1;
+ /* while (q{i-t-1} * (yt * b + y{t-1})) >
+ xi * b**2 + xi-1 * b + xi-2
+
+ do q{i-t-1} -= 1;
*/
q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK;
do {
@@ -1843,10 +1843,10 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
}
}
- /* now q is the quotient and x is the remainder
- * [which we have to normalize]
+ /* now q is the quotient and x is the remainder
+ * [which we have to normalize]
*/
-
+
/* get sign before writing to c */
x.sign = x.used == 0 ? MP_ZPOS : a->sign;
@@ -1914,7 +1914,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red
/* init M array */
/* init first cell */
if ((err = mp_init(&M[1])) != MP_OKAY) {
- return err;
+ return err;
}
/* now init the second half of the array */
@@ -1932,7 +1932,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red
if ((err = mp_init (&mu)) != MP_OKAY) {
goto LBL_M;
}
-
+
if (redmode == 0) {
if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) {
goto LBL_MU;
@@ -1943,22 +1943,22 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red
goto LBL_MU;
}
redux = mp_reduce_2k_l;
- }
+ }
/* create M table
*
- * The M table contains powers of the base,
+ * The M table contains powers of the base,
* e.g. M[x] = G**x mod P
*
- * The first half of the table is not
+ * The first half of the table is not
* computed though accept for M[0] and M[1]
*/
if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) {
goto LBL_MU;
}
- /* compute the value at M[1<<(winsize-1)] by squaring
- * M[1] (winsize-1) times
+ /* compute the value at M[1<<(winsize-1)] by squaring
+ * M[1] (winsize-1) times
*/
if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
goto LBL_MU;
@@ -1966,7 +1966,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red
for (x = 0; x < (winsize - 1); x++) {
/* square it */
- if ((err = mp_sqr (&M[1 << (winsize - 1)],
+ if ((err = mp_sqr (&M[1 << (winsize - 1)],
&M[1 << (winsize - 1)])) != MP_OKAY) {
goto LBL_MU;
}
@@ -2117,18 +2117,18 @@ static int mp_sqr (mp_int * a, mp_int * b)
if (a->used >= TOOM_SQR_CUTOFF) {
res = mp_toom_sqr(a, b);
/* Karatsuba? */
- } else
+ } else
#endif
#ifdef BN_MP_KARATSUBA_SQR_C
if (a->used >= KARATSUBA_SQR_CUTOFF) {
res = mp_karatsuba_sqr (a, b);
- } else
+ } else
#endif
{
#ifdef BN_FAST_S_MP_SQR_C
/* can we use the fast comba multiplier? */
- if ((a->used * 2 + 1) < MP_WARRAY &&
- a->used <
+ if ((a->used * 2 + 1) < MP_WARRAY &&
+ a->used <
(1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) {
res = fast_s_mp_sqr (a, b);
} else
@@ -2145,7 +2145,7 @@ if (a->used >= KARATSUBA_SQR_CUTOFF) {
}
-/* reduces a modulo n where n is of the form 2**p - d
+/* reduces a modulo n where n is of the form 2**p - d
This differs from reduce_2k since "d" can be larger
than a single digit.
*/
@@ -2153,33 +2153,33 @@ static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
{
mp_int q;
int p, res;
-
+
if ((res = mp_init(&q)) != MP_OKAY) {
return res;
}
-
- p = mp_count_bits(n);
+
+ p = mp_count_bits(n);
top:
/* q = a/2**p, a = a mod 2**p */
if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
goto ERR;
}
-
+
/* q = q * d */
- if ((res = mp_mul(&q, d, &q)) != MP_OKAY) {
+ if ((res = mp_mul(&q, d, &q)) != MP_OKAY) {
goto ERR;
}
-
+
/* a = a + q */
if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
goto ERR;
}
-
+
if (mp_cmp_mag(a, n) != MP_LT) {
s_mp_sub(a, n, a);
goto top;
}
-
+
ERR:
mp_clear(&q);
return res;
@@ -2191,26 +2191,26 @@ static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
{
int res;
mp_int tmp;
-
+
if ((res = mp_init(&tmp)) != MP_OKAY) {
return res;
}
-
+
if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
goto ERR;
}
-
+
if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
goto ERR;
}
-
+
ERR:
mp_clear(&tmp);
return res;
}
-/* computes a = 2**b
+/* computes a = 2**b
*
* Simple algorithm which zeroes the int, grows it then just sets one bit
* as required.
@@ -2243,7 +2243,7 @@ static int mp_2expt (mp_int * a, int b)
static int mp_reduce_setup (mp_int * a, mp_int * b)
{
int res;
-
+
if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
return res;
}
@@ -2251,7 +2251,7 @@ static int mp_reduce_setup (mp_int * a, mp_int * b)
}
-/* reduces x mod m, assumes 0 < x < m**2, mu is
+/* reduces x mod m, assumes 0 < x < m**2, mu is
* precomputed via mp_reduce_setup.
* From HAC pp.604 Algorithm 14.42
*/
@@ -2266,7 +2266,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
}
/* q1 = x / b**(k-1) */
- mp_rshd (&q, um - 1);
+ mp_rshd (&q, um - 1);
/* according to HAC this optimization is ok */
if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
@@ -2282,8 +2282,8 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
goto CLEANUP;
}
-#else
- {
+#else
+ {
#error mp_reduce would always fail
res = MP_VAL;
goto CLEANUP;
@@ -2292,7 +2292,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
}
/* q3 = q2 / b**(k+1) */
- mp_rshd (&q, um + 1);
+ mp_rshd (&q, um + 1);
/* x = x mod b**(k+1), quick (no division) */
if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
@@ -2326,7 +2326,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
goto CLEANUP;
}
}
-
+
CLEANUP:
mp_clear (&q);
@@ -2335,7 +2335,7 @@ CLEANUP:
/* multiplies |a| * |b| and only computes up to digs digits of result
- * HAC pp. 595, Algorithm 14.12 Modified so you can control how
+ * HAC pp. 595, Algorithm 14.12 Modified so you can control how
* many digits of output are created.
*/
static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
@@ -2349,7 +2349,7 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
#ifdef BN_FAST_S_MP_MUL_DIGS_C
/* can we use the fast multiplier? */
if (((digs) < MP_WARRAY) &&
- MIN (a->used, b->used) <
+ MIN (a->used, b->used) <
(1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
return fast_s_mp_mul_digs (a, b, c, digs);
}
@@ -2372,10 +2372,10 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
/* setup some aliases */
/* copy of the digit from a used within the nested loop */
tmpx = a->dp[ix];
-
+
/* an alias for the destination shifted ix places */
tmpt = t.dp + ix;
-
+
/* an alias for the digits of b */
tmpy = b->dp;
@@ -2409,15 +2409,15 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
#ifdef BN_FAST_S_MP_MUL_DIGS_C
/* Fast (comba) multiplier
*
- * This is the fast column-array [comba] multiplier. It is
- * designed to compute the columns of the product first
- * then handle the carries afterwards. This has the effect
+ * This is the fast column-array [comba] multiplier. It is
+ * designed to compute the columns of the product first
+ * then handle the carries afterwards. This has the effect
* of making the nested loops that compute the columns very
* simple and schedulable on super-scalar processors.
*
- * This has been modified to produce a variable number of
- * digits of output so if say only a half-product is required
- * you don't have to compute the upper half (a feature
+ * This has been modified to produce a variable number of
+ * digits of output so if say only a half-product is required
+ * you don't have to compute the upper half (a feature
* required for fast Barrett reduction).
*
* Based on Algorithm 14.12 on pp.595 of HAC.
@@ -2441,7 +2441,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
/* clear the carry */
_W = 0;
- for (ix = 0; ix < pa; ix++) {
+ for (ix = 0; ix < pa; ix++) {
int tx, ty;
int iy;
mp_digit *tmpx, *tmpy;
@@ -2454,7 +2454,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
tmpx = a->dp + tx;
tmpy = b->dp + ty;
- /* this is the number of times the loop will iterrate, essentially
+ /* this is the number of times the loop will iterrate, essentially
while (tx++ < a->used && ty-- >= 0) { ... }
*/
iy = MIN(a->used-tx, ty+1);
@@ -2501,8 +2501,8 @@ static int mp_init_size (mp_int * a, int size)
int x;
/* pad size so there are always extra digits */
- size += (MP_PREC * 2) - (size % MP_PREC);
-
+ size += (MP_PREC * 2) - (size % MP_PREC);
+
/* alloc mem */
a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size);
if (a->dp == NULL) {
@@ -2556,7 +2556,7 @@ static int s_mp_sqr (mp_int * a, mp_int * b)
/* alias for where to store the results */
tmpt = t.dp + (2*ix + 1);
-
+
for (iy = ix + 1; iy < pa; iy++) {
/* first calculate the product */
r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
@@ -2863,24 +2863,24 @@ static int mp_mul_2(mp_int * a, mp_int * b)
/* alias for source */
tmpa = a->dp;
-
+
/* alias for dest */
tmpb = b->dp;
/* carry */
r = 0;
for (x = 0; x < a->used; x++) {
-
- /* get what will be the *next* carry bit from the
- * MSB of the current digit
+
+ /* get what will be the *next* carry bit from the
+ * MSB of the current digit
*/
rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
-
+
/* now shift up this digit, add in the carry [from the previous] */
*tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
-
- /* copy the carry that would be from the source
- * digit into the next iteration
+
+ /* copy the carry that would be from the source
+ * digit into the next iteration
*/
r = rr;
}
@@ -2892,8 +2892,8 @@ static int mp_mul_2(mp_int * a, mp_int * b)
++(b->used);
}
- /* now zero any excess digits on the destination
- * that we didn't write to
+ /* now zero any excess digits on the destination
+ * that we didn't write to
*/
tmpb = b->dp + b->used;
for (x = b->used; x < oldused; x++) {
@@ -3011,7 +3011,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int
/* determine and setup reduction code */
if (redmode == 0) {
-#ifdef BN_MP_MONTGOMERY_SETUP_C
+#ifdef BN_MP_MONTGOMERY_SETUP_C
/* now setup montgomery */
if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) {
goto LBL_M;
@@ -3026,7 +3026,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int
if (((P->used * 2 + 1) < MP_WARRAY) &&
P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
redux = fast_mp_montgomery_reduce;
- } else
+ } else
#endif
{
#ifdef BN_MP_MONTGOMERY_REDUCE_C
@@ -3077,7 +3077,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int
if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) {
goto LBL_RES;
}
-#else
+#else
err = MP_VAL;
goto LBL_RES;
#endif
@@ -3245,10 +3245,10 @@ LBL_M:
#ifdef BN_FAST_S_MP_SQR_C
/* the jist of squaring...
- * you do like mult except the offset of the tmpx [one that
- * starts closer to zero] can't equal the offset of tmpy.
+ * you do like mult except the offset of the tmpx [one that
+ * starts closer to zero] can't equal the offset of tmpy.
* So basically you set up iy like before then you min it with
- * (ty-tx) so that it never happens. You double all those
+ * (ty-tx) so that it never happens. You double all those
* you add in the inner loop
After that loop you do the squares and add them in.
@@ -3270,7 +3270,7 @@ static int fast_s_mp_sqr (mp_int * a, mp_int * b)
/* number of output digits to produce */
W1 = 0;
- for (ix = 0; ix < pa; ix++) {
+ for (ix = 0; ix < pa; ix++) {
int tx, ty, iy;
mp_word _W;
mp_digit *tmpy;
@@ -3291,7 +3291,7 @@ static int fast_s_mp_sqr (mp_int * a, mp_int * b)
*/
iy = MIN(a->used-tx, ty+1);
- /* now for squaring tx can never equal ty
+ /* now for squaring tx can never equal ty
* we halve the distance since they approach at a rate of 2x
* and we have to round because odd cases need to be executed
*/
diff --git a/src/tls/rsa.c b/src/tls/rsa.c
index 0b7b530bc37c..3525eb9919db 100644
--- a/src/tls/rsa.c
+++ b/src/tls/rsa.c
@@ -80,7 +80,7 @@ crypto_rsa_import_public_key(const u8 *buf, size_t len)
* PKCS #1, 7.1:
* RSAPublicKey ::= SEQUENCE {
* modulus INTEGER, -- n
- * publicExponent INTEGER -- e
+ * publicExponent INTEGER -- e
* }
*/
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 9bc0d211f48d..76e19746b020 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -264,7 +264,7 @@ failed:
* @in_data: Pointer to plaintext data to be encrypted
* @in_len: Input buffer length
* @out_data: Pointer to output buffer (encrypted TLS data)
- * @out_len: Maximum out_data length
+ * @out_len: Maximum out_data length
* Returns: Number of bytes written to out_data, -1 on failure
*
* This function is used after TLS handshake has been completed successfully to
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 244c3cb06082..e66f1a98896d 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -685,10 +685,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
pos, conn->dh_p_len);
goto fail;
}
- conn->dh_p = os_malloc(conn->dh_p_len);
+ conn->dh_p = os_memdup(pos, conn->dh_p_len);
if (conn->dh_p == NULL)
goto fail;
- os_memcpy(conn->dh_p, pos, conn->dh_p_len);
pos += conn->dh_p_len;
wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
conn->dh_p, conn->dh_p_len);
@@ -700,10 +699,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
if (val == 0 || val > (size_t) (end - pos))
goto fail;
conn->dh_g_len = val;
- conn->dh_g = os_malloc(conn->dh_g_len);
+ conn->dh_g = os_memdup(pos, conn->dh_g_len);
if (conn->dh_g == NULL)
goto fail;
- os_memcpy(conn->dh_g, pos, conn->dh_g_len);
pos += conn->dh_g_len;
wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
conn->dh_g, conn->dh_g_len);
@@ -717,10 +715,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
if (val == 0 || val > (size_t) (end - pos))
goto fail;
conn->dh_ys_len = val;
- conn->dh_ys = os_malloc(conn->dh_ys_len);
+ conn->dh_ys = os_memdup(pos, conn->dh_ys_len);
if (conn->dh_ys == NULL)
goto fail;
- os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
pos += conn->dh_ys_len;
wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
conn->dh_ys, conn->dh_ys_len);
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index 6b28417e499c..e178915a454d 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -21,7 +21,7 @@
* RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
* Add support for commonly used cipher suites; don't bother with exportable
* suites.
- */
+ */
static const struct tls_cipher_suite tls_cipher_suites[] = {
{ TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
@@ -482,21 +482,21 @@ int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
"\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
{
- wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256");
+ wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-256");
decrypted = buf + 19;
buflen -= 19;
} else if (buflen >= 19 + 48 &&
os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01"
"\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0)
{
- wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384");
+ wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-384");
decrypted = buf + 19;
buflen -= 19;
} else if (buflen >= 19 + 64 &&
os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01"
"\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0)
{
- wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512");
+ wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-512");
decrypted = buf + 19;
buflen -= 19;
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 52c1ae0143da..842e5dd728c7 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -1166,10 +1166,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
if (hdr.length == 0)
return -1;
os_free(cred->dh_p);
- cred->dh_p = os_malloc(hdr.length);
+ cred->dh_p = os_memdup(hdr.payload, hdr.length);
if (cred->dh_p == NULL)
return -1;
- os_memcpy(cred->dh_p, hdr.payload, hdr.length);
cred->dh_p_len = hdr.length;
pos = hdr.payload + hdr.length;
@@ -1188,10 +1187,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
if (hdr.length == 0)
return -1;
os_free(cred->dh_g);
- cred->dh_g = os_malloc(hdr.length);
+ cred->dh_g = os_memdup(hdr.payload, hdr.length);
if (cred->dh_g == NULL)
return -1;
- os_memcpy(cred->dh_g, hdr.payload, hdr.length);
cred->dh_g_len = hdr.length;
return 0;
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index ba47337bcbb1..540696904095 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -216,7 +216,7 @@ failed:
* @in_data: Pointer to plaintext data to be encrypted
* @in_len: Input buffer length
* @out_data: Pointer to output buffer (encrypted TLS data)
- * @out_len: Maximum out_data length
+ * @out_len: Maximum out_data length
* Returns: Number of bytes written to out_data, -1 on failure
*
* This function is used after TLS handshake has been completed successfully to
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index 75f222c4f249..f80c9a358bf5 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -274,13 +274,12 @@ static int x509_parse_public_key(const u8 *buf, size_t len,
*/
}
os_free(cert->public_key);
- cert->public_key = os_malloc(hdr.length - 1);
+ cert->public_key = os_memdup(pos + 1, hdr.length - 1);
if (cert->public_key == NULL) {
wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
"public key");
return -1;
}
- os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
cert->public_key_len = hdr.length - 1;
wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
cert->public_key, cert->public_key_len);
@@ -925,10 +924,9 @@ static int x509_parse_alt_name_ip(struct x509_name *name,
/* iPAddress OCTET STRING */
wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
os_free(name->ip);
- name->ip = os_malloc(len);
+ name->ip = os_memdup(pos, len);
if (name->ip == NULL)
return -1;
- os_memcpy(name->ip, pos, len);
name->ip_len = len;
return 0;
}
@@ -1700,14 +1698,13 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
return NULL;
}
os_free(cert->sign_value);
- cert->sign_value = os_malloc(hdr.length - 1);
+ cert->sign_value = os_memdup(pos + 1, hdr.length - 1);
if (cert->sign_value == NULL) {
wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
"signatureValue");
x509_certificate_free(cert);
return NULL;
}
- os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
cert->sign_value_len = hdr.length - 1;
wpa_hexdump(MSG_MSGDUMP, "X509: signature",
cert->sign_value, cert->sign_value_len);
@@ -2039,7 +2036,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted,
for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
cert->issuer_trusted = 0;
- x509_name_string(&cert->subject, buf, sizeof(buf));
+ x509_name_string(&cert->subject, buf, sizeof(buf));
wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
if (chain_trusted)
@@ -2063,11 +2060,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted,
wpa_printf(MSG_DEBUG, "X509: Certificate "
"chain issuer name mismatch");
x509_name_string(&cert->issuer, buf,
- sizeof(buf));
+ sizeof(buf));
wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
buf);
x509_name_string(&cert->next->subject, buf,
- sizeof(buf));
+ sizeof(buf));
wpa_printf(MSG_DEBUG, "X509: next cert "
"subject: %s", buf);
*reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
diff --git a/src/utils/Makefile b/src/utils/Makefile
index 8aad813cfc85..52efc5321fca 100644
--- a/src/utils/Makefile
+++ b/src/utils/Makefile
@@ -17,6 +17,7 @@ LIB_OBJS= \
base64.o \
bitfield.o \
common.o \
+ crc32.o \
ip_addr.o \
radiotap.o \
trace.o \
diff --git a/src/utils/base64.c b/src/utils/base64.c
index d44f290e5684..8eb4ba127d48 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -13,21 +13,14 @@
static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const unsigned char base64_url_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-/**
- * base64_encode - Base64 encode
- * @src: Data to be encoded
- * @len: Length of the data to be encoded
- * @out_len: Pointer to output length variable, or %NULL if not used
- * Returns: Allocated buffer of out_len bytes of encoded data,
- * or %NULL on failure
- *
- * Caller is responsible for freeing the returned buffer. Returned buffer is
- * nul terminated to make it easier to use as a C string. The nul terminator is
- * not included in out_len.
- */
-unsigned char * base64_encode(const unsigned char *src, size_t len,
- size_t *out_len)
+
+static unsigned char * base64_gen_encode(const unsigned char *src, size_t len,
+ size_t *out_len,
+ const unsigned char *table,
+ int add_pad)
{
unsigned char *out, *pos;
const unsigned char *end, *in;
@@ -35,7 +28,8 @@ unsigned char * base64_encode(const unsigned char *src, size_t len,
int line_len;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
- olen += olen / 72; /* line feeds */
+ if (add_pad)
+ olen += olen / 72; /* line feeds */
olen++; /* nul termination */
if (olen < len)
return NULL; /* integer overflow */
@@ -48,35 +42,35 @@ unsigned char * base64_encode(const unsigned char *src, size_t len,
pos = out;
line_len = 0;
while (end - in >= 3) {
- *pos++ = base64_table[(in[0] >> 2) & 0x3f];
- *pos++ = base64_table[(((in[0] & 0x03) << 4) |
- (in[1] >> 4)) & 0x3f];
- *pos++ = base64_table[(((in[1] & 0x0f) << 2) |
- (in[2] >> 6)) & 0x3f];
- *pos++ = base64_table[in[2] & 0x3f];
+ *pos++ = table[(in[0] >> 2) & 0x3f];
+ *pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f];
+ *pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f];
+ *pos++ = table[in[2] & 0x3f];
in += 3;
line_len += 4;
- if (line_len >= 72) {
+ if (add_pad && line_len >= 72) {
*pos++ = '\n';
line_len = 0;
}
}
if (end - in) {
- *pos++ = base64_table[(in[0] >> 2) & 0x3f];
+ *pos++ = table[(in[0] >> 2) & 0x3f];
if (end - in == 1) {
- *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f];
- *pos++ = '=';
+ *pos++ = table[((in[0] & 0x03) << 4) & 0x3f];
+ if (add_pad)
+ *pos++ = '=';
} else {
- *pos++ = base64_table[(((in[0] & 0x03) << 4) |
- (in[1] >> 4)) & 0x3f];
- *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f];
+ *pos++ = table[(((in[0] & 0x03) << 4) |
+ (in[1] >> 4)) & 0x3f];
+ *pos++ = table[((in[1] & 0x0f) << 2) & 0x3f];
}
- *pos++ = '=';
+ if (add_pad)
+ *pos++ = '=';
line_len += 4;
}
- if (line_len)
+ if (add_pad && line_len)
*pos++ = '\n';
*pos = '\0';
@@ -86,26 +80,18 @@ unsigned char * base64_encode(const unsigned char *src, size_t len,
}
-/**
- * base64_decode - Base64 decode
- * @src: Data to be decoded
- * @len: Length of the data to be decoded
- * @out_len: Pointer to output length variable
- * Returns: Allocated buffer of out_len bytes of decoded data,
- * or %NULL on failure
- *
- * Caller is responsible for freeing the returned buffer.
- */
-unsigned char * base64_decode(const unsigned char *src, size_t len,
- size_t *out_len)
+static unsigned char * base64_gen_decode(const unsigned char *src, size_t len,
+ size_t *out_len,
+ const unsigned char *table)
{
unsigned char dtable[256], *out, *pos, block[4], tmp;
size_t i, count, olen;
int pad = 0;
+ size_t extra_pad;
os_memset(dtable, 0x80, 256);
for (i = 0; i < sizeof(base64_table) - 1; i++)
- dtable[base64_table[i]] = (unsigned char) i;
+ dtable[table[i]] = (unsigned char) i;
dtable['='] = 0;
count = 0;
@@ -114,21 +100,28 @@ unsigned char * base64_decode(const unsigned char *src, size_t len,
count++;
}
- if (count == 0 || count % 4)
+ if (count == 0)
return NULL;
+ extra_pad = (4 - count % 4) % 4;
- olen = count / 4 * 3;
+ olen = (count + extra_pad) / 4 * 3;
pos = out = os_malloc(olen);
if (out == NULL)
return NULL;
count = 0;
- for (i = 0; i < len; i++) {
- tmp = dtable[src[i]];
+ for (i = 0; i < len + extra_pad; i++) {
+ unsigned char val;
+
+ if (i >= len)
+ val = '=';
+ else
+ val = src[i];
+ tmp = dtable[val];
if (tmp == 0x80)
continue;
- if (src[i] == '=')
+ if (val == '=')
pad++;
block[count] = tmp;
count++;
@@ -155,3 +148,53 @@ unsigned char * base64_decode(const unsigned char *src, size_t len,
*out_len = pos - out;
return out;
}
+
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ return base64_gen_encode(src, len, out_len, base64_table, 1);
+}
+
+
+unsigned char * base64_url_encode(const unsigned char *src, size_t len,
+ size_t *out_len, int add_pad)
+{
+ return base64_gen_encode(src, len, out_len, base64_url_table, add_pad);
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ return base64_gen_decode(src, len, out_len, base64_table);
+}
+
+
+unsigned char * base64_url_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ return base64_gen_decode(src, len, out_len, base64_url_table);
+}
diff --git a/src/utils/base64.h b/src/utils/base64.h
index aa21fd0fc1b7..5a72c3ebf522 100644
--- a/src/utils/base64.h
+++ b/src/utils/base64.h
@@ -13,5 +13,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len,
size_t *out_len);
unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len);
+unsigned char * base64_url_encode(const unsigned char *src, size_t len,
+ size_t *out_len, int add_pad);
+unsigned char * base64_url_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
#endif /* BASE64_H */
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
index 59ba4d1e02d8..dfb4b67977f8 100644
--- a/src/utils/browser-wpadebug.c
+++ b/src/utils/browser-wpadebug.c
@@ -52,7 +52,7 @@ static void http_req(void *ctx, struct http_request *req)
eloop_terminate();
return;
}
- wpabuf_put_str(resp, "User input completed");
+ wpabuf_put_str(resp, "HTTP/1.1\r\n\r\nUser input completed");
if (done) {
eloop_cancel_timeout(browser_timeout, NULL, NULL);
@@ -97,6 +97,7 @@ int hs20_web_browser(const char *url)
if (pid == 0) {
/* run the external command in the child process */
char *argv[14];
+ char *envp[] = { "PATH=/system/bin:/vendor/bin", NULL };
argv[0] = "browser-wpadebug";
argv[1] = "start";
@@ -113,8 +114,8 @@ int hs20_web_browser(const char *url)
argv[12] = "-3"; /* USER_CURRENT_OR_SELF */
argv[13] = NULL;
- execv("/system/bin/am", argv);
- wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+ execve("/system/bin/am", argv, envp);
+ wpa_printf(MSG_ERROR, "execve: %s", strerror(errno));
exit(0);
return -1;
}
diff --git a/src/utils/common.c b/src/utils/common.c
index 04a533a05902..1eb33705bef3 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -1200,3 +1200,24 @@ int str_starts(const char *str, const char *start)
{
return os_strncmp(str, start, os_strlen(start)) == 0;
}
+
+
+/**
+ * rssi_to_rcpi - Convert RSSI to RCPI
+ * @rssi: RSSI to convert
+ * Returns: RCPI corresponding to the given RSSI value, or 255 if not available.
+ *
+ * It's possible to estimate RCPI based on RSSI in dBm. This calculation will
+ * not reflect the correct value for high rates, but it's good enough for Action
+ * frames which are transmitted with up to 24 Mbps rates.
+ */
+u8 rssi_to_rcpi(int rssi)
+{
+ if (!rssi)
+ return 255; /* not available */
+ if (rssi < -110)
+ return 0;
+ if (rssi > 0)
+ return 220;
+ return (rssi + 110) * 2;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 77856774d215..f824d001aeab 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -53,6 +53,15 @@ static inline unsigned int bswap_32(unsigned int v)
}
#endif /* __APPLE__ */
+#ifdef __rtems__
+#include <rtems/endian.h>
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#define bswap_16 CPU_swap_u16
+#define bswap_32 CPU_swap_u32
+#endif /* __rtems__ */
+
#ifdef CONFIG_NATIVE_WINDOWS
#include <winsock.h>
@@ -141,6 +150,7 @@ static inline unsigned int wpa_swap_32(unsigned int v)
#define host_to_le32(n) (n)
#define be_to_host32(n) wpa_swap_32(n)
#define host_to_be32(n) wpa_swap_32(n)
+#define host_to_le64(n) (n)
#define WPA_BYTE_SWAP_DEFINED
@@ -331,6 +341,9 @@ static inline void WPA_PUT_LE64(u8 *a, u64 val)
#ifndef ETH_P_RRB
#define ETH_P_RRB 0x890D
#endif /* ETH_P_RRB */
+#ifndef ETH_P_OUI
+#define ETH_P_OUI 0x88B7
+#endif /* ETH_P_OUI */
#ifdef __GNUC__
@@ -423,6 +436,7 @@ void perror(const char *s);
#define __bitwise __attribute__((bitwise))
#else
#define __force
+#undef __bitwise
#define __bitwise
#endif
@@ -552,6 +566,7 @@ int is_ctrl_char(char c);
int str_starts(const char *str, const char *start);
+u8 rssi_to_rcpi(int rssi);
/*
* gcc 4.4 ends up generating strict-aliasing warnings about some very common
diff --git a/src/utils/crc32.c b/src/utils/crc32.c
new file mode 100644
index 000000000000..12d9e2a7008e
--- /dev/null
+++ b/src/utils/crc32.c
@@ -0,0 +1,85 @@
+/*
+ * 32-bit CRC for FCS calculation
+ * Copyright (c) 2010, 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/crc32.h"
+
+/*
+ * IEEE 802.11 FCS CRC32
+ * G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 +
+ * x^5 + x^4 + x^2 + x + 1
+ */
+static const u32 crc32_table[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+ 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+ 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+ 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+ 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+ 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+ 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+ 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+ 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+ 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+ 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+ 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+ 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+ 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+ 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+ 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+ 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+ 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+ 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+ 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+ 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+ 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+ 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+ 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+ 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+ 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+ 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+ 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+ 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+ 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+ 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+ 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+ 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+ 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+ 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+ 0x2d02ef8d
+};
+
+
+u32 crc32(const u8 *frame, size_t frame_len)
+{
+ size_t i;
+ u32 crc;
+
+ crc = 0xFFFFFFFF;
+ for (i = 0; i < frame_len; i++)
+ crc = crc32_table[(crc ^ frame[i]) & 0xff] ^ (crc >> 8);
+
+ return ~crc;
+}
diff --git a/src/utils/crc32.h b/src/utils/crc32.h
new file mode 100644
index 000000000000..dc31399beb63
--- /dev/null
+++ b/src/utils/crc32.h
@@ -0,0 +1,14 @@
+/*
+ * 32-bit CRC for FCS calculation
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CRC32_H
+#define CRC32_H
+
+u32 crc32(const u8 *frame, size_t frame_len);
+
+#endif /* CRC32_H */
diff --git a/src/utils/eloop.h b/src/utils/eloop.h
index 97af16f0130a..04ee6d1837b6 100644
--- a/src/utils/eloop.h
+++ b/src/utils/eloop.h
@@ -45,16 +45,16 @@ typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx);
/**
* eloop_event_handler - eloop generic event callback type
* @eloop_ctx: Registered callback context data (eloop_data)
- * @sock_ctx: Registered callback context data (user_data)
+ * @user_ctx: Registered callback context data (user_data)
*/
-typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx);
+typedef void (*eloop_event_handler)(void *eloop_ctx, void *user_ctx);
/**
* eloop_timeout_handler - eloop timeout event callback type
* @eloop_ctx: Registered callback context data (eloop_data)
- * @sock_ctx: Registered callback context data (user_data)
+ * @user_ctx: Registered callback context data (user_data)
*/
-typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx);
+typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
/**
* eloop_signal_handler - eloop signal event callback type
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index a06aae8d9b9d..58519ea8d248 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -486,12 +486,11 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
return;
n->hash_len = ASN1_STRING_length(hash->hashValue);
- n->hash = os_malloc(n->hash_len);
+ n->hash = os_memdup(ASN1_STRING_data(hash->hashValue), n->hash_len);
if (n->hash == NULL) {
os_free(n->alg_oid);
return;
}
- os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len);
len = ASN1_STRING_length(uri);
n->uri = os_malloc(len + 1);
@@ -987,7 +986,7 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
- ssl_ctx = ssl->ctx;
+ ssl_ctx = SSL_get_SSL_CTX(ssl);
ctx = SSL_CTX_get_app_data(ssl_ctx);
wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d",
@@ -1095,7 +1094,7 @@ static int ocsp_resp_cb(SSL *s, void *arg)
{
struct http_ctx *ctx = arg;
const unsigned char *p;
- int len, status, reason;
+ int len, status, reason, res;
OCSP_RESPONSE *rsp;
OCSP_BASICRESP *basic;
OCSP_CERTID *id;
@@ -1200,17 +1199,36 @@ static int ocsp_resp_cb(SSL *s, void *arg)
return 0;
}
- id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+ id = OCSP_cert_to_id(EVP_sha256(), ctx->peer_cert, ctx->peer_issuer);
if (!id) {
- wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
ctx->last_err = "Could not create OCSP certificate identifier";
return 0;
}
- if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
- &this_update, &next_update)) {
+ res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+ &this_update, &next_update);
+ if (!res) {
+ id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+ if (!id) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(rsp);
+ ctx->last_err =
+ "Could not create OCSP certificate identifier";
+ return 0;
+ }
+
+ res = OCSP_resp_find_status(basic, id, &status, &reason,
+ &produced_at, &this_update,
+ &next_update);
+ }
+
+ if (!res) {
wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
(ctx->ocsp == MANDATORY_OCSP) ? "" :
" (OCSP not required)");
diff --git a/src/utils/json.c b/src/utils/json.c
new file mode 100644
index 000000000000..b9130d3a65ab
--- /dev/null
+++ b/src/utils/json.c
@@ -0,0 +1,569 @@
+/*
+ * JavaScript Object Notation (JSON) parser (RFC7159)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "json.h"
+
+#define JSON_MAX_DEPTH 10
+#define JSON_MAX_TOKENS 500
+
+
+void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
+{
+ char *end = txt + maxlen;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (txt + 4 >= end)
+ break;
+
+ switch (data[i]) {
+ case '\"':
+ *txt++ = '\\';
+ *txt++ = '\"';
+ break;
+ case '\\':
+ *txt++ = '\\';
+ *txt++ = '\\';
+ break;
+ case '\n':
+ *txt++ = '\\';
+ *txt++ = 'n';
+ break;
+ case '\r':
+ *txt++ = '\\';
+ *txt++ = 'r';
+ break;
+ case '\t':
+ *txt++ = '\\';
+ *txt++ = 't';
+ break;
+ default:
+ if (data[i] >= 32 && data[i] <= 126) {
+ *txt++ = data[i];
+ } else {
+ txt += os_snprintf(txt, end - txt, "\\u%04x",
+ data[i]);
+ }
+ break;
+ }
+ }
+
+ *txt = '\0';
+}
+
+
+static char * json_parse_string(const char **json_pos, const char *end)
+{
+ const char *pos = *json_pos;
+ char *str, *spos, *s_end;
+ size_t max_len, buf_len;
+ u8 bin[2];
+
+ pos++; /* skip starting quote */
+
+ max_len = end - pos + 1;
+ buf_len = max_len > 10 ? 10 : max_len;
+ str = os_malloc(buf_len);
+ if (!str)
+ return NULL;
+ spos = str;
+ s_end = str + buf_len;
+
+ for (; pos < end; pos++) {
+ if (buf_len < max_len && s_end - spos < 3) {
+ char *tmp;
+ int idx;
+
+ idx = spos - str;
+ buf_len *= 2;
+ if (buf_len > max_len)
+ buf_len = max_len;
+ tmp = os_realloc(str, buf_len);
+ if (!tmp)
+ goto fail;
+ str = tmp;
+ spos = str + idx;
+ s_end = str + buf_len;
+ }
+
+ switch (*pos) {
+ case '\"': /* end string */
+ *spos = '\0';
+ /* caller will move to the next position */
+ *json_pos = pos;
+ return str;
+ case '\\':
+ pos++;
+ switch (*pos) {
+ case '"':
+ case '\\':
+ case '/':
+ *spos++ = *pos;
+ break;
+ case 'n':
+ *spos++ = '\n';
+ break;
+ case 'r':
+ *spos++ = '\r';
+ break;
+ case 't':
+ *spos++ = '\t';
+ break;
+ case 'u':
+ if (end - pos < 5 ||
+ hexstr2bin(pos + 1, bin, 2) < 0 ||
+ bin[1] == 0x00) {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Invalid \\u escape");
+ goto fail;
+ }
+ if (bin[0] == 0x00) {
+ *spos++ = bin[1];
+ } else {
+ *spos++ = bin[0];
+ *spos++ = bin[1];
+ }
+ pos += 4;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "JSON: Unknown escape '%c'", *pos);
+ goto fail;
+ }
+ break;
+ default:
+ *spos++ = *pos;
+ break;
+ }
+ }
+
+fail:
+ os_free(str);
+ return NULL;
+}
+
+
+static int json_parse_number(const char **json_pos, const char *end,
+ int *ret_val)
+{
+ const char *pos = *json_pos;
+ size_t len;
+ char *str;
+
+ for (; pos < end; pos++) {
+ if (*pos != '-' && (*pos < '0' || *pos > '9')) {
+ pos--;
+ break;
+ }
+ }
+ if (pos < *json_pos)
+ return -1;
+ len = pos - *json_pos + 1;
+ str = os_malloc(len + 1);
+ if (!str)
+ return -1;
+ os_memcpy(str, *json_pos, len);
+ str[len] = '\0';
+
+ *ret_val = atoi(str);
+ os_free(str);
+ *json_pos = pos;
+ return 0;
+}
+
+
+static int json_check_tree_state(struct json_token *token)
+{
+ if (!token)
+ return 0;
+ if (json_check_tree_state(token->child) < 0 ||
+ json_check_tree_state(token->sibling) < 0)
+ return -1;
+ if (token->state != JSON_COMPLETED) {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Unexpected token state %d (name=%s type=%d)",
+ token->state, token->name ? token->name : "N/A",
+ token->type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static struct json_token * json_alloc_token(unsigned int *tokens)
+{
+ (*tokens)++;
+ if (*tokens > JSON_MAX_TOKENS) {
+ wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
+ return NULL;
+ }
+ return os_zalloc(sizeof(struct json_token));
+}
+
+
+struct json_token * json_parse(const char *data, size_t data_len)
+{
+ struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
+ const char *pos, *end;
+ char *str;
+ int num;
+ unsigned int depth = 0;
+ unsigned int tokens = 0;
+
+ pos = data;
+ end = data + data_len;
+
+ for (; pos < end; pos++) {
+ switch (*pos) {
+ case '[': /* start array */
+ case '{': /* start object */
+ if (!curr_token) {
+ token = json_alloc_token(&tokens);
+ if (!token)
+ goto fail;
+ if (!root)
+ root = token;
+ } else if (curr_token->state == JSON_WAITING_VALUE) {
+ token = curr_token;
+ } else if (curr_token->parent &&
+ curr_token->parent->type == JSON_ARRAY &&
+ curr_token->parent->state == JSON_STARTED &&
+ curr_token->state == JSON_EMPTY) {
+ token = curr_token;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Invalid state for start array/object");
+ goto fail;
+ }
+ depth++;
+ if (depth > JSON_MAX_DEPTH) {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Max depth exceeded");
+ goto fail;
+ }
+ token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
+ token->state = JSON_STARTED;
+ token->child = json_alloc_token(&tokens);
+ if (!token->child)
+ goto fail;
+ curr_token = token->child;
+ curr_token->parent = token;
+ curr_token->state = JSON_EMPTY;
+ break;
+ case ']': /* end array */
+ case '}': /* end object */
+ if (!curr_token || !curr_token->parent ||
+ curr_token->parent->state != JSON_STARTED) {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Invalid state for end array/object");
+ goto fail;
+ }
+ depth--;
+ curr_token = curr_token->parent;
+ if ((*pos == ']' &&
+ curr_token->type != JSON_ARRAY) ||
+ (*pos == '}' &&
+ curr_token->type != JSON_OBJECT)) {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Array/Object mismatch");
+ goto fail;
+ }
+ if (curr_token->child->state == JSON_EMPTY &&
+ !curr_token->child->child &&
+ !curr_token->child->sibling) {
+ /* Remove pending child token since the
+ * array/object was empty. */
+ json_free(curr_token->child);
+ curr_token->child = NULL;
+ }
+ curr_token->state = JSON_COMPLETED;
+ break;
+ case '\"': /* string */
+ str = json_parse_string(&pos, end);
+ if (!str)
+ goto fail;
+ if (!curr_token) {
+ token = json_alloc_token(&tokens);
+ if (!token)
+ goto fail;
+ token->type = JSON_STRING;
+ token->string = str;
+ token->state = JSON_COMPLETED;
+ } else if (curr_token->parent &&
+ curr_token->parent->type == JSON_ARRAY &&
+ curr_token->parent->state == JSON_STARTED &&
+ curr_token->state == JSON_EMPTY) {
+ curr_token->string = str;
+ curr_token->state = JSON_COMPLETED;
+ curr_token->type = JSON_STRING;
+ wpa_printf(MSG_MSGDUMP,
+ "JSON: String value: '%s'",
+ curr_token->string);
+ } else if (curr_token->state == JSON_EMPTY) {
+ curr_token->type = JSON_VALUE;
+ curr_token->name = str;
+ curr_token->state = JSON_STARTED;
+ } else if (curr_token->state == JSON_WAITING_VALUE) {
+ curr_token->string = str;
+ curr_token->state = JSON_COMPLETED;
+ curr_token->type = JSON_STRING;
+ wpa_printf(MSG_MSGDUMP,
+ "JSON: String value: '%s' = '%s'",
+ curr_token->name,
+ curr_token->string);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Invalid state for a string");
+ os_free(str);
+ goto fail;
+ }
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ /* ignore whitespace */
+ break;
+ case ':': /* name/value separator */
+ if (!curr_token || curr_token->state != JSON_STARTED)
+ goto fail;
+ curr_token->state = JSON_WAITING_VALUE;
+ break;
+ case ',': /* member separator */
+ if (!curr_token)
+ goto fail;
+ curr_token->sibling = json_alloc_token(&tokens);
+ if (!curr_token->sibling)
+ goto fail;
+ curr_token->sibling->parent = curr_token->parent;
+ curr_token = curr_token->sibling;
+ curr_token->state = JSON_EMPTY;
+ break;
+ case 't': /* true */
+ case 'f': /* false */
+ case 'n': /* null */
+ if (!((end - pos >= 4 &&
+ os_strncmp(pos, "true", 4) == 0) ||
+ (end - pos >= 5 &&
+ os_strncmp(pos, "false", 5) == 0) ||
+ (end - pos >= 4 &&
+ os_strncmp(pos, "null", 4) == 0))) {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Invalid literal name");
+ goto fail;
+ }
+ if (!curr_token) {
+ token = json_alloc_token(&tokens);
+ if (!token)
+ goto fail;
+ curr_token = token;
+ } else if (curr_token->state == JSON_WAITING_VALUE) {
+ wpa_printf(MSG_MSGDUMP,
+ "JSON: Literal name: '%s' = %c",
+ curr_token->name, *pos);
+ } else if (curr_token->parent &&
+ curr_token->parent->type == JSON_ARRAY &&
+ curr_token->parent->state == JSON_STARTED &&
+ curr_token->state == JSON_EMPTY) {
+ wpa_printf(MSG_MSGDUMP,
+ "JSON: Literal name: %c", *pos);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Invalid state for a literal name");
+ goto fail;
+ }
+ switch (*pos) {
+ case 't':
+ curr_token->type = JSON_BOOLEAN;
+ curr_token->number = 1;
+ pos += 3;
+ break;
+ case 'f':
+ curr_token->type = JSON_BOOLEAN;
+ curr_token->number = 0;
+ pos += 4;
+ break;
+ case 'n':
+ curr_token->type = JSON_NULL;
+ pos += 3;
+ break;
+ }
+ curr_token->state = JSON_COMPLETED;
+ break;
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* number */
+ if (json_parse_number(&pos, end, &num) < 0)
+ goto fail;
+ if (!curr_token) {
+ token = json_alloc_token(&tokens);
+ if (!token)
+ goto fail;
+ token->type = JSON_NUMBER;
+ token->number = num;
+ token->state = JSON_COMPLETED;
+ } else if (curr_token->state == JSON_WAITING_VALUE) {
+ curr_token->number = num;
+ curr_token->state = JSON_COMPLETED;
+ curr_token->type = JSON_NUMBER;
+ wpa_printf(MSG_MSGDUMP,
+ "JSON: Number value: '%s' = '%d'",
+ curr_token->name,
+ curr_token->number);
+ } else if (curr_token->parent &&
+ curr_token->parent->type == JSON_ARRAY &&
+ curr_token->parent->state == JSON_STARTED &&
+ curr_token->state == JSON_EMPTY) {
+ curr_token->number = num;
+ curr_token->state = JSON_COMPLETED;
+ curr_token->type = JSON_NUMBER;
+ wpa_printf(MSG_MSGDUMP,
+ "JSON: Number value: %d",
+ curr_token->number);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Invalid state for a number");
+ goto fail;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "JSON: Unexpected JSON character: %c", *pos);
+ goto fail;
+ }
+
+ if (!root)
+ root = token;
+ if (!curr_token)
+ curr_token = token;
+ }
+
+ if (json_check_tree_state(root) < 0) {
+ wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
+ goto fail;
+ }
+
+ return root;
+fail:
+ wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
+ json_free(root);
+ return NULL;
+}
+
+
+void json_free(struct json_token *json)
+{
+ if (!json)
+ return;
+ json_free(json->child);
+ json_free(json->sibling);
+ os_free(json->name);
+ os_free(json->string);
+ os_free(json);
+}
+
+
+struct json_token * json_get_member(struct json_token *json, const char *name)
+{
+ struct json_token *token, *ret = NULL;
+
+ if (!json || json->type != JSON_OBJECT)
+ return NULL;
+ /* Return last matching entry */
+ for (token = json->child; token; token = token->sibling) {
+ if (token->name && os_strcmp(token->name, name) == 0)
+ ret = token;
+ }
+ return ret;
+}
+
+
+struct wpabuf * json_get_member_base64url(struct json_token *json,
+ const char *name)
+{
+ struct json_token *token;
+ unsigned char *buf;
+ size_t buflen;
+ struct wpabuf *ret;
+
+ token = json_get_member(json, name);
+ if (!token || token->type != JSON_STRING)
+ return NULL;
+ buf = base64_url_decode((const unsigned char *) token->string,
+ os_strlen(token->string), &buflen);
+ if (!buf)
+ return NULL;
+ ret = wpabuf_alloc_ext_data(buf, buflen);
+ if (!ret)
+ os_free(buf);
+
+ return ret;
+}
+
+
+static const char * json_type_str(enum json_type type)
+{
+ switch (type) {
+ case JSON_VALUE:
+ return "VALUE";
+ case JSON_OBJECT:
+ return "OBJECT";
+ case JSON_ARRAY:
+ return "ARRAY";
+ case JSON_STRING:
+ return "STRING";
+ case JSON_NUMBER:
+ return "NUMBER";
+ case JSON_BOOLEAN:
+ return "BOOLEAN";
+ case JSON_NULL:
+ return "NULL";
+ }
+ return "??";
+}
+
+
+static void json_print_token(struct json_token *token, int depth,
+ char *buf, size_t buflen)
+{
+ size_t len;
+ int ret;
+
+ if (!token)
+ return;
+ len = os_strlen(buf);
+ ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
+ depth, json_type_str(token->type),
+ token->name ? token->name : "");
+ if (os_snprintf_error(buflen - len, ret)) {
+ buf[len] = '\0';
+ return;
+ }
+ json_print_token(token->child, depth + 1, buf, buflen);
+ json_print_token(token->sibling, depth, buf, buflen);
+}
+
+
+void json_print_tree(struct json_token *root, char *buf, size_t buflen)
+{
+ buf[0] = '\0';
+ json_print_token(root, 1, buf, buflen);
+}
diff --git a/src/utils/json.h b/src/utils/json.h
new file mode 100644
index 000000000000..8faa95d8bf20
--- /dev/null
+++ b/src/utils/json.h
@@ -0,0 +1,42 @@
+/*
+ * JavaScript Object Notation (JSON) parser (RFC7159)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef JSON_H
+#define JSON_H
+
+struct json_token {
+ enum json_type {
+ JSON_VALUE,
+ JSON_OBJECT,
+ JSON_ARRAY,
+ JSON_STRING,
+ JSON_NUMBER,
+ JSON_BOOLEAN,
+ JSON_NULL,
+ } type;
+ enum json_parsing_state {
+ JSON_EMPTY,
+ JSON_STARTED,
+ JSON_WAITING_VALUE,
+ JSON_COMPLETED,
+ } state;
+ char *name;
+ char *string;
+ int number;
+ struct json_token *parent, *child, *sibling;
+};
+
+void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len);
+struct json_token * json_parse(const char *data, size_t data_len);
+void json_free(struct json_token *json);
+struct json_token * json_get_member(struct json_token *json, const char *name);
+struct wpabuf * json_get_member_base64url(struct json_token *json,
+ const char *name);
+void json_print_tree(struct json_token *root, char *buf, size_t buflen);
+
+#endif /* JSON_H */
diff --git a/src/utils/os.h b/src/utils/os.h
index e8f0b792738a..21ba5c3ff85b 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -614,6 +614,18 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz);
*/
int os_memcmp_const(const void *a, const void *b, size_t len);
+
+/**
+ * os_memdup - Allocate duplicate of passed memory chunk
+ * @src: Source buffer to duplicate
+ * @len: Length of source buffer
+ * Returns: %NULL if allocation failed, copy of src buffer otherwise
+ *
+ * This function allocates a memory block like os_malloc() would, and
+ * copies the given source buffer into it.
+ */
+void * os_memdup(const void *src, size_t len);
+
/**
* os_exec - Execute an external program
* @program: Path to the program
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index 0c3214d32ce5..e74f206a2c5a 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -114,6 +114,12 @@ void * os_zalloc(size_t size)
}
+void * os_memdup(const void *src, size_t n)
+{
+ return NULL;
+}
+
+
#ifdef OS_NO_C_LIB_DEFINES
void * os_malloc(size_t size)
{
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index 65c6fa412f20..1894fcdb0cf2 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -80,6 +80,9 @@ int os_get_reltime(struct os_reltime *t)
struct timespec ts;
int res;
+ if (TEST_FAIL())
+ return -1;
+
while (1) {
res = clock_gettime(clock_id, &ts);
if (res == 0) {
@@ -505,6 +508,16 @@ int os_memcmp_const(const void *a, const void *b, size_t len)
}
+void * os_memdup(const void *src, size_t len)
+{
+ void *r = os_malloc(len);
+
+ if (r)
+ os_memcpy(r, src, len);
+ return r;
+}
+
+
#ifdef WPA_TRACE
#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
@@ -537,6 +550,8 @@ static int testing_fail_alloc(void)
i++;
if (i < res && os_strcmp(func[i], "os_strdup") == 0)
i++;
+ if (i < res && os_strcmp(func[i], "os_memdup") == 0)
+ i++;
pos = wpa_trace_fail_func;
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index dea27b9f2ad8..f9e4b308ea5f 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -283,3 +283,13 @@ int os_exec(const char *program, const char *arg, int wait_completion)
{
return -1;
}
+
+
+void * os_memdup(const void *src, size_t len)
+{
+ void *r = os_malloc(len);
+
+ if (r)
+ os_memcpy(r, src, len);
+ return r;
+}
diff --git a/src/utils/trace.c b/src/utils/trace.c
index d72cf604f8e9..e0b5b0bb99f5 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -6,6 +6,10 @@
* See README for more details.
*/
+#ifdef WPA_TRACE_BFD
+#define _GNU_SOURCE
+#include <link.h>
+#endif /* WPA_TRACE_BCD */
#include "includes.h"
#include "common.h"
@@ -25,6 +29,28 @@ static struct dl_list active_references =
static char *prg_fname = NULL;
static bfd *cached_abfd = NULL;
static asymbol **syms = NULL;
+static unsigned long start_offset;
+static int start_offset_looked_up;
+
+
+static int callback(struct dl_phdr_info *info, size_t size, void *data)
+{
+ /*
+ * dl_iterate_phdr(3):
+ * "The first object visited by callback is the main program."
+ */
+ start_offset = info->dlpi_addr;
+
+ /*
+ * dl_iterate_phdr(3):
+ * "The dl_iterate_phdr() function walks through the list of an
+ * application's shared objects and calls the function callback
+ * once for each object, until either all shared objects have
+ * been processed or callback returns a nonzero value."
+ */
+ return 1;
+}
+
static void get_prg_fname(void)
{
@@ -160,7 +186,7 @@ static void wpa_trace_bfd_addr(void *pc)
if (abfd == NULL)
return;
- data.pc = (bfd_hostptr_t) pc;
+ data.pc = (bfd_hostptr_t) (pc - start_offset);
data.found = FALSE;
bfd_map_over_sections(abfd, find_addr_sect, &data);
@@ -201,7 +227,7 @@ static const char * wpa_trace_bfd_addr2func(void *pc)
if (abfd == NULL)
return NULL;
- data.pc = (bfd_hostptr_t) pc;
+ data.pc = (bfd_hostptr_t) (pc - start_offset);
data.found = FALSE;
bfd_map_over_sections(abfd, find_addr_sect, &data);
@@ -233,6 +259,11 @@ static void wpa_trace_bfd_init(void)
wpa_printf(MSG_INFO, "Failed to read symbols");
return;
}
+
+ if (!start_offset_looked_up) {
+ dl_iterate_phdr(callback, NULL);
+ start_offset_looked_up = 1;
+ }
}
@@ -268,7 +299,7 @@ size_t wpa_trace_calling_func(const char *buf[], size_t len)
for (i = 0; i < btrace_num; i++) {
struct bfd_data data;
- data.pc = (bfd_hostptr_t) btrace_res[i];
+ data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset);
data.found = FALSE;
bfd_map_over_sections(abfd, find_addr_sect, &data);
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index abdb79c9879c..1b8ff82b4173 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -16,6 +16,7 @@
#include "utils/base64.h"
#include "utils/ip_addr.h"
#include "utils/eloop.h"
+#include "utils/json.h"
#include "utils/module_tests.h"
@@ -839,6 +840,85 @@ static int eloop_tests(void)
}
+#ifdef CONFIG_JSON
+struct json_test_data {
+ const char *json;
+ const char *tree;
+};
+
+static const struct json_test_data json_test_cases[] = {
+ { "{}", "[1:OBJECT:]" },
+ { "[]", "[1:ARRAY:]" },
+ { "{", NULL },
+ { "[", NULL },
+ { "}", NULL },
+ { "]", NULL },
+ { "[[]]", "[1:ARRAY:][2:ARRAY:]" },
+ { "{\"t\":\"test\"}", "[1:OBJECT:][2:STRING:t]" },
+ { "{\"t\":123}", "[1:OBJECT:][2:NUMBER:t]" },
+ { "{\"t\":true}", "[1:OBJECT:][2:BOOLEAN:t]" },
+ { "{\"t\":false}", "[1:OBJECT:][2:BOOLEAN:t]" },
+ { "{\"t\":null}", "[1:OBJECT:][2:NULL:t]" },
+ { "{\"t\":truetrue}", NULL },
+ { "\"test\"", "[1:STRING:]" },
+ { "123", "[1:NUMBER:]" },
+ { "true", "[1:BOOLEAN:]" },
+ { "false", "[1:BOOLEAN:]" },
+ { "null", "[1:NULL:]" },
+ { "truetrue", NULL },
+ { " {\t\n\r\"a\"\n:\r1\n,\n\"b\":3\n}\n",
+ "[1:OBJECT:][2:NUMBER:a][2:NUMBER:b]" },
+ { ",", NULL },
+ { "{,}", NULL },
+ { "[,]", NULL },
+ { ":", NULL },
+ { "{:}", NULL },
+ { "[:]", NULL },
+ { "{ \"\\u005c\" : \"\\u005c\" }", "[1:OBJECT:][2:STRING:\\]" },
+ { "[{},{}]", "[1:ARRAY:][2:OBJECT:][2:OBJECT:]" },
+ { "[1,2]", "[1:ARRAY:][2:NUMBER:][2:NUMBER:]" },
+ { "[\"1\",\"2\"]", "[1:ARRAY:][2:STRING:][2:STRING:]" },
+ { "[true,false]", "[1:ARRAY:][2:BOOLEAN:][2:BOOLEAN:]" },
+};
+#endif /* CONFIG_JSON */
+
+
+static int json_tests(void)
+{
+#ifdef CONFIG_JSON
+ unsigned int i;
+ struct json_token *root;
+ char buf[1000];
+
+ wpa_printf(MSG_INFO, "JSON tests");
+
+ for (i = 0; i < ARRAY_SIZE(json_test_cases); i++) {
+ const struct json_test_data *test = &json_test_cases[i];
+ int res = 0;
+
+ root = json_parse(test->json, os_strlen(test->json));
+ if ((root && !test->tree) || (!root && test->tree)) {
+ wpa_printf(MSG_INFO, "JSON test %u failed", i);
+ res = -1;
+ } else if (root) {
+ json_print_tree(root, buf, sizeof(buf));
+ if (os_strcmp(buf, test->tree) != 0) {
+ wpa_printf(MSG_INFO,
+ "JSON test %u tree mismatch: %s %s",
+ i, buf, test->tree);
+ res = -1;
+ }
+ }
+ json_free(root);
+ if (res < 0)
+ return -1;
+
+ }
+#endif /* CONFIG_JSON */
+ return 0;
+}
+
+
int utils_module_tests(void)
{
int ret = 0;
@@ -855,6 +935,7 @@ int utils_module_tests(void)
wpabuf_tests() < 0 ||
ip_addr_tests() < 0 ||
eloop_tests() < 0 ||
+ json_tests() < 0 ||
int_array_tests() < 0)
ret = -1;
diff --git a/src/utils/uuid.c b/src/utils/uuid.c
index 0f224f976b80..98e43d02f68b 100644
--- a/src/utils/uuid.c
+++ b/src/utils/uuid.c
@@ -9,6 +9,7 @@
#include "includes.h"
#include "common.h"
+#include "crypto/sha256.h"
#include "uuid.h"
int uuid_str2bin(const char *str, u8 *bin)
@@ -69,3 +70,27 @@ int is_nil_uuid(const u8 *uuid)
return 0;
return 1;
}
+
+
+int uuid_random(u8 *uuid)
+{
+ struct os_time t;
+ u8 hash[SHA256_MAC_LEN];
+
+ /* Use HMAC-SHA256 and timestamp as context to avoid exposing direct
+ * os_get_random() output in the UUID field. */
+ os_get_time(&t);
+ if (os_get_random(uuid, UUID_LEN) < 0 ||
+ hmac_sha256(uuid, UUID_LEN, (const u8 *) &t, sizeof(t), hash) < 0)
+ return -1;
+
+ os_memcpy(uuid, hash, UUID_LEN);
+
+ /* Version: 4 = random */
+ uuid[6] = (4 << 4) | (uuid[6] & 0x0f);
+
+ /* Variant specified in RFC 4122 */
+ uuid[8] = 0x80 | (uuid[8] & 0x3f);
+
+ return 0;
+}
diff --git a/src/utils/uuid.h b/src/utils/uuid.h
index 5e860cbc5936..6e20210f99b9 100644
--- a/src/utils/uuid.h
+++ b/src/utils/uuid.h
@@ -14,5 +14,6 @@
int uuid_str2bin(const char *str, u8 *bin);
int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
int is_nil_uuid(const u8 *uuid);
+int uuid_random(u8 *uuid);
#endif /* UUID_H */
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index f7acf6b9f698..a56462b8bbdc 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -13,7 +13,7 @@
#ifdef CONFIG_DEBUG_SYSLOG
#include <syslog.h>
-static int wpa_debug_syslog = 0;
+int wpa_debug_syslog = 0;
#endif /* CONFIG_DEBUG_SYSLOG */
#ifdef CONFIG_DEBUG_LINUX_TRACING
@@ -58,6 +58,10 @@ static int wpa_to_android_level(int level)
#ifndef CONFIG_NO_STDOUT_DEBUG
#ifdef CONFIG_DEBUG_FILE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
static FILE *out_file = NULL;
#endif /* CONFIG_DEBUG_FILE */
@@ -539,6 +543,8 @@ int wpa_debug_reopen_file(void)
int wpa_debug_open_file(const char *path)
{
#ifdef CONFIG_DEBUG_FILE
+ int out_fd;
+
if (!path)
return 0;
@@ -548,10 +554,28 @@ int wpa_debug_open_file(const char *path)
last_path = os_strdup(path);
}
- out_file = fopen(path, "a");
+ out_fd = open(path, O_CREAT | O_APPEND | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP);
+ if (out_fd < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: Failed to open output file descriptor, using standard output",
+ __func__);
+ return -1;
+ }
+
+#ifdef __linux__
+ if (fcntl(out_fd, F_SETFD, FD_CLOEXEC) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to set FD_CLOEXEC - continue without: %s",
+ __func__, strerror(errno));
+ }
+#endif /* __linux__ */
+
+ out_file = fdopen(out_fd, "a");
if (out_file == NULL) {
wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
"output file, using standard output");
+ close(out_fd);
return -1;
}
#ifndef _WIN32
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 17d8f963802e..1fe0b7db7482 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -14,6 +14,9 @@
extern int wpa_debug_level;
extern int wpa_debug_show_keys;
extern int wpa_debug_timestamp;
+#ifdef CONFIG_DEBUG_SYSLOG
+extern int wpa_debug_syslog;
+#endif /* CONFIG_DEBUG_SYSLOG */
/* Debugging function - conditional printf and hex dump. Driver wrappers can
* use these for debugging purposes. */
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
index 96cb25cc1764..77ee47288007 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -244,15 +244,13 @@ struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
if (a)
len += wpabuf_len(a);
- if (b)
- len += wpabuf_len(b);
+ len += wpabuf_len(b);
n = wpabuf_alloc(len);
if (n) {
if (a)
wpabuf_put_buf(n, a);
- if (b)
- wpabuf_put_buf(n, b);
+ wpabuf_put_buf(n, b);
}
wpabuf_free(a);
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
index 4916d29765f9..dae91fee46f6 100644
--- a/src/utils/xml-utils.c
+++ b/src/utils/xml-utils.c
@@ -246,10 +246,10 @@ static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out,
xml_node_create_text(ctx, tnds, NULL, "Path", uri);
val = get_val(ctx, node);
- if (val) {
- xml_node_create_text(ctx, tnds, NULL, "Value", val);
- xml_node_get_text_free(ctx, val);
- }
+ if (val || !xml_node_first_child(ctx, node))
+ xml_node_create_text(ctx, tnds, NULL, "Value",
+ val ? val : "");
+ xml_node_get_text_free(ctx, val);
new_uri = add_path(uri, name);
node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
diff --git a/src/wps/wps.c b/src/wps/wps.c
index fade6b6905dc..8d228270ff10 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -51,12 +51,11 @@ struct wps_data * wps_init(const struct wps_config *cfg)
}
if (cfg->pin) {
data->dev_pw_id = cfg->dev_pw_id;
- data->dev_password = os_malloc(cfg->pin_len);
+ data->dev_password = os_memdup(cfg->pin, cfg->pin_len);
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
- os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
data->dev_password_len = cfg->pin_len;
wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password",
data->dev_password, data->dev_password_len);
@@ -75,14 +74,12 @@ struct wps_data * wps_init(const struct wps_config *cfg)
data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
data->dev_password =
- os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+ os_memdup(wpabuf_head(cfg->wps->ap_nfc_dev_pw),
+ wpabuf_len(cfg->wps->ap_nfc_dev_pw));
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
- os_memcpy(data->dev_password,
- wpabuf_head(cfg->wps->ap_nfc_dev_pw),
- wpabuf_len(cfg->wps->ap_nfc_dev_pw));
data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password",
data->dev_password, data->dev_password_len);
@@ -124,15 +121,14 @@ struct wps_data * wps_init(const struct wps_config *cfg)
if (cfg->new_ap_settings) {
data->new_ap_settings =
- os_malloc(sizeof(*data->new_ap_settings));
+ os_memdup(cfg->new_ap_settings,
+ sizeof(*data->new_ap_settings));
if (data->new_ap_settings == NULL) {
bin_clear_free(data->dev_password,
data->dev_password_len);
os_free(data);
return NULL;
}
- os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
- sizeof(*data->new_ap_settings));
}
if (cfg->peer_addr)
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 2e3472177de9..bcae1ba5887b 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -654,6 +654,7 @@ int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey)
pub = wpabuf_zeropad(pub, 192);
if (pub == NULL) {
wpabuf_free(priv);
+ dh5_free(dh_ctx);
return -1;
}
wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index b840acd924a7..affd6a4af38a 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -322,11 +322,10 @@ static int wps_er_ap_use_cached_settings(struct wps_er *er,
if (!s)
return -1;
- ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
+ ap->ap_settings = os_memdup(&s->ap_settings, sizeof(*ap->ap_settings));
if (ap->ap_settings == NULL)
return -1;
- os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
return 0;
}
@@ -1958,10 +1957,9 @@ int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
}
os_free(ap->ap_settings);
- ap->ap_settings = os_malloc(sizeof(*cred));
+ ap->ap_settings = os_memdup(cred, sizeof(*cred));
if (ap->ap_settings == NULL)
return -1;
- os_memcpy(ap->ap_settings, cred, sizeof(*cred));
ap->ap_settings->cred_attr = NULL;
wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
"config request");
@@ -2018,10 +2016,9 @@ int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
}
os_free(ap->ap_settings);
- ap->ap_settings = os_malloc(sizeof(*cred));
+ ap->ap_settings = os_memdup(cred, sizeof(*cred));
if (ap->ap_settings == NULL)
return -1;
- os_memcpy(ap->ap_settings, cred, sizeof(*cred));
ap->ap_settings->cred_attr = NULL;
if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index fac8bd837f2f..379925e3f0a9 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -748,12 +748,11 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
p->wildcard_uuid = 1;
else
os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
- p->pin = os_malloc(pin_len);
+ p->pin = os_memdup(pin, pin_len);
if (p->pin == NULL) {
os_free(p);
return -1;
}
- os_memcpy(p->pin, pin, pin_len);
p->pin_len = pin_len;
if (timeout) {
@@ -881,6 +880,7 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
const u8 *uuid, size_t *pin_len)
{
struct wps_uuid_pin *pin, *found = NULL;
+ int wildcard = 0;
wps_registrar_expire_pins(reg);
@@ -900,7 +900,7 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
pin->wildcard_uuid == 2) {
wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
"PIN. Assigned it for this UUID-E");
- pin->wildcard_uuid++;
+ wildcard = 1;
os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
found = pin;
break;
@@ -922,6 +922,8 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
}
*pin_len = found->pin_len;
found->flags |= PIN_LOCKED;
+ if (wildcard)
+ found->wildcard_uuid++;
return found->pin;
}
@@ -1404,10 +1406,9 @@ static int wps_get_dev_password(struct wps_data *wps)
return -1;
}
- wps->dev_password = os_malloc(pin_len);
+ wps->dev_password = os_memdup(pin, pin_len);
if (wps->dev_password == NULL)
return -1;
- os_memcpy(wps->dev_password, pin, pin_len);
wps->dev_password_len = pin_len;
return 0;
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index a8d6a7f944e9..a6809956d86f 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -92,7 +92,10 @@ OBJS += eap_register.c
OBJS += src/utils/common.c
OBJS += src/utils/wpa_debug.c
OBJS += src/utils/wpabuf.c
+OBJS += src/utils/bitfield.c
OBJS += wmm_ac.c
+OBJS += op_classes.c
+OBJS += rrm.c
OBJS_p = wpa_passphrase.c
OBJS_p += src/utils/common.c
OBJS_p += src/utils/wpa_debug.c
@@ -221,8 +224,6 @@ ifdef CONFIG_MESH
NEED_80211_COMMON=y
NEED_SHA256=y
NEED_AES_SIV=y
-NEED_AES_OMAC1=y
-NEED_AES_CTR=y
CONFIG_SAE=y
CONFIG_AP=y
L_CFLAGS += -DCONFIG_MESH
@@ -238,6 +239,47 @@ NEED_ECC=y
NEED_DH_GROUPS=y
endif
+ifdef CONFIG_DPP
+L_CFLAGS += -DCONFIG_DPP
+OBJS += src/common/dpp.c
+OBJS += dpp_supplicant.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_SERVER=y
+NEED_BASE64=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
+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_MBO
+CONFIG_WNM=y
+endif
+
ifdef CONFIG_WNM
L_CFLAGS += -DCONFIG_WNM
OBJS += wnm_sta.c
@@ -254,15 +296,14 @@ ifdef CONFIG_TDLS_TESTING
L_CFLAGS += -DCONFIG_TDLS_TESTING
endif
-ifdef CONFIG_PEERKEY
-L_CFLAGS += -DCONFIG_PEERKEY
+ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+L_CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
endif
ifndef CONFIG_NO_WPA
OBJS += src/rsn_supp/wpa.c
OBJS += src/rsn_supp/preauth.c
OBJS += src/rsn_supp/pmksa_cache.c
-OBJS += src/rsn_supp/peerkey.c
OBJS += src/rsn_supp/wpa_ie.c
OBJS += src/common/wpa_common.c
NEED_AES=y
@@ -294,7 +335,6 @@ OBJS += src/p2p/p2p_invitation.c
OBJS += src/p2p/p2p_dev_disc.c
OBJS += src/p2p/p2p_group.c
OBJS += src/ap/p2p_hostapd.c
-OBJS += src/utils/bitfield.c
L_CFLAGS += -DCONFIG_P2P
NEED_GAS=y
NEED_OFFCHANNEL=y
@@ -640,6 +680,7 @@ L_CFLAGS += -DEAP_PWD
OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
CONFIG_IEEE8021X_EAPOL=y
NEED_SHA256=y
+NEED_ECC=y
endif
ifdef CONFIG_EAP_EKE
@@ -811,13 +852,20 @@ OBJS += src/ap/ieee802_11_ht.c
ifdef CONFIG_IEEE80211AC
OBJS += src/ap/ieee802_11_vht.c
endif
+ifdef CONFIG_IEEE80211AX
+OBJS += src/ap/ieee802_11_he.c
endif
-ifdef CONFIG_WNM
+endif
+ifdef CONFIG_WNM_AP
+L_CFLAGS += -DCONFIG_WNM_AP
OBJS += src/ap/wnm_ap.c
endif
ifdef CONFIG_MBO
OBJS += src/ap/mbo_ap.c
endif
+ifdef CONFIG_FILS
+OBJS += src/ap/fils_hlp.c
+endif
ifdef CONFIG_CTRL_IFACE
OBJS += src/ap/ctrl_iface_ap.c
endif
@@ -832,11 +880,9 @@ L_CFLAGS += -DCONFIG_IEEE80211N
ifdef CONFIG_IEEE80211AC
L_CFLAGS += -DCONFIG_IEEE80211AC
endif
+ifdef CONFIG_IEEE80211AX
+L_CFLAGS += -DCONFIG_IEEE80211AX
endif
-
-ifdef CONFIG_MBO
-OBJS += mbo.c
-L_CFLAGS += -DCONFIG_MBO
endif
ifdef NEED_AP_MLME
@@ -852,6 +898,10 @@ L_CFLAGS += -DEAP_SERVER_WSC
OBJS += src/ap/wps_hostapd.c
OBJS += src/eap_server/eap_server_wsc.c
endif
+ifdef CONFIG_DPP
+OBJS += src/ap/dpp_hostapd.c
+OBJS += src/ap/gas_query_ap.c
+endif
ifdef CONFIG_INTERWORKING
OBJS += src/ap/gas_serv.c
endif
@@ -860,18 +910,21 @@ OBJS += src/ap/hs20.c
endif
endif
+ifdef CONFIG_MBO
+OBJS += mbo.c
+L_CFLAGS += -DCONFIG_MBO
+endif
+
+ifdef CONFIG_TESTING_OPTIONS
+L_CFLAGS += -DCONFIG_TESTING_OPTIONS
+endif
+
ifdef NEED_RSN_AUTHENTICATOR
L_CFLAGS += -DCONFIG_NO_RADIUS
NEED_AES_WRAP=y
OBJS += src/ap/wpa_auth.c
OBJS += src/ap/wpa_auth_ie.c
OBJS += src/ap/pmksa_cache_auth.c
-ifdef CONFIG_IEEE80211R
-OBJS += src/ap/wpa_auth_ft.c
-endif
-ifdef CONFIG_PEERKEY
-OBJS += src/ap/peerkey_auth.c
-endif
endif
ifdef CONFIG_ACS
@@ -971,25 +1024,40 @@ ifdef CONFIG_TLS_ADD_DL
LIBS += -ldl
LIBS_p += -ldl
endif
+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
-OBJS_p += src/crypto/crypto_gnutls.c
+OBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c
+OBJS_p += 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_p += -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
@@ -1131,6 +1199,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
@@ -1163,9 +1237,6 @@ ifdef CONFIG_INTERNAL_AES
AESOBJS += src/crypto/aes-internal-enc.c
endif
endif
-ifdef NEED_AES_SIV
-AESOBJS += src/crypto/aes-siv.c
-endif
ifdef NEED_AES
OBJS += $(AESOBJS)
endif
@@ -1173,8 +1244,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
@@ -1200,9 +1273,11 @@ endif
MD5OBJS =
ifndef CONFIG_FIPS
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), gnutls)
MD5OBJS += src/crypto/md5.c
endif
endif
+endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
MD5OBJS += src/crypto/md5-internal.c
@@ -1240,8 +1315,10 @@ SHA256OBJS = # none by default
ifdef NEED_SHA256
L_CFLAGS += -DCONFIG_SHA256
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), gnutls)
SHA256OBJS += src/crypto/sha256.c
endif
+endif
SHA256OBJS += src/crypto/sha256-prf.c
ifdef CONFIG_INTERNAL_SHA256
SHA256OBJS += src/crypto/sha256-internal.c
@@ -1261,12 +1338,34 @@ ifdef NEED_HMAC_SHA256_KDF
L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF
SHA256OBJS += src/crypto/sha256-kdf.c
endif
+ifdef NEED_HMAC_SHA384_KDF
+L_CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+SHA256OBJS += src/crypto/sha384-kdf.c
+endif
+ifdef NEED_HMAC_SHA512_KDF
+L_CFLAGS += -DCONFIG_HMAC_SHA512_KDF
+SHA256OBJS += src/crypto/sha512-kdf.c
+endif
OBJS += $(SHA256OBJS)
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), gnutls)
+OBJS += src/crypto/sha512.c
+endif
+endif
+OBJS += src/crypto/sha512-prf.c
+endif
ifdef NEED_DH_GROUPS
OBJS += src/crypto/dh_groups.c
@@ -1490,6 +1589,12 @@ OBJS += src/utils/ext_password.c
L_CFLAGS += -DCONFIG_EXT_PASSWORD
endif
+ifdef NEED_GAS_SERVER
+OBJS += src/common/gas_server.c
+L_CFLAGS += -DCONFIG_GAS_SERVER
+NEED_GAS=y
+endif
+
ifdef NEED_GAS
OBJS += src/common/gas.c
OBJS += gas_query.c
@@ -1502,6 +1607,11 @@ OBJS += offchannel.c
L_CFLAGS += -DCONFIG_OFFCHANNEL
endif
+ifdef NEED_JSON
+OBJS += src/utils/json.c
+L_CFLAGS += -DCONFIG_JSON
+endif
+
OBJS += src/drivers/driver_common.c
OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c
@@ -1580,9 +1690,7 @@ endif
# With BoringSSL we need libkeystore-engine in order to provide access to
# keystore keys.
-ifneq (,$(wildcard external/boringssl/flavor.mk))
LOCAL_SHARED_LIBRARIES += libkeystore-engine
-endif
ifdef CONFIG_DRIVER_NL80211
ifneq ($(wildcard external/libnl),)
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index f28055f4093e..bf4daaa4cb1e 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,75 @@
ChangeLog for wpa_supplicant
+2018-12-02 - v2.7
+ * fixed WPA packet number reuse with replayed messages and key
+ reinstallation
+ [https://w1.fi/security/2017-1/] (CVE-2017-13077, CVE-2017-13078,
+ CVE-2017-13079, CVE-2017-13080, CVE-2017-13081, CVE-2017-13082,
+ CVE-2017-13086, CVE-2017-13087, CVE-2017-13088)
+ * fixed unauthenticated EAPOL-Key decryption in wpa_supplicant
+ [https://w1.fi/security/2018-1/] (CVE-2018-14526)
+ * 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)
+ * added support for RSA 3k key case with Suite B 192-bit level
+ * fixed Suite B PMKSA caching not to update PMKID during each 4-way
+ handshake
+ * fixed EAP-pwd pre-processing with PasswordHashHash
+ * added EAP-pwd client support for salted passwords
+ * fixed a regression in TDLS prohibited bit validation
+ * started to use estimated throughput to avoid undesired signal
+ strength based roaming decision
+ * MACsec/MKA:
+ - new macsec_linux driver interface support for the Linux
+ kernel macsec module
+ - number of fixes and extensions
+ * added support for external persistent storage of PMKSA cache
+ (PMKSA_GET/PMKSA_ADD control interface commands; and
+ MESH_PMKSA_GET/MESH_PMKSA_SET for the mesh case)
+ * fixed mesh channel configuration pri/sec switch case
+ * added support for beacon report
+ * large number of other fixes, cleanup, and extensions
+ * added support for randomizing local address for GAS queries
+ (gas_rand_mac_addr parameter)
+ * fixed EAP-SIM/AKA/AKA' ext auth cases within TLS tunnel
+ * added option for using random WPS UUID (auto_uuid=1)
+ * added SHA256-hash support for OCSP certificate matching
+ * fixed EAP-AKA' to add AT_KDF into Synchronization-Failure
+ * fixed a regression in RSN pre-authentication candidate selection
+ * added option to configure allowed group management cipher suites
+ (group_mgmt network profile parameter)
+ * removed all PeerKey functionality
+ * fixed nl80211 AP and mesh mode configuration regression with
+ Linux 4.15 and newer
+ * added ap_isolate configuration option for AP mode
+ * added support for nl80211 to offload 4-way handshake into the driver
+ * added support for using wolfSSL cryptographic library
+ * SAE
+ - added support for configuring SAE password separately of the
+ WPA2 PSK/passphrase
+ - 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
+ - fixed FT-SAE PMKID matching
+ * Hotspot 2.0
+ - added support for fetching of Operator Icon Metadata ANQP-element
+ - 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 fetching Venue URL information
+ * added support for using OpenSSL 1.1.1
+ * FT
+ - disabled PMKSA caching with FT since it is not fully functional
+ - added support for SHA384 based AKM
+ - added support for BIP ciphers BIP-CMAC-256, BIP-GMAC-128,
+ BIP-GMAC-256 in addition to previously supported BIP-CMAC-128
+ - fixed additional IE inclusion in Reassociation Request frame when
+ using FT protocol
+
2016-10-02 - v2.6
* fixed WNM Sleep Mode processing when PMF is not enabled
[http://w1.fi/security/2015-6/] (CVE-2015-5310)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index f3e86c1de6c0..c2e93e20b58a 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -103,6 +103,9 @@ OBJS += eap_register.o
OBJS += ../src/utils/common.o
OBJS += ../src/utils/wpa_debug.o
OBJS += ../src/utils/wpabuf.o
+OBJS += ../src/utils/bitfield.o
+OBJS += op_classes.o
+OBJS += rrm.o
OBJS_p = wpa_passphrase.o
OBJS_p += ../src/utils/common.o
OBJS_p += ../src/utils/wpa_debug.o
@@ -254,8 +257,6 @@ ifdef CONFIG_MESH
NEED_80211_COMMON=y
NEED_SHA256=y
NEED_AES_SIV=y
-NEED_AES_OMAC1=y
-NEED_AES_CTR=y
CONFIG_SAE=y
CONFIG_AP=y
CFLAGS += -DCONFIG_MESH
@@ -271,6 +272,47 @@ NEED_ECC=y
NEED_DH_GROUPS=y
endif
+ifdef CONFIG_DPP
+CFLAGS += -DCONFIG_DPP
+OBJS += ../src/common/dpp.o
+OBJS += dpp_supplicant.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_SERVER=y
+NEED_BASE64=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
+NEED_SHA384=y
+NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
+endif
+
+ifdef CONFIG_MBO
+CONFIG_WNM=y
+endif
+
ifdef CONFIG_WNM
CFLAGS += -DCONFIG_WNM
OBJS += wnm_sta.o
@@ -287,15 +329,14 @@ ifdef CONFIG_TDLS_TESTING
CFLAGS += -DCONFIG_TDLS_TESTING
endif
-ifdef CONFIG_PEERKEY
-CFLAGS += -DCONFIG_PEERKEY
+ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
endif
ifndef CONFIG_NO_WPA
OBJS += ../src/rsn_supp/wpa.o
OBJS += ../src/rsn_supp/preauth.o
OBJS += ../src/rsn_supp/pmksa_cache.o
-OBJS += ../src/rsn_supp/peerkey.o
OBJS += ../src/rsn_supp/wpa_ie.o
OBJS += ../src/common/wpa_common.o
NEED_AES=y
@@ -335,7 +376,6 @@ OBJS += ../src/p2p/p2p_invitation.o
OBJS += ../src/p2p/p2p_dev_disc.o
OBJS += ../src/p2p/p2p_group.o
OBJS += ../src/ap/p2p_hostapd.o
-OBJS += ../src/utils/bitfield.o
CFLAGS += -DCONFIG_P2P
NEED_GAS=y
NEED_OFFCHANNEL=y
@@ -665,9 +705,13 @@ endif
ifdef CONFIG_EAP_PWD
CFLAGS += -DEAP_PWD
+ifeq ($(CONFIG_TLS), wolfssl)
+CFLAGS += -DCONFIG_ECC
+endif
OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
CONFIG_IEEE8021X_EAPOL=y
NEED_SHA256=y
+NEED_ECC=y
endif
ifdef CONFIG_EAP_EKE
@@ -794,20 +838,9 @@ endif
endif
endif
-ifdef CONFIG_IEEE8021X_EAPOL
-# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
-CFLAGS += -DIEEE8021X_EAPOL
-OBJS += ../src/eapol_supp/eapol_supp_sm.o
-OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o
-NEED_EAP_COMMON=y
-ifdef CONFIG_DYNAMIC_EAP_METHODS
-CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
-LIBS += -ldl -rdynamic
-endif
-endif
-
ifdef CONFIG_MACSEC
CFLAGS += -DCONFIG_MACSEC
+CONFIG_IEEE8021X_EAPOL=y
NEED_AES_ENCBLOCK=y
NEED_AES_UNWRAP=y
NEED_AES_WRAP=y
@@ -819,6 +852,18 @@ OBJS += ../src/pae/ieee802_1x_key.o
OBJS += ../src/pae/ieee802_1x_secy_ops.o
endif
+ifdef CONFIG_IEEE8021X_EAPOL
+# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
+CFLAGS += -DIEEE8021X_EAPOL
+OBJS += ../src/eapol_supp/eapol_supp_sm.o
+OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o
+NEED_EAP_COMMON=y
+ifdef CONFIG_DYNAMIC_EAP_METHODS
+CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+LIBS += -ldl -rdynamic
+endif
+endif
+
ifdef CONFIG_AP
NEED_EAP_COMMON=y
NEED_RSN_AUTHENTICATOR=y
@@ -852,13 +897,20 @@ OBJS += ../src/ap/ieee802_11_ht.o
ifdef CONFIG_IEEE80211AC
OBJS += ../src/ap/ieee802_11_vht.o
endif
+ifdef CONFIG_IEEE80211AX
+OBJS += ../src/ap/ieee802_11_he.o
endif
-ifdef CONFIG_WNM
+endif
+ifdef CONFIG_WNM_AP
+CFLAGS += -DCONFIG_WNM_AP
OBJS += ../src/ap/wnm_ap.o
endif
ifdef CONFIG_MBO
OBJS += ../src/ap/mbo_ap.o
endif
+ifdef CONFIG_FILS
+OBJS += ../src/ap/fils_hlp.o
+endif
ifdef CONFIG_CTRL_IFACE
OBJS += ../src/ap/ctrl_iface_ap.o
endif
@@ -873,11 +925,9 @@ CFLAGS += -DCONFIG_IEEE80211N
ifdef CONFIG_IEEE80211AC
CFLAGS += -DCONFIG_IEEE80211AC
endif
+ifdef CONFIG_IEEE80211AX
+CFLAGS += -DCONFIG_IEEE80211AX
endif
-
-ifdef CONFIG_MBO
-OBJS += mbo.o
-CFLAGS += -DCONFIG_MBO
endif
ifdef NEED_AP_MLME
@@ -893,6 +943,10 @@ CFLAGS += -DEAP_SERVER_WSC
OBJS += ../src/ap/wps_hostapd.o
OBJS += ../src/eap_server/eap_server_wsc.o
endif
+ifdef CONFIG_DPP
+OBJS += ../src/ap/dpp_hostapd.o
+OBJS += ../src/ap/gas_query_ap.o
+endif
ifdef CONFIG_INTERWORKING
OBJS += ../src/ap/gas_serv.o
endif
@@ -901,18 +955,17 @@ OBJS += ../src/ap/hs20.o
endif
endif
+ifdef CONFIG_MBO
+OBJS += mbo.o
+CFLAGS += -DCONFIG_MBO
+endif
+
ifdef NEED_RSN_AUTHENTICATOR
CFLAGS += -DCONFIG_NO_RADIUS
NEED_AES_WRAP=y
OBJS += ../src/ap/wpa_auth.o
OBJS += ../src/ap/wpa_auth_ie.o
OBJS += ../src/ap/pmksa_cache_auth.o
-ifdef CONFIG_IEEE80211R
-OBJS += ../src/ap/wpa_auth_ft.o
-endif
-ifdef CONFIG_PEERKEY
-OBJS += ../src/ap/peerkey_auth.o
-endif
endif
ifdef CONFIG_ACS
@@ -996,6 +1049,21 @@ CFLAGS += -DCONFIG_TLSV12
NEED_SHA256=y
endif
+ifeq ($(CONFIG_TLS), wolfssl)
+ifdef TLS_FUNCS
+CFLAGS += -DWOLFSSL_DER_LOAD -I/usr/local/include/wolfssl
+OBJS += ../src/crypto/tls_wolfssl.o
+endif
+OBJS += ../src/crypto/crypto_wolfssl.o
+OBJS_p += ../src/crypto/crypto_wolfssl.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_wolfssl.o
+endif
+NEED_TLS_PRF_SHA256=y
+LIBS += -lwolfssl -lm
+LIBS_p += -lwolfssl -lm
+endif
+
ifeq ($(CONFIG_TLS), openssl)
ifdef TLS_FUNCS
CFLAGS += -DEAP_TLS_OPENSSL
@@ -1017,26 +1085,41 @@ ifdef CONFIG_TLS_ADD_DL
LIBS += -ldl
LIBS_p += -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
-OBJS_p += ../src/crypto/crypto_gnutls.o
-OBJS_priv += ../src/crypto/crypto_gnutls.o
+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
+OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
+OBJS_priv += ../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_p += -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
@@ -1119,6 +1202,48 @@ CONFIG_INTERNAL_RC4=y
endif
endif
+ifeq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/crypto_linux.o
+OBJS_p += ../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_client.o
+OBJS += ../src/tls/tlsv1_client_write.o
+OBJS += ../src/tls/tlsv1_client_read.o
+OBJS += ../src/tls/tlsv1_client_ocsp.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_CLIENT
+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
@@ -1159,8 +1284,10 @@ AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o
endif
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), wolfssl)
NEED_INTERNAL_AES_WRAP=y
endif
+endif
ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
# Seems to be needed at least with BoringSSL
NEED_INTERNAL_AES_WRAP=y
@@ -1173,11 +1300,19 @@ NEED_INTERNAL_AES_WRAP=y
endif
ifdef NEED_INTERNAL_AES_WRAP
+ifneq ($(CONFIG_TLS), linux)
AESOBJS += ../src/crypto/aes-unwrap.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
@@ -1190,11 +1325,12 @@ NEED_AES_ENC=y
ifdef CONFIG_OPENSSL_CMAC
CFLAGS += -DCONFIG_OPENSSL_CMAC
else
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), wolfssl)
AESOBJS += ../src/crypto/aes-omac1.o
endif
endif
-ifdef NEED_AES_SIV
-AESOBJS += ../src/crypto/aes-siv.o
+endif
endif
ifdef NEED_AES_WRAP
NEED_AES_ENC=y
@@ -1205,9 +1341,13 @@ endif
ifdef NEED_AES_CBC
NEED_AES_ENC=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_ENC
ifdef CONFIG_INTERNAL_AES
AESOBJS += ../src/crypto/aes-internal-enc.o
@@ -1219,8 +1359,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
@@ -1232,9 +1378,11 @@ ifdef CONFIG_NO_WPA_PASSPHRASE
CFLAGS += -DCONFIG_NO_PBKDF2
else
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), wolfssl)
SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
endif
endif
+endif
ifdef NEED_T_PRF
SHA1OBJS += ../src/crypto/sha1-tprf.o
endif
@@ -1245,9 +1393,15 @@ endif
ifndef CONFIG_FIPS
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+ifneq ($(CONFIG_TLS), wolfssl)
MD5OBJS += ../src/crypto/md5.o
endif
endif
+endif
+endif
+endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
MD5OBJS += ../src/crypto/md5-internal.o
@@ -1265,6 +1419,9 @@ endif
DESOBJS = # none needed when not internal
ifdef NEED_DES
+ifndef CONFIG_FIPS
+CFLAGS += -DCONFIG_DES
+endif
ifdef CONFIG_INTERNAL_DES
DESOBJS += ../src/crypto/des-internal.o
endif
@@ -1286,8 +1443,14 @@ SHA256OBJS = # none by default
ifdef NEED_SHA256
CFLAGS += -DCONFIG_SHA256
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+ifneq ($(CONFIG_TLS), wolfssl)
SHA256OBJS += ../src/crypto/sha256.o
endif
+endif
+endif
+endif
SHA256OBJS += ../src/crypto/sha256-prf.o
ifdef CONFIG_INTERNAL_SHA256
SHA256OBJS += ../src/crypto/sha256-internal.o
@@ -1307,12 +1470,42 @@ ifdef NEED_HMAC_SHA256_KDF
CFLAGS += -DCONFIG_HMAC_SHA256_KDF
OBJS += ../src/crypto/sha256-kdf.o
endif
+ifdef NEED_HMAC_SHA384_KDF
+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+OBJS += ../src/crypto/sha384-kdf.o
+endif
+ifdef NEED_HMAC_SHA512_KDF
+CFLAGS += -DCONFIG_HMAC_SHA512_KDF
+OBJS += ../src/crypto/sha512-kdf.o
+endif
OBJS += $(SHA256OBJS)
endif
ifdef NEED_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
CFLAGS += -DCONFIG_SHA384
OBJS += ../src/crypto/sha384-prf.o
endif
+ifdef NEED_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
+CFLAGS += -DCONFIG_SHA512
+OBJS += ../src/crypto/sha512-prf.o
+endif
ifdef NEED_DH_GROUPS
OBJS += ../src/crypto/dh_groups.o
@@ -1506,9 +1699,11 @@ endif
ifdef CONFIG_FIPS
CFLAGS += -DCONFIG_FIPS
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), wolfssl)
$(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
endif
endif
+endif
OBJS += $(SHA1OBJS) $(DESOBJS)
@@ -1561,6 +1756,12 @@ OBJS += ../src/utils/ext_password.o
CFLAGS += -DCONFIG_EXT_PASSWORD
endif
+ifdef NEED_GAS_SERVER
+OBJS += ../src/common/gas_server.o
+CFLAGS += -DCONFIG_GAS_SERVER
+NEED_GAS=y
+endif
+
ifdef NEED_GAS
OBJS += ../src/common/gas.o
OBJS += gas_query.o
@@ -1573,6 +1774,11 @@ OBJS += offchannel.o
CFLAGS += -DCONFIG_OFFCHANNEL
endif
+ifdef NEED_JSON
+OBJS += ../src/utils/json.o
+CFLAGS += -DCONFIG_JSON
+endif
+
ifdef CONFIG_MODULE_TESTS
CFLAGS += -DCONFIG_MODULE_TESTS
OBJS += wpas_module_tests.o
@@ -1582,9 +1788,6 @@ OBJS += ../src/crypto/crypto_module_tests.o
ifdef CONFIG_WPS
OBJS += ../src/wps/wps_module_tests.o
endif
-ifndef CONFIG_P2P
-OBJS += ../src/utils/bitfield.o
-endif
endif
OBJS += ../src/drivers/driver_common.o
@@ -1698,7 +1901,7 @@ preauth_test: $(OBJS_t2)
@$(E) " LD " $@
wpa_passphrase: $(OBJS_p)
- $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
+ $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
@$(E) " LD " $@
wpa_cli: $(OBJS_c)
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index 11ab01a9c171..2a3265f21eaa 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -1,7 +1,7 @@
WPA Supplicant
==============
-Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
@@ -83,7 +83,7 @@ Supported WPA/IEEE 802.11i features:
authentication)
(following methods are supported, but since they do not generate keying
material, they cannot be used with WPA or IEEE 802.1X WEP keying)
- * EAP-MD5-Challenge
+ * EAP-MD5-Challenge
* EAP-MSCHAPv2
* EAP-GTC
* EAP-OTP
@@ -965,6 +965,17 @@ wpa_priv can control multiple interface with one process, but it is
also possible to run multiple wpa_priv processes at the same time, if
desired.
+It should be noted that the interface used between wpa_supplicant and
+wpa_priv does not include all the capabilities of the wpa_supplicant
+driver interface and at times, this interface lacks update especially
+for recent addition. Consequently, use of wpa_priv does come with the
+price of somewhat reduced available functionality. The next section
+describing how wpa_supplicant can be used with reduced privileges
+without having to handle the complexity of separate wpa_priv. While that
+approve does not provide separation for network admin capabilities, it
+does allow other root privileges to be dropped without the drawbacks of
+the wpa_priv process.
+
Linux capabilities instead of privileged process
------------------------------------------------
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index e4eed2074f91..334287101c92 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -197,6 +197,20 @@ Credentials can be pre-configured for automatic network selection:
# pre-configured with the credential since the NAI Realm information
# may not be available or fetched.
#
+# required_roaming_consortium: Required Roaming Consortium OI
+# If required_roaming_consortium_len is non-zero, this field contains the
+# Roaming Consortium OI that is required to be advertised by the AP for
+# the credential to be considered matching.
+#
+# roaming_consortiums: Roaming Consortium OI(s) memberships
+# This string field contains one or more comma delimited OIs (hexdump)
+# identifying the roaming consortiums of which the provider is a member.
+# The list is sorted from the most preferred one to the least preferred
+# one. A match between the Roaming Consortium OIs advertised by an AP and
+# the OIs in this list indicates that successful authentication is
+# possible.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/RoamingConsortiumOI)
+#
# eap: Pre-configured EAP method
# This optional field can be used to specify which EAP method will be
# used with this credential. If not set, the EAP method is selected
@@ -295,6 +309,7 @@ Credentials can be pre-configured for automatic network selection:
# ca_cert="/etc/wpa_supplicant/ca.pem"
# domain="example.com"
# roaming_consortium=223344
+# roaming_consortiums="112233,4455667788,aabbcc"
# eap=TTLS
# phase2="auth=MSCHAPV2"
#}
@@ -591,7 +606,7 @@ network={
Hotspot 2.0 connection with external network selection
------------------------------------------------------
-When an component controlling wpa_supplicant takes care of Interworking
+When a component controlling wpa_supplicant takes care of Interworking
network selection, following configuration and network profile
parameters can be used to configure a temporary network profile for a
Hotspot 2.0 connection (e.g., with SET, ADD_NETWORK, SET_NETWORK, and
@@ -613,6 +628,7 @@ network={
eap=TTLS
phase2="auth=MSCHAPV2"
update_identifier=54321
+ roaming_consortium_selection=112233
#ocsp=2
}
@@ -628,4 +644,5 @@ update_identifier: PPS/UpdateIdentifier
ca_cert: from the downloaded trust root based on PPS information
eap: Credential/UsernamePassword/EAPMethod or NAI Realm list
phase2: Credential/UsernamePassword/EAPMethod or NAI Realm list
+roaming_consortium_selection: Matching OI from HomeSP/RoamingConsortiumOI
ocsp: Credential/CheckAAAServerCertStatus
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 02505bb991aa..c97f591311d3 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -1,9 +1,9 @@
# Example wpa_supplicant build time configuration
#
# This file lists the configuration options that are used when building the
-# hostapd binary. All lines starting with # are ignored. Configuration option
-# lines must be commented out complete, if they are not to be included, i.e.,
-# just setting VARIABLE=n is not disabling that variable.
+# wpa_supplicant binary. All lines starting with # are ignored. Configuration
+# option lines must be commented out complete, if they are not to be included,
+# i.e., just setting VARIABLE=n is not disabling that variable.
#
# This file is included in Makefile, so variables like CFLAGS and LIBS can also
# be modified from here. In most cases, these lines should use += in order not
@@ -91,10 +91,9 @@ CONFIG_EAP_PEAP=y
CONFIG_EAP_TTLS=y
# EAP-FAST
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch)
-# to add the needed functions.
+# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
+# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
+# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
#CONFIG_EAP_FAST=y
# EAP-GTC
@@ -152,6 +151,9 @@ CONFIG_WPS_NFC=y
# EAP-IKEv2
#CONFIG_EAP_IKEV2=y
+# EAP-EKE
+#CONFIG_EAP_EKE=y
+
# PKCS#12 (PFX) support (used to read private key and certificate file from
# a file that usually has extension .p12 or .pfx)
CONFIG_PKCS12=y
@@ -176,8 +178,10 @@ CONFIG_SMARTCARD=y
# Select control interface backend for external programs, e.g, wpa_cli:
# unix = UNIX domain sockets (default for Linux/*BSD)
# udp = UDP sockets using localhost (127.0.0.1)
+# udp6 = UDP IPv6 sockets using localhost (::1)
# named_pipe = Windows Named Pipe (default for Windows)
# udp-remote = UDP sockets with remote access (only for tests systems/purpose)
+# udp6-remote = UDP IPv6 sockets with remote access (only for tests purpose)
# y = use default (backwards compatibility)
# If this option is commented out, control interface is not included in the
# build.
@@ -254,6 +258,9 @@ CONFIG_ELOOP=eloop
# Should we use epoll instead of select? Select is used by default.
#CONFIG_ELOOP_EPOLL=y
+# Should we use kqueue instead of select? Select is used by default.
+#CONFIG_ELOOP_KQUEUE=y
+
# Select layer 2 packet implementation
# linux = Linux packet socket (default)
# pcap = libpcap/libdnet/WinPcap
@@ -263,8 +270,11 @@ CONFIG_ELOOP=eloop
# none = Empty template
CONFIG_L2_PACKET=linux
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
+# Disable Linux packet socket workaround applicable for station interface
+# in a bridge for EAPOL frames. This should be uncommented only if the kernel
+# is known to not have the regression issue in packet socket behavior with
+# bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
+#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
# IEEE 802.11w (management frame protection), also known as PMF
# Driver support is also needed for IEEE 802.11w.
@@ -291,6 +301,10 @@ CONFIG_IEEE80211W=y
# will be used)
#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
@@ -349,7 +363,7 @@ CONFIG_IEEE80211W=y
# amount of memory/flash.
#CONFIG_DYNAMIC_EAP_METHODS=y
-# IEEE Std 802.11r-2008 (Fast BSS Transition)
+# IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode
CONFIG_IEEE80211R=y
# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
@@ -424,11 +438,21 @@ CONFIG_ANDROID_LOG=y
# disabled. This will save some in binary size and CPU use. However, this
# should only be considered for builds that are known to be used on devices
# that meet the requirements described above.
-#CONFIG_NO_RANDOM_POOL=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
# IEEE 802.11n (High Throughput) support (mainly for AP mode)
CONFIG_IEEE80211N=y
+# IEEE 802.11ac (Very High Throughput) support (mainly for AP mode)
+# (depends on CONFIG_IEEE80211N)
+#CONFIG_IEEE80211AC=y
+
# Wireless Network Management (IEEE Std 802.11v-2011)
# Note: This is experimental and not complete implementation.
CONFIG_WNM=y
@@ -442,6 +466,9 @@ CONFIG_INTERWORKING=y
# Hotspot 2.0
CONFIG_HS20=y
+# Enable interface matching in wpa_supplicant
+#CONFIG_MATCH_IFACE=y
+
# Disable roaming in wpa_supplicant
CONFIG_NO_ROAMING=y
@@ -489,4 +516,36 @@ CONFIG_WIFI_DISPLAY=y
# Support Multi Band Operation
#CONFIG_MBO=y
+# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
+# Note: This is an experimental and not yet complete implementation. This
+# should not be enabled for production use.
+#CONFIG_FILS=y
+
+# Support RSN on IBSS networks
+# This is needed to be able to use mode=1 network profile with proto=RSN and
+# key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None).
+#CONFIG_IBSS_RSN=y
+
+# External PMKSA cache control
+# This can be used to enable control interface commands that allow the current
+# PMKSA cache entries to be fetched and new entries to be added.
+#CONFIG_PMKSA_CACHE_EXTERNAL=y
+
+# Mesh Networking (IEEE 802.11s)
+#CONFIG_MESH=y
+
+# Background scanning modules
+# These can be used to request wpa_supplicant to perform background scanning
+# operations for roaming within an ESS (same SSID). See the bgscan parameter in
+# the wpa_supplicant.conf file for more details.
+# Periodic background scans based on signal strength
+#CONFIG_BGSCAN_SIMPLE=y
+# Learn channels used by the network and try to avoid bgscans on other
+# channels (experimental)
+#CONFIG_BGSCAN_LEARN=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
+
include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 5afb772ba192..ea846a0fad4b 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -46,23 +46,50 @@ static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
#ifdef CONFIG_IEEE80211N
static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
struct hostapd_config *conf,
struct hostapd_hw_modes *mode)
{
#ifdef CONFIG_P2P
u8 center_chan = 0;
u8 channel = conf->channel;
+#endif /* CONFIG_P2P */
if (!conf->secondary_channel)
goto no_vht;
+ /* Use the maximum oper channel width if it's given. */
+ if (ssid->max_oper_chwidth)
+ conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+
+ ieee80211_freq_to_chan(ssid->vht_center_freq2,
+ &conf->vht_oper_centr_freq_seg1_idx);
+
+ if (!ssid->p2p_group) {
+ if (!ssid->vht_center_freq1 ||
+ conf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+ goto no_vht;
+ ieee80211_freq_to_chan(ssid->vht_center_freq1,
+ &conf->vht_oper_centr_freq_seg0_idx);
+ wpa_printf(MSG_DEBUG, "VHT seg0 index %d for AP",
+ conf->vht_oper_centr_freq_seg0_idx);
+ return;
+ }
+
+#ifdef CONFIG_P2P
switch (conf->vht_oper_chwidth) {
case VHT_CHANWIDTH_80MHZ:
case VHT_CHANWIDTH_80P80MHZ:
center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+ wpa_printf(MSG_DEBUG,
+ "VHT center channel %u for 80 or 80+80 MHz bandwidth",
+ center_chan);
break;
case VHT_CHANWIDTH_160MHZ:
center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+ wpa_printf(MSG_DEBUG,
+ "VHT center channel %u for 160 MHz bandwidth",
+ center_chan);
break;
default:
/*
@@ -72,10 +99,17 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
*/
conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
- if (!center_chan) {
+ if (center_chan) {
+ wpa_printf(MSG_DEBUG,
+ "VHT center channel %u for auto-selected 160 MHz bandwidth",
+ center_chan);
+ } else {
conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
channel);
+ wpa_printf(MSG_DEBUG,
+ "VHT center channel %u for auto-selected 80 MHz bandwidth",
+ center_chan);
}
break;
}
@@ -83,15 +117,17 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
goto no_vht;
conf->vht_oper_centr_freq_seg0_idx = center_chan;
+ wpa_printf(MSG_DEBUG, "VHT seg0 index %d for P2P GO",
+ conf->vht_oper_centr_freq_seg0_idx);
return;
+#endif /* CONFIG_P2P */
no_vht:
- conf->vht_oper_centr_freq_seg0_idx =
- channel + conf->secondary_channel * 2;
-#else /* CONFIG_P2P */
+ wpa_printf(MSG_DEBUG,
+ "No VHT higher bandwidth support for the selected channel %d",
+ conf->channel);
conf->vht_oper_centr_freq_seg0_idx =
conf->channel + conf->secondary_channel * 2;
-#endif /* CONFIG_P2P */
conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
}
#endif /* CONFIG_IEEE80211N */
@@ -123,6 +159,11 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
if (wpa_s->hw.modes) {
struct hostapd_hw_modes *mode = NULL;
int i, no_ht = 0;
+
+ wpa_printf(MSG_DEBUG,
+ "Determining HT/VHT options based on driver capabilities (freq=%u chan=%u)",
+ ssid->frequency, conf->channel);
+
for (i = 0; i < wpa_s->hw.num_modes; i++) {
if (wpa_s->hw.modes[i].mode == conf->hw_mode) {
mode = &wpa_s->hw.modes[i];
@@ -131,27 +172,54 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
}
#ifdef CONFIG_HT_OVERRIDES
- if (ssid->disable_ht) {
+ if (ssid->disable_ht)
+ ssid->ht = 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+ if (!ssid->ht) {
+ wpa_printf(MSG_DEBUG,
+ "HT not enabled in network profile");
conf->ieee80211n = 0;
conf->ht_capab = 0;
no_ht = 1;
}
-#endif /* CONFIG_HT_OVERRIDES */
if (!no_ht && mode && mode->ht_capab) {
+ wpa_printf(MSG_DEBUG,
+ "Enable HT support (p2p_group=%d 11a=%d ht40_hw_capab=%d ssid->ht40=%d)",
+ ssid->p2p_group,
+ conf->hw_mode == HOSTAPD_MODE_IEEE80211A,
+ !!(mode->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET),
+ ssid->ht40);
conf->ieee80211n = 1;
#ifdef CONFIG_P2P
- if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
+ if (ssid->p2p_group &&
+ conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
(mode->ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
- ssid->ht40)
+ ssid->ht40) {
conf->secondary_channel =
wpas_p2p_get_ht40_mode(wpa_s, mode,
conf->channel);
+ wpa_printf(MSG_DEBUG,
+ "HT secondary channel offset %d for P2P group",
+ conf->secondary_channel);
+ }
+#endif /* CONFIG_P2P */
+
+ if (!ssid->p2p_group &&
+ (mode->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ conf->secondary_channel = ssid->ht40;
+ wpa_printf(MSG_DEBUG,
+ "HT secondary channel offset %d for AP",
+ conf->secondary_channel);
+ }
+
if (conf->secondary_channel)
conf->ht_capab |=
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
-#endif /* CONFIG_P2P */
/*
* white-list capabilities that won't cause issues
@@ -168,7 +236,8 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
if (mode->vht_capab && ssid->vht) {
conf->ieee80211ac = 1;
- wpas_conf_ap_vht(wpa_s, conf, mode);
+ conf->vht_capab |= mode->vht_capab;
+ wpas_conf_ap_vht(wpa_s, ssid, conf, mode);
}
}
}
@@ -229,11 +298,13 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_ACS */
- if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) {
+ if (ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes,
+ wpa_s->hw.num_modes) && wpa_s->conf->country[0]) {
conf->ieee80211h = 1;
conf->ieee80211d = 1;
conf->country[0] = wpa_s->conf->country[0];
conf->country[1] = wpa_s->conf->country[1];
+ conf->country[2] = ' ';
}
#ifdef CONFIG_P2P
@@ -316,17 +387,34 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i] == 0)
continue;
- wep->key[i] = os_malloc(ssid->wep_key_len[i]);
+ wep->key[i] = os_memdup(ssid->wep_key[i],
+ ssid->wep_key_len[i]);
if (wep->key[i] == NULL)
return -1;
- os_memcpy(wep->key[i], ssid->wep_key[i],
- ssid->wep_key_len[i]);
wep->len[i] = ssid->wep_key_len[i];
}
wep->idx = ssid->wep_tx_keyidx;
wep->keys_set = 1;
}
+ if (wpa_s->conf->go_interworking) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Enable Interworking with access_network_type: %d",
+ wpa_s->conf->go_access_network_type);
+ bss->interworking = wpa_s->conf->go_interworking;
+ bss->access_network_type = wpa_s->conf->go_access_network_type;
+ bss->internet = wpa_s->conf->go_internet;
+ if (wpa_s->conf->go_venue_group) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Venue group: %d Venue type: %d",
+ wpa_s->conf->go_venue_group,
+ wpa_s->conf->go_venue_type);
+ bss->venue_group = wpa_s->conf->go_venue_group;
+ bss->venue_type = wpa_s->conf->go_venue_type;
+ bss->venue_info_set = 1;
+ }
+ }
+
if (ssid->ap_max_inactivity)
bss->ap_max_inactivity = ssid->ap_max_inactivity;
@@ -461,6 +549,9 @@ no_wps:
else
bss->max_num_sta = wpa_s->conf->max_num_sta;
+ if (!bss->isolate)
+ bss->isolate = wpa_s->conf->ap_isolate;
+
bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
if (wpa_s->conf->ap_vendor_elements) {
@@ -585,9 +676,18 @@ static void wpas_ap_configured_cb(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
+ wpa_printf(MSG_DEBUG, "AP interface setup completed - state %s",
+ hostapd_state_text(wpa_s->ap_iface->state));
+ if (wpa_s->ap_iface->state == HAPD_IFACE_DISABLED) {
+ wpa_supplicant_ap_deinit(wpa_s);
+ return;
+ }
+
#ifdef CONFIG_ACS
- if (wpa_s->current_ssid && wpa_s->current_ssid->acs)
+ if (wpa_s->current_ssid && wpa_s->current_ssid->acs) {
wpa_s->assoc_freq = wpa_s->ap_iface->freq;
+ wpa_s->current_ssid->frequency = wpa_s->ap_iface->freq;
+ }
#endif /* CONFIG_ACS */
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
@@ -662,7 +762,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
else
params.uapsd = -1;
- if (ieee80211_is_dfs(params.freq.freq))
+ if (ieee80211_is_dfs(params.freq.freq, wpa_s->hw.modes,
+ wpa_s->hw.num_modes))
params.freq.freq = 0; /* set channel after CAC */
if (params.p2p)
@@ -692,13 +793,6 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
return -1;
}
- /* Use the maximum oper channel width if it's given. */
- if (ssid->max_oper_chwidth)
- conf->vht_oper_chwidth = ssid->max_oper_chwidth;
-
- ieee80211_freq_to_chan(ssid->vht_center_freq2,
- &conf->vht_oper_centr_freq_seg1_idx);
-
os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
wpa_s->conf->wmm_ac_params,
sizeof(wpa_s->conf->wmm_ac_params));
@@ -777,6 +871,14 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
wpa_s->assoc_freq = ssid->frequency;
+#if defined(CONFIG_P2P) && defined(CONFIG_ACS)
+ if (wpa_s->p2p_go_do_acs) {
+ wpa_s->ap_iface->conf->channel = 0;
+ wpa_s->ap_iface->conf->hw_mode = wpa_s->p2p_go_acs_band;
+ ssid->acs = 1;
+ }
+#endif /* CONFIG_P2P && CONFIG_ACS */
+
if (hostapd_setup_interface(wpa_s->ap_iface)) {
wpa_printf(MSG_ERROR, "Failed to initialize AP interface");
wpa_supplicant_ap_deinit(wpa_s);
@@ -1436,12 +1538,49 @@ void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s)
if (wpa_s->ifmsh)
hostapd_ctrl_iface_pmksa_flush(wpa_s->ifmsh->bss[0]);
}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr,
+ char *buf, size_t len)
+{
+ return hostapd_ctrl_iface_pmksa_list_mesh(wpa_s->ifmsh->bss[0], addr,
+ &buf[0], len);
+}
+
+
+int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ struct external_pmksa_cache *entry;
+ void *pmksa_cache;
+
+ pmksa_cache = hostapd_ctrl_iface_pmksa_create_entry(wpa_s->own_addr,
+ cmd);
+ if (!pmksa_cache)
+ return -1;
+
+ entry = os_zalloc(sizeof(struct external_pmksa_cache));
+ if (!entry)
+ return -1;
+
+ entry->pmksa_cache = pmksa_cache;
+
+ dl_list_add(&wpa_s->mesh_external_pmksa_cache, &entry->list);
+
+ return 0;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
#endif /* CONFIG_CTRL_IFACE */
#ifdef NEED_AP_MLME
-void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar)
+void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar)
{
if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
return;
@@ -1453,8 +1592,8 @@ void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
}
-void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar)
+void wpas_ap_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar)
{
if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
return;
@@ -1465,8 +1604,8 @@ void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
}
-void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar)
+void wpas_ap_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar)
{
if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
return;
@@ -1477,8 +1616,8 @@ void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
}
-void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar)
+void wpas_ap_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar)
{
if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
return;
@@ -1489,8 +1628,8 @@ void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
}
-void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar)
+void wpas_ap_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar)
{
if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
return;
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 5a59ddcc1c93..447b551863a3 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -85,17 +85,20 @@ int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
size_t len);
void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
+int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr,
+ char *buf, size_t len);
+int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd);
-void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
+void wpas_ap_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
struct dfs_event *radar);
-void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar);
-void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar);
-void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar);
-void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
- struct dfs_event *radar);
+void wpas_ap_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
+void wpas_ap_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
+void wpas_ap_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar);
void ap_periodic(struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c
index 072a1d5414ae..5056a9300a87 100644
--- a/wpa_supplicant/autoscan.c
+++ b/wpa_supplicant/autoscan.c
@@ -47,11 +47,16 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
struct sched_scan_plan *scan_plans;
/* Give preference to scheduled scan plans if supported/configured */
- if (wpa_s->sched_scan_plans)
+ if (wpa_s->sched_scan_plans) {
+ wpa_printf(MSG_DEBUG,
+ "autoscan: sched_scan_plans set - use it instead");
return 0;
+ }
- if (wpa_s->autoscan && wpa_s->autoscan_priv)
+ if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+ wpa_printf(MSG_DEBUG, "autoscan: Already initialized");
return 0;
+ }
if (name == NULL)
return 0;
diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c
index 798b43c3fdf7..1ea640114c8e 100644
--- a/wpa_supplicant/bgscan.c
+++ b/wpa_supplicant/bgscan.c
@@ -34,8 +34,6 @@ int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const struct bgscan_ops *ops = NULL;
bgscan_deinit(wpa_s);
- if (name == NULL)
- return -1;
params = os_strchr(name, ':');
if (params == NULL) {
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index a320cc43068c..cb732f709b9e 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -320,9 +320,6 @@ static int bgscan_learn_get_params(struct bgscan_learn_data *data,
{
const char *pos;
- if (params == NULL)
- return 0;
-
data->short_interval = atoi(params);
pos = os_strchr(params, ':');
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index a467cc5b9271..41a26df0d635 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -56,12 +56,7 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx)
} else {
if (data->scan_interval == data->short_interval) {
data->short_scan_count++;
- /*
- * Spend at most the duration of a long scan interval
- * scanning at the short scan interval. After that,
- * revert to the long scan interval.
- */
- if (data->short_scan_count > data->max_short_scans) {
+ if (data->short_scan_count >= data->max_short_scans) {
data->scan_interval = data->long_interval;
wpa_printf(MSG_DEBUG, "bgscan simple: Backing "
"off to long scan interval");
@@ -85,9 +80,6 @@ static int bgscan_simple_get_params(struct bgscan_simple_data *data,
{
const char *pos;
- if (params == NULL)
- return 0;
-
data->short_interval = atoi(params);
pos = os_strchr(params, ':');
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 3a8778db9058..3a41db98e5ba 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -93,6 +93,7 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
ANQP_DUP(nai_realm);
ANQP_DUP(anqp_3gpp);
ANQP_DUP(domain_name);
+ ANQP_DUP(fils_realm_info);
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
ANQP_DUP(hs20_capability_list);
@@ -101,6 +102,8 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
ANQP_DUP(hs20_connection_capability);
ANQP_DUP(hs20_operating_class);
ANQP_DUP(hs20_osu_providers_list);
+ ANQP_DUP(hs20_operator_icon_metadata);
+ ANQP_DUP(hs20_osu_providers_nai_list);
#endif /* CONFIG_HS20 */
#undef ANQP_DUP
@@ -168,6 +171,7 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
wpabuf_free(anqp->nai_realm);
wpabuf_free(anqp->anqp_3gpp);
wpabuf_free(anqp->domain_name);
+ wpabuf_free(anqp->fils_realm_info);
while ((elem = dl_list_first(&anqp->anqp_elems,
struct wpa_bss_anqp_elem, list))) {
@@ -183,6 +187,8 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
wpabuf_free(anqp->hs20_connection_capability);
wpabuf_free(anqp->hs20_operating_class);
wpabuf_free(anqp->hs20_osu_providers_list);
+ wpabuf_free(anqp->hs20_operator_icon_metadata);
+ wpabuf_free(anqp->hs20_osu_providers_nai_list);
#endif /* CONFIG_HS20 */
os_free(anqp);
@@ -267,9 +273,9 @@ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
}
-static void calculate_update_time(const struct os_reltime *fetch_time,
- unsigned int age_ms,
- struct os_reltime *update_time)
+void calculate_update_time(const struct os_reltime *fetch_time,
+ unsigned int age_ms,
+ struct os_reltime *update_time)
{
os_time_t usec;
@@ -595,6 +601,42 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
{
u32 changes;
+ if (bss->last_update_idx == wpa_s->bss_update_idx) {
+ struct os_reltime update_time;
+
+ /*
+ * Some drivers (e.g., cfg80211) include multiple BSS entries
+ * for the same BSS if that BSS's channel changes. The BSS list
+ * implementation in wpa_supplicant does not do that and we need
+ * to filter out the obsolete results here to make sure only the
+ * most current BSS information remains in the table.
+ */
+ wpa_printf(MSG_DEBUG, "BSS: " MACSTR
+ " has multiple entries in the scan results - select the most current one",
+ MAC2STR(bss->bssid));
+ calculate_update_time(fetch_time, res->age, &update_time);
+ wpa_printf(MSG_DEBUG,
+ "Previous last_update: %u.%06u (freq %d%s)",
+ (unsigned int) bss->last_update.sec,
+ (unsigned int) bss->last_update.usec,
+ bss->freq,
+ (bss->flags & WPA_BSS_ASSOCIATED) ? " assoc" : "");
+ wpa_printf(MSG_DEBUG, "New last_update: %u.%06u (freq %d%s)",
+ (unsigned int) update_time.sec,
+ (unsigned int) update_time.usec,
+ res->freq,
+ (res->flags & WPA_SCAN_ASSOCIATED) ? " assoc" : "");
+ if ((bss->flags & WPA_BSS_ASSOCIATED) ||
+ (!(res->flags & WPA_SCAN_ASSOCIATED) &&
+ !os_reltime_before(&bss->last_update, &update_time))) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore this BSS entry since the previous update looks more current");
+ return bss;
+ }
+ wpa_printf(MSG_DEBUG,
+ "Accept this BSS entry since it looks more current than the previous update");
+ }
+
changes = wpa_bss_compare_res(bss, res);
if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
@@ -1279,3 +1321,19 @@ int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
*rates = r;
return len;
}
+
+
+#ifdef CONFIG_FILS
+const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss)
+{
+ const u8 *ie;
+
+ if (bss) {
+ ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+ if (ie && ie[1] >= 4 && WPA_GET_LE16(ie + 2) & BIT(7))
+ return ie + 4;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_FILS */
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 84e8fb07461e..5251b2c354e3 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -40,6 +40,7 @@ struct wpa_bss_anqp {
struct wpabuf *nai_realm;
struct wpabuf *anqp_3gpp;
struct wpabuf *domain_name;
+ struct wpabuf *fils_realm_info;
struct dl_list anqp_elems; /* list of struct wpa_bss_anqp_elem */
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
@@ -49,6 +50,8 @@ struct wpa_bss_anqp {
struct wpabuf *hs20_connection_capability;
struct wpabuf *hs20_operating_class;
struct wpabuf *hs20_osu_providers_list;
+ struct wpabuf *hs20_operator_icon_metadata;
+ struct wpabuf *hs20_osu_providers_nai_list;
#endif /* CONFIG_HS20 */
};
@@ -144,6 +147,7 @@ int wpa_bss_get_max_rate(const struct wpa_bss *bss);
int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss);
+const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss);
static inline int bss_is_dmg(const struct wpa_bss *bss)
{
@@ -167,4 +171,8 @@ static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level)
bss->level = new_level;
}
+void calculate_update_time(const struct os_reltime *fetch_time,
+ unsigned int age_ms,
+ struct os_reltime *update_time);
+
#endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index dd922caf80af..c43960697dc3 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / Configuration parser and common functions
- * 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.
@@ -11,6 +11,7 @@
#include "common.h"
#include "utils/uuid.h"
#include "utils/ip_addr.h"
+#include "common/ieee802_1x_defs.h"
#include "crypto/sha1.h"
#include "rsn_supp/wpa.h"
#include "eap_peer/eap.h"
@@ -396,6 +397,50 @@ static char * wpa_config_write_bssid(const struct parse_data *data,
#endif /* NO_CONFIG_WRITE */
+static int wpa_config_parse_bssid_hint(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+ os_strcmp(value, "any") == 0) {
+ ssid->bssid_hint_set = 0;
+ wpa_printf(MSG_MSGDUMP, "BSSID hint any");
+ return 0;
+ }
+ if (hwaddr_aton(value, ssid->bssid_hint)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID hint '%s'.",
+ line, value);
+ return -1;
+ }
+ ssid->bssid_hint_set = 1;
+ wpa_hexdump(MSG_MSGDUMP, "BSSID hint", ssid->bssid_hint, ETH_ALEN);
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_hint(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *value;
+ int res;
+
+ if (!ssid->bssid_hint_set)
+ return NULL;
+
+ value = os_malloc(20);
+ if (!value)
+ return NULL;
+ res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid_hint));
+ if (os_snprintf_error(20, res)) {
+ os_free(value);
+ return NULL;
+ }
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
@@ -690,6 +735,10 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data,
val |= WPA_KEY_MGMT_FT_PSK;
else if (os_strcmp(start, "FT-EAP") == 0)
val |= WPA_KEY_MGMT_FT_IEEE8021X;
+#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 */
#ifdef CONFIG_IEEE80211W
else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
@@ -719,6 +768,26 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data,
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
+ 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 */
+#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 */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
@@ -827,6 +896,18 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
}
pos += ret;
}
+
+#ifdef CONFIG_SHA384
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ ret = os_snprintf(pos, end - pos, "%sFT-EAP-SHA384",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
@@ -921,6 +1002,47 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
}
#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#ifdef CONFIG_IEEE80211R
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+ if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
if (pos == buf) {
os_free(buf);
buf = NULL;
@@ -1042,6 +1164,40 @@ static char * wpa_config_write_group(const struct parse_data *data,
#endif /* NO_CONFIG_WRITE */
+static int wpa_config_parse_group_mgmt(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ int val;
+
+ val = wpa_config_parse_cipher(line, value);
+ if (val == -1)
+ return -1;
+
+ if (val & ~WPA_ALLOWED_GROUP_MGMT_CIPHERS) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: not allowed group management cipher (0x%x).",
+ line, val);
+ return -1;
+ }
+
+ if (ssid->group_mgmt_cipher == val)
+ return 1;
+ wpa_printf(MSG_MSGDUMP, "group_mgmt: 0x%x", val);
+ ssid->group_mgmt_cipher = val;
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_group_mgmt(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return wpa_config_write_cipher(ssid->group_mgmt_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
static int wpa_config_parse_auth_alg(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
@@ -1816,6 +1972,87 @@ static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
#endif /* CONFIG_MESH */
+#ifdef CONFIG_MACSEC
+
+static int wpa_config_parse_mka_cak(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ if (hexstr2bin(value, ssid->mka_cak, MACSEC_CAK_LEN) ||
+ value[MACSEC_CAK_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.",
+ line, value);
+ return -1;
+ }
+
+ ssid->mka_psk_set |= MKA_PSK_SET_CAK;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak, MACSEC_CAK_LEN);
+ return 0;
+}
+
+
+static int wpa_config_parse_mka_ckn(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ if (hexstr2bin(value, ssid->mka_ckn, MACSEC_CKN_LEN) ||
+ value[MACSEC_CKN_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
+ line, value);
+ return -1;
+ }
+
+ ssid->mka_psk_set |= MKA_PSK_SET_CKN;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn, MACSEC_CKN_LEN);
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+
+static char * wpa_config_write_mka_cak(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
+ return NULL;
+
+ return wpa_config_write_string_hex(ssid->mka_cak, MACSEC_CAK_LEN);
+}
+
+
+static char * wpa_config_write_mka_ckn(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
+ return NULL;
+ return wpa_config_write_string_hex(ssid->mka_ckn, MACSEC_CKN_LEN);
+}
+
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_MACSEC */
+
+
+static int wpa_config_parse_peerkey(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ wpa_printf(MSG_INFO, "NOTE: Obsolete peerkey parameter ignored");
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_peerkey(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
/* Helper macros for network block parser */
#ifdef OFFSET
@@ -1907,24 +2144,34 @@ static const struct parse_data ssid_fields[] = {
{ STR_RANGE(ssid, 0, SSID_MAX_LEN) },
{ INT_RANGE(scan_ssid, 0, 1) },
{ FUNC(bssid) },
+ { FUNC(bssid_hint) },
{ FUNC(bssid_blacklist) },
{ FUNC(bssid_whitelist) },
{ FUNC_KEY(psk) },
{ INT(mem_only_psk) },
+ { STR_KEY(sae_password) },
+ { STR(sae_password_id) },
{ FUNC(proto) },
{ FUNC(key_mgmt) },
{ INT(bg_scan_period) },
{ FUNC(pairwise) },
{ FUNC(group) },
+ { FUNC(group_mgmt) },
{ FUNC(auth_alg) },
{ FUNC(scan_freq) },
{ FUNC(freq_list) },
+ { INT_RANGE(ht, 0, 1) },
+ { INT_RANGE(vht, 0, 1) },
+ { INT_RANGE(ht40, -1, 1) },
{ INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT,
VHT_CHANWIDTH_80P80MHZ) },
+ { INT(vht_center_freq1) },
+ { INT(vht_center_freq2) },
#ifdef IEEE8021X_EAPOL
{ FUNC(eap) },
{ STR_LENe(identity) },
{ STR_LENe(anonymous_identity) },
+ { STR_LENe(imsi_identity) },
{ FUNC_KEY(password) },
{ STRe(ca_cert) },
{ STRe(ca_path) },
@@ -1981,6 +2228,7 @@ static const struct parse_data ssid_fields[] = {
#ifdef CONFIG_MESH
{ INT_RANGE(mode, 0, 5) },
{ INT_RANGE(no_auto_peer, 0, 1) },
+ { INT_RANGE(mesh_rssi_threshold, -255, 1) },
#else /* CONFIG_MESH */
{ INT_RANGE(mode, 0, 4) },
#endif /* CONFIG_MESH */
@@ -1990,7 +2238,7 @@ static const struct parse_data ssid_fields[] = {
#ifdef CONFIG_IEEE80211W
{ INT_RANGE(ieee80211w, 0, 2) },
#endif /* CONFIG_IEEE80211W */
- { INT_RANGE(peerkey, 0, 1) },
+ { FUNC(peerkey) /* obsolete - removed */ },
{ INT_RANGE(mixed_cell, 0, 1) },
{ INT_RANGE(frequency, 0, 65000) },
{ INT_RANGE(fixed_freq, 0, 1) },
@@ -2050,13 +2298,28 @@ static const struct parse_data ssid_fields[] = {
{ INT(beacon_int) },
#ifdef CONFIG_MACSEC
{ INT_RANGE(macsec_policy, 0, 1) },
+ { INT_RANGE(macsec_integ_only, 0, 1) },
+ { INT_RANGE(macsec_port, 1, 65534) },
+ { INT_RANGE(mka_priority, 0, 255) },
+ { FUNC_KEY(mka_cak) },
+ { FUNC_KEY(mka_ckn) },
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_HS20
{ INT(update_identifier) },
+ { STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) },
#endif /* CONFIG_HS20 */
{ INT_RANGE(mac_addr, 0, 2) },
{ INT_RANGE(pbss, 0, 2) },
{ INT_RANGE(wps_disabled, 0, 1) },
+ { INT_RANGE(fils_dh_group, 0, 65535) },
+#ifdef CONFIG_DPP
+ { STR(dpp_connector) },
+ { STR_LEN(dpp_netaccesskey) },
+ { INT(dpp_netaccesskey_expiry) },
+ { STR_LEN(dpp_csign) },
+#endif /* CONFIG_DPP */
+ { INT_RANGE(owe_group, 0, 65535) },
+ { INT_RANGE(owe_only, 0, 1) },
};
#undef OFFSET
@@ -2168,6 +2431,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap)
os_free(eap->eap_methods);
bin_clear_free(eap->identity, eap->identity_len);
os_free(eap->anonymous_identity);
+ os_free(eap->imsi_identity);
bin_clear_free(eap->password, eap->password_len);
os_free(eap->ca_cert);
os_free(eap->ca_path);
@@ -2226,6 +2490,8 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
os_free(ssid->ssid);
str_clear_free(ssid->passphrase);
os_free(ssid->ext_psk);
+ str_clear_free(ssid->sae_password);
+ os_free(ssid->sae_password_id);
#ifdef IEEE8021X_EAPOL
eap_peer_config_free(&ssid->eap);
#endif /* IEEE8021X_EAPOL */
@@ -2242,6 +2508,12 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
#ifdef CONFIG_MESH
os_free(ssid->mesh_basic_rates);
#endif /* CONFIG_MESH */
+#ifdef CONFIG_HS20
+ os_free(ssid->roaming_consortium_selection);
+#endif /* CONFIG_HS20 */
+ os_free(ssid->dpp_connector);
+ bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len);
+ os_free(ssid->dpp_csign);
while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
list))) {
dl_list_del(&psk->list);
@@ -2495,6 +2767,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
ssid->group_cipher = DEFAULT_GROUP;
ssid->key_mgmt = DEFAULT_KEY_MGMT;
ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+ ssid->ht = 1;
#ifdef IEEE8021X_EAPOL
ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
@@ -2506,6 +2779,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
+ ssid->mesh_rssi_threshold = DEFAULT_MESH_RSSI_THRESHOLD;
#endif /* CONFIG_MESH */
#ifdef CONFIG_HT_OVERRIDES
ssid->disable_ht = DEFAULT_DISABLE_HT;
@@ -2538,6 +2812,9 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
#ifdef CONFIG_IEEE80211W
ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_MACSEC
+ ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+#endif /* CONFIG_MACSEC */
ssid->mac_addr = -1;
}
@@ -2849,11 +3126,64 @@ static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
}
+static int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred,
+ const char *value)
+{
+ u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
+ size_t roaming_consortiums_len[MAX_ROAMING_CONS];
+ unsigned int num_roaming_consortiums = 0;
+ const char *pos, *end;
+ size_t len;
+
+ os_memset(roaming_consortiums, 0, sizeof(roaming_consortiums));
+ os_memset(roaming_consortiums_len, 0, sizeof(roaming_consortiums_len));
+
+ for (pos = value;;) {
+ end = os_strchr(pos, ',');
+ len = end ? (size_t) (end - pos) : os_strlen(pos);
+ if (!end && len == 0)
+ break;
+ if (len == 0 || (len & 1) != 0 ||
+ len / 2 > MAX_ROAMING_CONS_OI_LEN ||
+ hexstr2bin(pos,
+ roaming_consortiums[num_roaming_consortiums],
+ len / 2) < 0) {
+ wpa_printf(MSG_INFO,
+ "Invalid roaming_consortiums entry: %s",
+ pos);
+ return -1;
+ }
+ roaming_consortiums_len[num_roaming_consortiums] = len / 2;
+ num_roaming_consortiums++;
+
+ if (!end)
+ break;
+
+ if (num_roaming_consortiums >= MAX_ROAMING_CONS) {
+ wpa_printf(MSG_INFO,
+ "Too many roaming_consortiums OIs");
+ return -1;
+ }
+
+ pos = end + 1;
+ }
+
+ os_memcpy(cred->roaming_consortiums, roaming_consortiums,
+ sizeof(roaming_consortiums));
+ os_memcpy(cred->roaming_consortiums_len, roaming_consortiums_len,
+ sizeof(roaming_consortiums_len));
+ cred->num_roaming_consortiums = num_roaming_consortiums;
+
+ return 0;
+}
+
+
int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
const char *value, int line)
{
char *val;
size_t len;
+ int res;
if (os_strcmp(var, "temporary") == 0) {
cred->temporary = atoi(value);
@@ -3076,6 +3406,16 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
return 0;
}
+ if (os_strcmp(var, "roaming_consortiums") == 0) {
+ res = wpa_config_set_cred_roaming_consortiums(cred, val);
+ if (res < 0)
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid roaming_consortiums",
+ line);
+ os_free(val);
+ return res;
+ }
+
if (os_strcmp(var, "excluded_ssid") == 0) {
struct excluded_ssid *e;
@@ -3387,6 +3727,31 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
return buf;
}
+ if (os_strcmp(var, "roaming_consortiums") == 0) {
+ size_t buflen;
+ char *buf, *pos;
+ size_t i;
+
+ if (!cred->num_roaming_consortiums)
+ return NULL;
+ buflen = cred->num_roaming_consortiums *
+ MAX_ROAMING_CONS_OI_LEN * 2 + 1;
+ buf = os_malloc(buflen);
+ if (!buf)
+ return NULL;
+ pos = buf;
+ for (i = 0; i < cred->num_roaming_consortiums; i++) {
+ if (i > 0)
+ *pos++ = ',';
+ pos += wpa_snprintf_hex(
+ pos, buf + buflen - pos,
+ cred->roaming_consortiums[i],
+ cred->roaming_consortiums_len[i]);
+ }
+ *pos = '\0';
+ return buf;
+ }
+
if (os_strcmp(var, "excluded_ssid") == 0) {
unsigned int i;
char *buf, *end, *pos;
@@ -3644,6 +4009,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
config->max_num_sta = DEFAULT_MAX_NUM_STA;
+ config->ap_isolate = DEFAULT_AP_ISOLATE;
config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ;
config->wmm_ac_params[0] = ac_be;
@@ -3658,12 +4024,16 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
#ifdef CONFIG_MBO
config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
+ config->disassoc_imminent_rssi_threshold =
+ DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
+ config->oce = DEFAULT_OCE_SUPPORT;
#endif /* CONFIG_MBO */
if (ctrl_interface)
config->ctrl_interface = os_strdup(ctrl_interface);
if (driver_param)
config->driver_param = os_strdup(driver_param);
+ config->gas_rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
return config;
}
@@ -4269,6 +4639,7 @@ static const struct global_parse_data global_fields[] = {
{ FUNC_NO_VAR(load_dynamic_eap), 0 },
#ifdef CONFIG_WPS
{ FUNC(uuid), CFG_CHANGED_UUID },
+ { INT_RANGE(auto_uuid, 0, 1), 0 },
{ STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
CFG_CHANGED_DEVICE_NAME },
{ STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
@@ -4318,6 +4689,7 @@ static const struct global_parse_data global_fields[] = {
{ INT_RANGE(filter_ssids, 0, 1), 0 },
{ INT_RANGE(filter_rssi, -100, 0), 0 },
{ INT(max_num_sta), 0 },
+ { INT_RANGE(ap_isolate, 0, 1), 0 },
{ INT_RANGE(disassoc_low_ack, 0, 1), 0 },
#ifdef CONFIG_HS20
{ INT_RANGE(hs20, 0, 1), 0 },
@@ -4325,6 +4697,11 @@ static const struct global_parse_data global_fields[] = {
{ INT_RANGE(interworking, 0, 1), 0 },
{ FUNC(hessid), 0 },
{ INT_RANGE(access_network_type, 0, 15), 0 },
+ { INT_RANGE(go_interworking, 0, 1), 0 },
+ { INT_RANGE(go_access_network_type, 0, 15), 0 },
+ { INT_RANGE(go_internet, 0, 1), 0 },
+ { INT_RANGE(go_venue_group, 0, 255), 0 },
+ { INT_RANGE(go_venue_type, 0, 255), 0 },
{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
{ STR(autoscan), 0 },
{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
@@ -4345,9 +4722,10 @@ static const struct global_parse_data global_fields[] = {
{ FUNC(freq_list), 0 },
{ INT(scan_cur_freq), 0 },
{ INT(sched_scan_interval), 0 },
+ { INT(sched_scan_start_delay), 0 },
{ INT(tdls_external_control), 0},
{ STR(osu_dir), 0 },
- { STR(wowlan_triggers), 0 },
+ { STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
{ INT(p2p_search_delay), 0},
{ INT(mac_addr), 0 },
{ INT(rand_addr_lifetime), 0 },
@@ -4361,16 +4739,23 @@ static const struct global_parse_data global_fields[] = {
{ INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
{ INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
#endif /* CONFIG_FST */
+ { INT_RANGE(cert_in_cb, 0, 1), 0 },
{ INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
{ STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
#ifdef CONFIG_MBO
{ STR(non_pref_chan), 0 },
{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
-#endif /*CONFIG_MBO */
+ { INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
+ { INT_RANGE(oce, 0, 3), 0 },
+#endif /* CONFIG_MBO */
{ INT(gas_address3), 0 },
{ INT_RANGE(ftm_responder, 0, 1), 0 },
{ INT_RANGE(ftm_initiator, 0, 1), 0 },
+ { INT(gas_rand_addr_lifetime), 0 },
+ { INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
+ { INT_RANGE(dpp_config_processing, 0, 2), 0 },
+ { INT_RANGE(coloc_intf_reporting, 0, 1), 0 },
};
#undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 48e64be5da1a..cd7571f59329 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -32,6 +32,7 @@
#define DEFAULT_BSS_EXPIRATION_AGE 180
#define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
#define DEFAULT_MAX_NUM_STA 128
+#define DEFAULT_AP_ISOLATE 0
#define DEFAULT_ACCESS_NETWORK_TYPE 15
#define DEFAULT_SCAN_CUR_FREQ 0
#define DEFAULT_P2P_SEARCH_DELAY 500
@@ -41,6 +42,8 @@
#define DEFAULT_P2P_GO_CTWINDOW 0
#define DEFAULT_WPA_RSC_RELAXATION 1
#define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
+#define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
+#define DEFAULT_OCE_SUPPORT OCE_STA
#include "config_ssid.h"
#include "wps/wps.h"
@@ -48,6 +51,9 @@
#include "common/ieee802_11_common.h"
+#define MAX_ROAMING_CONS 36
+#define MAX_ROAMING_CONS_OI_LEN 15
+
struct wpa_cred {
/**
* next - Next credential in the list
@@ -222,10 +228,43 @@ struct wpa_cred {
*/
size_t roaming_consortium_len;
+ /**
+ * required_roaming_consortium - Required Roaming Consortium OI
+ *
+ * If required_roaming_consortium_len is non-zero, this field contains
+ * the Roaming Consortium OI that is required to be advertised by the AP
+ * for the credential to be considered matching.
+ */
u8 required_roaming_consortium[15];
+
+ /**
+ * required_roaming_consortium_len - Length of required_roaming_consortium
+ */
size_t required_roaming_consortium_len;
/**
+ * roaming_consortiums - Roaming Consortium OI(s) memberships
+ *
+ * This field contains one or more OIs identifying the roaming
+ * consortiums of which the provider is a member. The list is sorted
+ * from the most preferred one to the least preferred one. A match
+ * between the Roaming Consortium OIs advertised by an AP and the OIs
+ * in this list indicates that successful authentication is possible.
+ * (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/RoamingConsortiumOI)
+ */
+ u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
+
+ /**
+ * roaming_consortiums_len - Length on roaming_consortiums[i]
+ */
+ size_t roaming_consortiums_len[MAX_ROAMING_CONS];
+
+ /**
+ * num_roaming_consortiums - Number of entries in roaming_consortiums
+ */
+ unsigned int num_roaming_consortiums;
+
+ /**
* eap_method - EAP method to use
*
* Pre-configured EAP method to use with this credential or %NULL to
@@ -334,6 +373,7 @@ struct wpa_cred {
#define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
#define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
#define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
+#define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
/**
* struct wpa_config - wpa_supplicant configuration data
@@ -625,6 +665,13 @@ struct wpa_config {
u8 uuid[16];
/**
+ * auto_uuid - Automatic UUID behavior
+ * 0 = generate static value based on the local MAC address (default)
+ * 1 = generate a random UUID every time wpa_supplicant starts
+ */
+ int auto_uuid;
+
+ /**
* device_name - Device Name (WPS)
* User-friendly description of device; up to 32 octets encoded in
* UTF-8
@@ -833,6 +880,20 @@ struct wpa_config {
unsigned int max_num_sta;
/**
+ * ap_isolate - Whether to use client isolation feature
+ *
+ * Client isolation can be used to prevent low-level bridging of
+ * frames between associated stations in the BSS. By default,
+ * this bridging is allowed (ap_isolate=0); except in P2P GO case,
+ * where p2p_intra_bss parameter is used to determine whether to allow
+ * intra-BSS forwarding (ap_isolate = !p2p_intra_bss).
+ *
+ * 0 = do not enable AP isolation
+ * 1 = enable AP isolation
+ */
+ int ap_isolate;
+
+ /**
* freq_list - Array of allowed scan frequencies or %NULL for all
*
* This is an optional zero-terminated array of frequencies in
@@ -854,7 +915,7 @@ struct wpa_config {
unsigned int changed_parameters;
/**
- * disassoc_low_ack - Disassocicate stations with massive packet loss
+ * disassoc_low_ack - Disassociate stations with massive packet loss
*/
int disassoc_low_ack;
@@ -872,6 +933,34 @@ struct wpa_config {
*/
int access_network_type;
+ /**
+ * go_interworking - Whether Interworking for P2P GO is enabled
+ */
+ int go_interworking;
+
+ /**
+ * go_access_network_type - P2P GO Access Network Type
+ *
+ * This indicates which access network type to advertise if Interworking
+ * is enabled for P2P GO.
+ */
+ int go_access_network_type;
+
+ /**
+ * go_internet - Interworking: Internet connectivity (0 or 1)
+ */
+ int go_internet;
+
+ /**
+ * go_venue_group - Interworking: Venue group
+ */
+ int go_venue_group;
+
+ /**
+ * go_venue_type: Interworking: Venue type
+ */
+ int go_venue_type;
+
/**
* hessid - Homogenous ESS identifier
*
@@ -1096,6 +1185,15 @@ struct wpa_config {
unsigned int sched_scan_interval;
/**
+ * sched_scan_start_delay - Schedule scan start delay before first scan
+ *
+ * Delay (in seconds) before scheduling first scan plan cycle. The
+ * driver may ignore this parameter and start immediately (or at any
+ * other time), if this feature is not supported.
+ */
+ unsigned int sched_scan_start_delay;
+
+ /**
* tdls_external_control - External control for TDLS setup requests
*
* Enable TDLS mode where external programs are given the control
@@ -1291,6 +1389,19 @@ struct wpa_config {
* mbo_cell_capa - Cellular capabilities for MBO
*/
enum mbo_cellular_capa mbo_cell_capa;
+
+ /**
+ * disassoc_imminent_rssi_threshold - RSSI threshold of candidate AP
+ * when disassociation imminent is set.
+ */
+ int disassoc_imminent_rssi_threshold;
+
+ /**
+ * oce - Enable OCE in STA and/or STA-CFON mode
+ * - Set BIT(0) to enable OCE in non-AP STA mode
+ * - Set BIT(1) to enable OCE in STA-CFON mode
+ */
+ unsigned int oce;
#endif /* CONFIG_MBO */
/**
@@ -1328,6 +1439,45 @@ struct wpa_config {
* wpa_supplicant.
*/
int ftm_initiator;
+
+ /**
+ * gas_rand_addr_lifetime - Lifetime of random MAC address for ANQP in
+ * seconds
+ */
+ unsigned int gas_rand_addr_lifetime;
+
+ /**
+ * gas_rand_mac_addr - GAS MAC address policy
+ *
+ * 0 = use permanent MAC address
+ * 1 = use random MAC address
+ * 2 = like 1, but maintain OUI (with local admin bit set)
+ */
+ int gas_rand_mac_addr;
+
+ /**
+ * dpp_config_processing - How to process DPP configuration
+ *
+ * 0 = report received configuration to an external program for
+ * processing; do not generate any network profile internally
+ * 1 = report received configuration to an external program and generate
+ * a network profile internally, but do not automatically connect
+ * to the created (disabled) profile; the network profile id is
+ * reported to external programs
+ * 2 = report received configuration to an external program, generate
+ * a network profile internally, try to connect to the created
+ * profile automatically
+ */
+ int dpp_config_processing;
+
+ /**
+ * coloc_intf_reporting - Colocated interference reporting
+ *
+ * dot11CoLocIntfReportingActivated
+ * 0 = disabled (false)
+ * 1 = enabled (true)
+ */
+ int coloc_intf_reporting;
};
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 7ae16545bebc..09115e19dc2d 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -19,6 +19,7 @@
#include "config.h"
#include "base64.h"
#include "uuid.h"
+#include "common/ieee802_1x_defs.h"
#include "p2p/p2p.h"
#include "eap_peer/eap_methods.h"
#include "eap_peer/eap.h"
@@ -136,9 +137,13 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
wpa_config_update_psk(ssid);
}
+ if (ssid->disabled == 2)
+ ssid->p2p_persistent_group = 1;
+
if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
- !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
- !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
+ !(ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256 |
+ WPA_CIPHER_NONE))) {
/* Group cipher cannot be stronger than the pairwise cipher. */
wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher"
" list since it was not allowed for pairwise "
@@ -308,7 +313,7 @@ static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line,
encoded_len += len;
}
- if (!end) {
+ if (!end || !encoded) {
wpa_printf(MSG_ERROR, "Line %d: blob was not terminated "
"properly", *line);
os_free(encoded);
@@ -393,7 +398,8 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
if (f == NULL) {
wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
"error: %s", name, strerror(errno));
- os_free(config);
+ if (config != cfgp)
+ os_free(config);
return NULL;
}
@@ -459,7 +465,8 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
#ifndef WPA_IGNORE_CONFIG_ERRORS
if (errors) {
- wpa_config_free(config);
+ if (config != cfgp)
+ wpa_config_free(config);
config = NULL;
head = NULL;
}
@@ -499,6 +506,17 @@ static void write_bssid(FILE *f, struct wpa_ssid *ssid)
}
+static void write_bssid_hint(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value = wpa_config_get(ssid, "bssid_hint");
+
+ if (!value)
+ return;
+ fprintf(f, "\tbssid_hint=%s\n", value);
+ os_free(value);
+}
+
+
static void write_psk(FILE *f, struct wpa_ssid *ssid)
{
char *value;
@@ -578,6 +596,22 @@ static void write_group(FILE *f, struct wpa_ssid *ssid)
}
+static void write_group_mgmt(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (!ssid->group_mgmt_cipher)
+ return;
+
+ value = wpa_config_get(ssid, "group_mgmt");
+ if (!value)
+ return;
+ if (value[0])
+ fprintf(f, "\tgroup_mgmt=%s\n", value);
+ os_free(value);
+}
+
+
static void write_auth_alg(FILE *f, struct wpa_ssid *ssid)
{
char *value;
@@ -662,6 +696,40 @@ static void write_psk_list(FILE *f, struct wpa_ssid *ssid)
#endif /* CONFIG_P2P */
+#ifdef CONFIG_MACSEC
+
+static void write_mka_cak(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
+ return;
+
+ value = wpa_config_get(ssid, "mka_cak");
+ if (!value)
+ return;
+ fprintf(f, "\tmka_cak=%s\n", value);
+ os_free(value);
+}
+
+
+static void write_mka_ckn(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value;
+
+ if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
+ return;
+
+ value = wpa_config_get(ssid, "mka_ckn");
+ if (!value)
+ return;
+ fprintf(f, "\tmka_ckn=%s\n", value);
+ os_free(value);
+}
+
+#endif /* CONFIG_MACSEC */
+
+
static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
{
int i;
@@ -675,15 +743,19 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
STR(ssid);
INT(scan_ssid);
write_bssid(f, ssid);
+ write_bssid_hint(f, ssid);
write_str(f, "bssid_blacklist", ssid);
write_str(f, "bssid_whitelist", ssid);
write_psk(f, ssid);
INT(mem_only_psk);
+ STR(sae_password);
+ STR(sae_password_id);
write_proto(f, ssid);
write_key_mgmt(f, ssid);
INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
write_pairwise(f, ssid);
write_group(f, ssid);
+ write_group_mgmt(f, ssid);
write_auth_alg(f, ssid);
STR(bgscan);
STR(autoscan);
@@ -692,6 +764,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
write_eap(f, ssid);
STR(identity);
STR(anonymous_identity);
+ STR(imsi_identity);
STR(password);
STR(ca_cert);
STR(ca_path);
@@ -752,11 +825,16 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
#endif /* CONFIG_ACS */
write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
INT(disabled);
- INT(peerkey);
INT(mixed_cell);
+ INT(vht);
+ INT_DEF(ht, 1);
+ INT(ht40);
INT(max_oper_chwidth);
+ INT(vht_center_freq1);
+ INT(vht_center_freq2);
INT(pbss);
INT(wps_disabled);
+ INT(fils_dh_group);
#ifdef CONFIG_IEEE80211W
write_int(f, "ieee80211w", ssid->ieee80211w,
MGMT_FRAME_PROTECTION_DEFAULT);
@@ -772,9 +850,15 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
INT(beacon_int);
#ifdef CONFIG_MACSEC
INT(macsec_policy);
+ write_mka_cak(f, ssid);
+ write_mka_ckn(f, ssid);
+ INT(macsec_integ_only);
+ INT(macsec_port);
+ INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER);
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_HS20
INT(update_identifier);
+ STR(roaming_consortium_selection);
#endif /* CONFIG_HS20 */
write_int(f, "mac_addr", ssid->mac_addr, -1);
#ifdef CONFIG_MESH
@@ -783,10 +867,19 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT);
INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT);
INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
+ INT_DEF(mesh_rssi_threshold, DEFAULT_MESH_RSSI_THRESHOLD);
#endif /* CONFIG_MESH */
INT(wpa_ptk_rekey);
INT(group_rekey);
INT(ignore_broadcast_ssid);
+#ifdef CONFIG_DPP
+ STR(dpp_connector);
+ STR(dpp_netaccesskey);
+ INT(dpp_netaccesskey_expiry);
+ STR(dpp_csign);
+#endif /* CONFIG_DPP */
+ INT(owe_group);
+ INT(owe_only);
#ifdef CONFIG_HT_OVERRIDES
INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
@@ -949,6 +1042,20 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
fprintf(f, "\n");
}
+ if (cred->num_roaming_consortiums) {
+ size_t j;
+
+ fprintf(f, "\troaming_consortiums=\"");
+ for (i = 0; i < cred->num_roaming_consortiums; i++) {
+ if (i > 0)
+ fprintf(f, ",");
+ for (j = 0; j < cred->roaming_consortiums_len[i]; j++)
+ fprintf(f, "%02x",
+ cred->roaming_consortiums[i][j]);
+ }
+ fprintf(f, "\"\n");
+ }
+
if (cred->sim_num != DEFAULT_USER_SELECTED_SIM)
fprintf(f, "\tsim_num=%d\n", cred->sim_num);
}
@@ -1039,6 +1146,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
uuid_bin2str(config->uuid, buf, sizeof(buf));
fprintf(f, "uuid=%s\n", buf);
}
+ if (config->auto_uuid)
+ fprintf(f, "auto_uuid=%d\n", config->auto_uuid);
if (config->device_name)
fprintf(f, "device_name=%s\n", config->device_name);
if (config->manufacturer)
@@ -1076,6 +1185,17 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
+ {
+ int i;
+ char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+
+ for (i = 0; i < config->num_sec_device_types; i++) {
+ buf = wps_dev_type_bin2str(config->sec_device_type[i],
+ _buf, sizeof(_buf));
+ if (buf)
+ fprintf(f, "sec_device_type=%s\n", buf);
+ }
+ }
if (config->p2p_listen_reg_class)
fprintf(f, "p2p_listen_reg_class=%d\n",
config->p2p_listen_reg_class);
@@ -1175,8 +1295,12 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
config->bss_expiration_scan_count);
if (config->filter_ssids)
fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
+ if (config->filter_rssi)
+ fprintf(f, "filter_rssi=%d\n", config->filter_rssi);
if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
+ if (config->ap_isolate != DEFAULT_AP_ISOLATE)
+ fprintf(f, "ap_isolate=%u\n", config->ap_isolate);
if (config->disassoc_low_ack)
fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack);
#ifdef CONFIG_HS20
@@ -1191,6 +1315,17 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
fprintf(f, "access_network_type=%d\n",
config->access_network_type);
+ if (config->go_interworking)
+ fprintf(f, "go_interworking=%d\n", config->go_interworking);
+ if (config->go_access_network_type)
+ fprintf(f, "go_access_network_type=%d\n",
+ config->go_access_network_type);
+ if (config->go_internet)
+ fprintf(f, "go_internet=%d\n", config->go_internet);
+ if (config->go_venue_group)
+ fprintf(f, "go_venue_group=%d\n", config->go_venue_group);
+ if (config->go_venue_type)
+ fprintf(f, "go_venue_type=%d\n", config->go_venue_type);
#endif /* CONFIG_INTERWORKING */
if (config->pbc_in_m1)
fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1);
@@ -1226,7 +1361,7 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
if (config->sae_groups) {
int i;
fprintf(f, "sae_groups=");
- for (i = 0; config->sae_groups[i] >= 0; i++) {
+ for (i = 0; config->sae_groups[i] > 0; i++) {
fprintf(f, "%s%d", i > 0 ? " " : "",
config->sae_groups[i]);
}
@@ -1264,6 +1399,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
fprintf(f, "sched_scan_interval=%u\n",
config->sched_scan_interval);
+ if (config->sched_scan_start_delay)
+ fprintf(f, "sched_scan_start_delay=%u\n",
+ config->sched_scan_start_delay);
+
if (config->external_sim)
fprintf(f, "external_sim=%d\n", config->external_sim);
@@ -1278,6 +1417,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
if (config->bgscan)
fprintf(f, "bgscan=\"%s\"\n", config->bgscan);
+ if (config->autoscan)
+ fprintf(f, "autoscan=%s\n", config->autoscan);
+
if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY)
fprintf(f, "p2p_search_delay=%u\n",
config->p2p_search_delay);
@@ -1335,6 +1477,12 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan);
if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA)
fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
+ if (config->disassoc_imminent_rssi_threshold !=
+ DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD)
+ fprintf(f, "disassoc_imminent_rssi_threshold=%d\n",
+ config->disassoc_imminent_rssi_threshold);
+ if (config->oce != DEFAULT_OCE_SUPPORT)
+ fprintf(f, "oce=%u\n", config->oce);
#endif /* CONFIG_MBO */
if (config->gas_address3)
@@ -1344,6 +1492,28 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
fprintf(f, "ftm_responder=%d\n", config->ftm_responder);
if (config->ftm_initiator)
fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator);
+
+ if (config->osu_dir)
+ fprintf(f, "osu_dir=%s\n", config->osu_dir);
+
+ if (config->fst_group_id)
+ fprintf(f, "fst_group_id=%s\n", config->fst_group_id);
+ if (config->fst_priority)
+ fprintf(f, "fst_priority=%d\n", config->fst_priority);
+ if (config->fst_llt)
+ fprintf(f, "fst_llt=%d\n", config->fst_llt);
+
+ if (config->gas_rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME)
+ fprintf(f, "gas_rand_addr_lifetime=%u\n",
+ config->gas_rand_addr_lifetime);
+ if (config->gas_rand_mac_addr)
+ fprintf(f, "gas_rand_mac_addr=%d\n", config->gas_rand_mac_addr);
+ if (config->dpp_config_processing)
+ fprintf(f, "dpp_config_processing=%d\n",
+ config->dpp_config_processing);
+ if (config->coloc_intf_reporting)
+ fprintf(f, "coloc_intf_reporting=%d\n",
+ config->coloc_intf_reporting);
}
#endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 010b594af85e..d2a52d760089 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -28,6 +28,7 @@
#define DEFAULT_MESH_RETRY_TIMEOUT 40
#define DEFAULT_MESH_CONFIRM_TIMEOUT 40
#define DEFAULT_MESH_HOLDING_TIMEOUT 40
+#define DEFAULT_MESH_RSSI_THRESHOLD 1 /* no change */
#define DEFAULT_DISABLE_HT 0
#define DEFAULT_DISABLE_HT40 0
#define DEFAULT_DISABLE_SGI 0
@@ -146,6 +147,19 @@ struct wpa_ssid {
int bssid_set;
/**
+ * bssid_hint - BSSID hint
+ *
+ * If set, this is configured to the driver as a preferred initial BSSID
+ * while connecting to this network.
+ */
+ u8 bssid_hint[ETH_ALEN];
+
+ /**
+ * bssid_hint_set - Whether BSSID hint is configured for this network
+ */
+ int bssid_hint_set;
+
+ /**
* go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set
*/
u8 go_p2p_dev_addr[ETH_ALEN];
@@ -170,6 +184,24 @@ struct wpa_ssid {
char *passphrase;
/**
+ * sae_password - SAE password
+ *
+ * This parameter can be used to set a password for SAE. By default, the
+ * passphrase value is used if this separate parameter is not used, but
+ * passphrase follows the WPA-PSK constraints (8..63 characters) even
+ * though SAE passwords do not have such constraints.
+ */
+ char *sae_password;
+
+ /**
+ * sae_password_id - SAE password identifier
+ *
+ * This parameter can be used to identify a specific SAE password. If
+ * not included, the default SAE password is used instead.
+ */
+ char *sae_password_id;
+
+ /**
* ext_psk - PSK/passphrase name in external storage
*
* If this is set, PSK/passphrase will be fetched from external storage
@@ -196,6 +228,15 @@ struct wpa_ssid {
int group_cipher;
/**
+ * group_mgmt_cipher - Bitfield of allowed group management ciphers
+ *
+ * This is a bitfield of WPA_CIPHER_AES_128_CMAC and WPA_CIPHER_BIP_*
+ * values. If 0, no constraint is used for the cipher, i.e., whatever
+ * the AP uses is accepted.
+ */
+ int group_mgmt_cipher;
+
+ /**
* key_mgmt - Bitfield of allowed key management protocols
*
* WPA_KEY_MGMT_*
@@ -392,17 +433,6 @@ struct wpa_ssid {
int disabled_for_connect;
/**
- * peerkey - Whether PeerKey handshake for direct links is allowed
- *
- * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are
- * enabled.
- *
- * 0 = disabled (default)
- * 1 = enabled
- */
- int peerkey;
-
- /**
* id_str - Network identifier string for external scripts
*
* This value is passed to external ctrl_iface monitors in
@@ -470,12 +500,14 @@ struct wpa_ssid {
int dot11MeshConfirmTimeout; /* msec */
int dot11MeshHoldingTimeout; /* msec */
+ int ht;
int ht40;
int vht;
- u8 max_oper_chwidth;
+ int max_oper_chwidth;
+ unsigned int vht_center_freq1;
unsigned int vht_center_freq2;
/**
@@ -728,10 +760,71 @@ struct wpa_ssid {
* determine whether to use a secure session or not.
*/
int macsec_policy;
+
+ /**
+ * macsec_integ_only - Determines how MACsec are transmitted
+ *
+ * This setting applies only when MACsec is in use, i.e.,
+ * - macsec_policy is enabled
+ * - the key server has decided to enable MACsec
+ *
+ * 0: Encrypt traffic (default)
+ * 1: Integrity only
+ */
+ int macsec_integ_only;
+
+ /**
+ * macsec_port - MACsec port (in SCI)
+ *
+ * Port component of the SCI.
+ *
+ * Range: 1-65534 (default: 1)
+ */
+ int macsec_port;
+
+ /**
+ * mka_priority - Priority of MKA Actor
+ *
+ * Range: 0-255 (default: 255)
+ */
+ int mka_priority;
+
+ /**
+ * mka_ckn - MKA pre-shared CKN
+ */
+#define MACSEC_CKN_LEN 32
+ u8 mka_ckn[MACSEC_CKN_LEN];
+
+ /**
+ * mka_cak - MKA pre-shared CAK
+ */
+#define MACSEC_CAK_LEN 16
+ u8 mka_cak[MACSEC_CAK_LEN];
+
+#define MKA_PSK_SET_CKN BIT(0)
+#define MKA_PSK_SET_CAK BIT(1)
+#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
+ /**
+ * mka_psk_set - Whether mka_ckn and mka_cak are set
+ */
+ u8 mka_psk_set;
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_HS20
int update_identifier;
+
+ /**
+ * roaming_consortium_selection - Roaming Consortium Selection
+ *
+ * The matching Roaming Consortium OI that was used to generate this
+ * network profile.
+ */
+ u8 *roaming_consortium_selection;
+
+ /**
+ * roaming_consortium_selection_len - roaming_consortium_selection len
+ */
+ size_t roaming_consortium_selection_len;
#endif /* CONFIG_HS20 */
unsigned int wps_run;
@@ -758,12 +851,92 @@ struct wpa_ssid {
int no_auto_peer;
/**
+ * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
+ *
+ * -255..-1 = threshold value in dBm
+ * 0 = not using RSSI threshold
+ * 1 = do not change driver default
+ */
+ int mesh_rssi_threshold;
+
+ /**
* wps_disabled - WPS disabled in AP mode
*
* 0 = WPS enabled and configured (default)
* 1 = WPS disabled
*/
int wps_disabled;
+
+ /**
+ * fils_dh_group - FILS DH Group
+ *
+ * 0 = PFS disabled with FILS shared key authentication
+ * 1-65535 DH Group to use for FILS PFS
+ */
+ int fils_dh_group;
+
+ /**
+ * dpp_connector - DPP Connector (signedConnector as string)
+ */
+ char *dpp_connector;
+
+ /**
+ * dpp_netaccesskey - DPP netAccessKey (own private key)
+ */
+ u8 *dpp_netaccesskey;
+
+ /**
+ * dpp_netaccesskey_len - DPP netAccessKey length in octets
+ */
+ size_t dpp_netaccesskey_len;
+
+ /**
+ * net_access_key_expiry - DPP netAccessKey expiry in UNIX time stamp
+ *
+ * 0 indicates no expiration.
+ */
+ unsigned int dpp_netaccesskey_expiry;
+
+ /**
+ * dpp_csign - C-sign-key (Configurator public key)
+ */
+ u8 *dpp_csign;
+
+ /**
+ * dpp_csign_len - C-sign-key length in octets
+ */
+ size_t dpp_csign_len;
+
+ /**
+ * owe_group - OWE DH Group
+ *
+ * 0 = use default (19) first and then try all supported groups one by
+ * one if AP rejects the selected group
+ * 1-65535 DH Group to use for OWE
+ *
+ * Groups 19 (NIST P-256), 20 (NIST P-384), and 21 (NIST P-521) are
+ * currently supported.
+ */
+ int owe_group;
+
+ /**
+ * owe_only - OWE-only mode (disable transition mode)
+ *
+ * 0 = enable transition mode (allow connection to either OWE or open
+ * BSS)
+ * 1 = disable transition mode (allow connection only with OWE)
+ */
+ int owe_only;
+
+ /**
+ * owe_transition_bss_select_count - OWE transition BSS select count
+ *
+ * This is an internally used variable (i.e., not used in external
+ * configuration) to track the number of selection attempts done for
+ * OWE BSS in transition mode. This allows fallback to an open BSS if
+ * the selection attempts for OWE BSS exceed the configured threshold.
+ */
+ int owe_transition_bss_select_count;
};
#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 82ba3b015dc9..0ce1830b4ef2 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -99,13 +99,12 @@ static int wpa_config_read_blobs(struct wpa_config *config, HKEY hk)
break;
}
blob->name = os_strdup((char *) name);
- blob->data = os_malloc(datalen);
+ blob->data = os_memdup(data, datalen);
if (blob->name == NULL || blob->data == NULL) {
wpa_config_free_blob(blob);
errors++;
break;
}
- os_memcpy(blob->data, data, datalen);
blob->len = datalen;
wpa_config_set_blob(config, blob);
@@ -234,6 +233,7 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
#ifdef CONFIG_WPS
if (wpa_config_read_global_uuid(config, hk))
errors++;
+ wpa_config_read_reg_dword(hk, TEXT("auto_uuid"), &config->auto_uuid);
config->device_name = wpa_config_read_reg_string(
hk, TEXT("device_name"));
config->manufacturer = wpa_config_read_reg_string(
@@ -580,6 +580,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
uuid_bin2str(config->uuid, buf, sizeof(buf));
wpa_config_write_reg_string(hk, "uuid", buf);
}
+ wpa_config_write_reg_dword(hk, TEXT("auto_uuid"), config->auto_uuid,
+ 0);
wpa_config_write_reg_string(hk, "device_name", config->device_name);
wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer);
wpa_config_write_reg_string(hk, "model_name", config->model_name);
@@ -617,6 +619,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
config->filter_ssids, 0);
wpa_config_write_reg_dword(hk, TEXT("max_num_sta"),
config->max_num_sta, DEFAULT_MAX_NUM_STA);
+ wpa_config_write_reg_dword(hk, TEXT("ap_isolate"),
+ config->ap_isolate, DEFAULT_AP_ISOLATE);
wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"),
config->disassoc_low_ack, 0);
@@ -868,6 +872,8 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id)
INT(scan_ssid);
write_bssid(netw, ssid);
write_psk(netw, ssid);
+ STR(sae_password);
+ STR(sae_password_id);
write_proto(netw, ssid);
write_key_mgmt(netw, ssid);
write_pairwise(netw, ssid);
@@ -877,6 +883,7 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id)
write_eap(netw, ssid);
STR(identity);
STR(anonymous_identity);
+ STR(imsi_identity);
STR(password);
STR(ca_cert);
STR(ca_path);
@@ -924,7 +931,6 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id)
write_int(netw, "proactive_key_caching", ssid->proactive_key_caching,
-1);
INT(disabled);
- INT(peerkey);
#ifdef CONFIG_IEEE80211W
write_int(netw, "ieee80211w", ssid->ieee80211w,
MGMT_FRAME_PROTECTION_DEFAULT);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index d814fdf7fd2d..77a3133d8d56 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -20,6 +20,9 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
+#ifdef CONFIG_DPP
+#include "common/dpp.h"
+#endif /* CONFIG_DPP */
#include "crypto/tls.h"
#include "ap/hostapd.h"
#include "eap_peer/eap.h"
@@ -52,6 +55,7 @@
#include "offchannel.h"
#include "drivers/driver.h"
#include "mesh.h"
+#include "dpp_supplicant.h"
static int wpa_supplicant_global_iface_list(struct wpa_global *global,
char *buf, int len);
@@ -61,6 +65,7 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
char *val);
+
static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
{
char *pos;
@@ -339,6 +344,75 @@ static int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s,
}
+static int
+wpas_ctrl_set_relative_rssi(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ int relative_rssi;
+
+ if (os_strcmp(cmd, "disable") == 0) {
+ wpa_s->srp.relative_rssi_set = 0;
+ return 0;
+ }
+
+ relative_rssi = atoi(cmd);
+ if (relative_rssi < 0 || relative_rssi > 100)
+ return -1;
+ wpa_s->srp.relative_rssi = relative_rssi;
+ wpa_s->srp.relative_rssi_set = 1;
+ return 0;
+}
+
+
+static int wpas_ctrl_set_relative_band_adjust(struct wpa_supplicant *wpa_s,
+ const char *cmd)
+{
+ char *pos;
+ int adjust_rssi;
+
+ /* <band>:adjust_value */
+ pos = os_strchr(cmd, ':');
+ if (!pos)
+ return -1;
+ pos++;
+ adjust_rssi = atoi(pos);
+ if (adjust_rssi < -100 || adjust_rssi > 100)
+ return -1;
+
+ if (os_strncmp(cmd, "2G", 2) == 0)
+ wpa_s->srp.relative_adjust_band = WPA_SETBAND_2G;
+ else if (os_strncmp(cmd, "5G", 2) == 0)
+ wpa_s->srp.relative_adjust_band = WPA_SETBAND_5G;
+ else
+ return -1;
+
+ wpa_s->srp.relative_adjust_rssi = adjust_rssi;
+
+ return 0;
+}
+
+
+static int wpas_ctrl_iface_set_ric_ies(struct wpa_supplicant *wpa_s,
+ const char *cmd)
+{
+ struct wpabuf *ric_ies;
+
+ if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) {
+ wpabuf_free(wpa_s->ric_ies);
+ wpa_s->ric_ies = NULL;
+ return 0;
+ }
+
+ ric_ies = wpabuf_parse_bin(cmd);
+ if (!ric_ies)
+ return -1;
+
+ wpabuf_free(wpa_s->ric_ies);
+ wpa_s->ric_ies = ric_ies;
+
+ return 0;
+}
+
+
static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
char *cmd)
{
@@ -365,16 +439,29 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
-1, -1, -1, atoi(value));
} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) {
if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
- atoi(value)))
+ atoi(value))) {
ret = -1;
+ } else {
+ value[-1] = '=';
+ wpa_config_process_global(wpa_s->conf, cmd, -1);
+ }
} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") ==
0) {
if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
- atoi(value)))
+ atoi(value))) {
ret = -1;
+ } else {
+ value[-1] = '=';
+ wpa_config_process_global(wpa_s->conf, cmd, -1);
+ }
} else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
- if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
+ if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
+ atoi(value))) {
ret = -1;
+ } else {
+ value[-1] = '=';
+ wpa_config_process_global(wpa_s->conf, cmd, -1);
+ }
} else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
wpa_s->wps_fragment_size = atoi(value);
#ifdef CONFIG_WPS_TESTING
@@ -494,6 +581,59 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
ret = set_disallow_aps(wpa_s, value);
} else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
wpa_s->no_keep_alive = !!atoi(value);
+#ifdef CONFIG_DPP
+ } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
+ os_free(wpa_s->dpp_configurator_params);
+ wpa_s->dpp_configurator_params = os_strdup(value);
+ } else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
+ wpa_s->dpp_init_max_tries = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
+ wpa_s->dpp_init_retry_time = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) {
+ wpa_s->dpp_resp_wait_time = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_resp_max_tries") == 0) {
+ wpa_s->dpp_resp_max_tries = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_resp_retry_time") == 0) {
+ wpa_s->dpp_resp_retry_time = atoi(value);
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strcasecmp(cmd, "dpp_pkex_own_mac_override") == 0) {
+ if (hwaddr_aton(value, dpp_pkex_own_mac_override))
+ ret = -1;
+ } else if (os_strcasecmp(cmd, "dpp_pkex_peer_mac_override") == 0) {
+ if (hwaddr_aton(value, dpp_pkex_peer_mac_override))
+ ret = -1;
+ } else if (os_strcasecmp(cmd, "dpp_pkex_ephemeral_key_override") == 0) {
+ size_t hex_len = os_strlen(value);
+
+ if (hex_len >
+ 2 * sizeof(dpp_pkex_ephemeral_key_override))
+ ret = -1;
+ else if (hexstr2bin(value, dpp_pkex_ephemeral_key_override,
+ hex_len / 2))
+ ret = -1;
+ else
+ dpp_pkex_ephemeral_key_override_len = hex_len / 2;
+ } else if (os_strcasecmp(cmd, "dpp_protocol_key_override") == 0) {
+ size_t hex_len = os_strlen(value);
+
+ if (hex_len > 2 * sizeof(dpp_protocol_key_override))
+ ret = -1;
+ else if (hexstr2bin(value, dpp_protocol_key_override,
+ hex_len / 2))
+ ret = -1;
+ else
+ dpp_protocol_key_override_len = hex_len / 2;
+ } else if (os_strcasecmp(cmd, "dpp_nonce_override") == 0) {
+ size_t hex_len = os_strlen(value);
+
+ if (hex_len > 2 * sizeof(dpp_nonce_override))
+ ret = -1;
+ else if (hexstr2bin(value, dpp_nonce_override, hex_len / 2))
+ ret = -1;
+ else
+ dpp_nonce_override_len = hex_len / 2;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
wpa_s->ext_mgmt_frame_handling = !!atoi(value);
@@ -515,9 +655,54 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
wpa_s->ignore_auth_resp = !!atoi(value);
} else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) {
wpa_s->ignore_assoc_disallow = !!atoi(value);
+ wpa_drv_ignore_assoc_disallow(wpa_s,
+ wpa_s->ignore_assoc_disallow);
} else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) {
wpa_s->reject_btm_req_reason = atoi(value);
+ } else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) {
+ os_free(wpa_s->get_pref_freq_list_override);
+ if (!value[0])
+ wpa_s->get_pref_freq_list_override = NULL;
+ else
+ wpa_s->get_pref_freq_list_override = os_strdup(value);
+ } else if (os_strcasecmp(cmd, "sae_commit_override") == 0) {
+ wpabuf_free(wpa_s->sae_commit_override);
+ if (value[0] == '\0')
+ wpa_s->sae_commit_override = NULL;
+ else
+ wpa_s->sae_commit_override = wpabuf_parse_bin(value);
+#ifdef CONFIG_DPP
+ } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
+ os_free(wpa_s->dpp_config_obj_override);
+ if (value[0] == '\0')
+ wpa_s->dpp_config_obj_override = NULL;
+ else
+ wpa_s->dpp_config_obj_override = os_strdup(value);
+ } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
+ os_free(wpa_s->dpp_discovery_override);
+ if (value[0] == '\0')
+ wpa_s->dpp_discovery_override = NULL;
+ else
+ wpa_s->dpp_discovery_override = os_strdup(value);
+ } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
+ os_free(wpa_s->dpp_groups_override);
+ if (value[0] == '\0')
+ wpa_s->dpp_groups_override = NULL;
+ else
+ wpa_s->dpp_groups_override = os_strdup(value);
+ } else if (os_strcasecmp(cmd,
+ "dpp_ignore_netaccesskey_mismatch") == 0) {
+ wpa_s->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_FILS
+ } else if (os_strcasecmp(cmd, "disable_fils") == 0) {
+ wpa_s->disable_fils = !!atoi(value);
+ wpa_drv_disable_fils(wpa_s, wpa_s->disable_fils);
+ wpa_supplicant_set_default_scan_ies(wpa_s);
+#endif /* CONFIG_FILS */
#ifndef CONFIG_NO_CONFIG_BLOBS
} else if (os_strcmp(cmd, "blob") == 0) {
ret = wpas_ctrl_set_blob(wpa_s, value);
@@ -527,11 +712,53 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_MBO
} else if (os_strcasecmp(cmd, "non_pref_chan") == 0) {
ret = wpas_mbo_update_non_pref_chan(wpa_s, value);
+ if (ret == 0) {
+ value[-1] = '=';
+ wpa_config_process_global(wpa_s->conf, cmd, -1);
+ }
} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+ } else if (os_strcasecmp(cmd, "oce") == 0) {
+ wpa_s->conf->oce = atoi(value);
+ if (wpa_s->conf->oce) {
+ if ((wpa_s->conf->oce & OCE_STA) &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
+ wpa_s->enable_oce = OCE_STA;
+
+ if ((wpa_s->conf->oce & OCE_STA_CFON) &&
+ (wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
+ /* TODO: Need to add STA-CFON support */
+ wpa_printf(MSG_ERROR,
+ "OCE STA-CFON feature is not yet supported");
+ return -1;
+ }
+ } else {
+ wpa_s->enable_oce = 0;
+ }
+ wpa_supplicant_set_default_scan_ies(wpa_s);
#endif /* CONFIG_MBO */
} else if (os_strcasecmp(cmd, "lci") == 0) {
ret = wpas_ctrl_iface_set_lci(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "tdls_trigger_control") == 0) {
+ ret = wpa_drv_set_tdls_mode(wpa_s, atoi(value));
+ } else if (os_strcasecmp(cmd, "relative_rssi") == 0) {
+ ret = wpas_ctrl_set_relative_rssi(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "relative_band_adjust") == 0) {
+ ret = wpas_ctrl_set_relative_band_adjust(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "ric_ies") == 0) {
+ ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "roaming") == 0) {
+ ret = wpa_drv_roaming(wpa_s, atoi(value), NULL);
+#ifdef CONFIG_WNM
+ } else if (os_strcasecmp(cmd, "coloc_intf_elems") == 0) {
+ struct wpabuf *elems;
+
+ elems = wpabuf_parse_bin(value);
+ if (!elems)
+ return -1;
+ wnm_set_coloc_intf_elems(wpa_s, elems);
+#endif /* CONFIG_WNM */
} else {
value[-1] = '=';
ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -577,6 +804,12 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_TESTING_GET_GTK */
} else if (os_strcmp(cmd, "tls_library") == 0) {
res = tls_get_library_version(buf, buflen);
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strcmp(cmd, "anonce") == 0) {
+ return wpa_snprintf_hex(buf, buflen,
+ wpa_sm_get_anonce(wpa_s->wpa),
+ WPA_NONCE_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
} else {
res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
}
@@ -610,27 +843,6 @@ static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
#endif /* IEEE8021X_EAPOL */
-#ifdef CONFIG_PEERKEY
-/* MLME-STKSTART.request(peer) */
-static int wpa_supplicant_ctrl_iface_stkstart(
- struct wpa_supplicant *wpa_s, char *addr)
-{
- u8 peer[ETH_ALEN];
-
- if (hwaddr_aton(addr, peer)) {
- wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid "
- "address '%s'", addr);
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR,
- MAC2STR(peer));
-
- return wpa_sm_stkstart(wpa_s->wpa, peer);
-}
-#endif /* CONFIG_PEERKEY */
-
-
#ifdef CONFIG_TDLS
static int wpa_supplicant_ctrl_iface_tdls_discover(
@@ -1914,6 +2126,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_AP */
pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
}
+#ifdef CONFIG_SME
#ifdef CONFIG_SAE
if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
#ifdef CONFIG_AP
@@ -1927,6 +2140,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
pos += ret;
}
#endif /* CONFIG_SAE */
+#endif /* CONFIG_SME */
ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
wpa_supplicant_state_txt(wpa_s->wpa_state));
if (os_snprintf_error(end - pos, ret))
@@ -2048,6 +2262,12 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
pos += res;
}
+#ifdef CONFIG_MACSEC
+ res = ieee802_1x_kay_get_status(wpa_s->kay, pos, end - pos);
+ if (res > 0)
+ pos += res;
+#endif /* CONFIG_MACSEC */
+
sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
if (sess_id) {
char *start = pos;
@@ -2081,6 +2301,13 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_WPS */
+ if (wpa_s->ieee80211ac) {
+ ret = os_snprintf(pos, end - pos, "ieee80211ac=1\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
#ifdef ANDROID
/*
* Allow using the STATUS command with default behavior, say for debug,
@@ -2437,6 +2664,59 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
}
#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+ if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+ if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#ifdef CONFIG_IEEE80211R
+ if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+ if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ if (data.key_mgmt & WPA_KEY_MGMT_OWE) {
+ ret = os_snprintf(pos, end - pos, "%sOWE",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP
+ if (data.key_mgmt & WPA_KEY_MGMT_DPP) {
+ ret = os_snprintf(pos, end - pos, "%sDPP",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#endif /* CONFIG_DPP */
+
if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
ret = os_snprintf(pos, end - pos, "%sOSEN",
pos == start ? "" : "+");
@@ -2512,7 +2792,7 @@ static int wpa_supplicant_ctrl_iface_scan_result(
{
char *pos, *end;
int ret;
- const u8 *ie, *ie2, *osen_ie, *p2p, *mesh;
+ const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe;
mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
@@ -2543,6 +2823,14 @@ static int wpa_supplicant_ctrl_iface_scan_result(
if (osen_ie)
pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
osen_ie, 2 + osen_ie[1]);
+ owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+ if (owe) {
+ ret = os_snprintf(pos, end - pos,
+ ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
ret = os_snprintf(pos, end - pos, "[WEP]");
@@ -2608,6 +2896,14 @@ static int wpa_supplicant_ctrl_iface_scan_result(
pos += ret;
}
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FILS
+ if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
+ ret = os_snprintf(pos, end - pos, "[FILS]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+#endif /* CONFIG_FILS */
#ifdef CONFIG_FST
if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
ret = os_snprintf(pos, end - pos, "[FST]");
@@ -2835,9 +3131,8 @@ static int wpa_supplicant_ctrl_iface_select_network(
if (pos) {
int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
if (freqs) {
- wpa_s->scan_req = MANUAL_SCAN_REQ;
- os_free(wpa_s->manual_scan_freqs);
- wpa_s->manual_scan_freqs = freqs;
+ os_free(wpa_s->select_network_scan_freqs);
+ wpa_s->select_network_scan_freqs = freqs;
}
}
@@ -3012,6 +3307,7 @@ static int wpa_supplicant_ctrl_iface_update_network(
return 0; /* No change to the previously configured value */
if (os_strcmp(name, "bssid") != 0 &&
+ os_strcmp(name, "bssid_hint") != 0 &&
os_strcmp(name, "priority") != 0) {
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
@@ -3647,6 +3943,50 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
pos += ret;
}
#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_OWE
+ if (capa->key_mgmt & WPA_DRIVER_CAPA_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 (capa->key_mgmt & WPA_DRIVER_CAPA_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 */
+#ifdef CONFIG_FILS
+ if (capa->key_mgmt & WPA_DRIVER_CAPA_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 (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
+ ret = os_snprintf(pos, end - pos, " FILS-SHA384");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#ifdef CONFIG_IEEE80211R
+ if (capa->key_mgmt & WPA_DRIVER_CAPA_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 (capa->key_mgmt & WPA_DRIVER_CAPA_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_IEEE80211R */
+#endif /* CONFIG_FILS */
return pos - buf;
}
@@ -3749,6 +4089,26 @@ static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ if (wpa_is_fils_supported(wpa_s)) {
+ ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITHOUT_PFS",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+#ifdef CONFIG_FILS_SK_PFS
+ if (wpa_is_fils_sk_pfs_supported(wpa_s)) {
+ ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITH_PFS",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+#endif /* CONFIG_FILS */
+
return pos - buf;
}
@@ -4006,6 +4366,27 @@ static int wpa_supplicant_ctrl_iface_get_capability(
}
#endif /* CONFIG_ACS */
+#ifdef CONFIG_FILS
+ if (os_strcmp(field, "fils") == 0) {
+#ifdef CONFIG_FILS_SK_PFS
+ if (wpa_is_fils_supported(wpa_s) &&
+ wpa_is_fils_sk_pfs_supported(wpa_s)) {
+ res = os_snprintf(buf, buflen, "FILS FILS-SK-PFS");
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ if (wpa_is_fils_supported(wpa_s)) {
+ res = os_snprintf(buf, buflen, "FILS");
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+ }
+ }
+#endif /* CONFIG_FILS */
+
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
field);
@@ -4048,13 +4429,85 @@ static char * anqp_add_hex(char *pos, char *end, const char *title,
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_FILS
+static int print_fils_indication(struct wpa_bss *bss, char *pos, char *end)
+{
+ char *start = pos;
+ const u8 *ie, *ie_end;
+ u16 info, realms;
+ int ret;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+ if (!ie)
+ return 0;
+ ie_end = ie + 2 + ie[1];
+ ie += 2;
+ if (ie_end - ie < 2)
+ return -1;
+
+ info = WPA_GET_LE16(ie);
+ ie += 2;
+ ret = os_snprintf(pos, end - pos, "fils_info=%04x\n", info);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ if (info & BIT(7)) {
+ /* Cache Identifier Included */
+ if (ie_end - ie < 2)
+ return -1;
+ ret = os_snprintf(pos, end - pos, "fils_cache_id=%02x%02x\n",
+ ie[0], ie[1]);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 2;
+ }
+
+ if (info & BIT(8)) {
+ /* HESSID Included */
+ if (ie_end - ie < ETH_ALEN)
+ return -1;
+ ret = os_snprintf(pos, end - pos, "fils_hessid=" MACSTR "\n",
+ MAC2STR(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += ETH_ALEN;
+ }
+
+ realms = (info & (BIT(3) | BIT(4) | BIT(5))) >> 3;
+ if (realms) {
+ if (ie_end - ie < realms * 2)
+ return -1;
+ ret = os_snprintf(pos, end - pos, "fils_realms=");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ret = wpa_snprintf_hex(pos, end - pos, ie, realms * 2);
+ if (ret <= 0)
+ return 0;
+ pos += ret;
+ ie += realms * 2;
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ return pos - start;
+}
+#endif /* CONFIG_FILS */
+
+
static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
unsigned long mask, char *buf, size_t buflen)
{
size_t i;
int ret;
char *pos, *end;
- const u8 *ie, *ie2, *osen_ie;
+ const u8 *ie, *ie2, *osen_ie, *mesh, *owe;
pos = buf;
end = buf + buflen;
@@ -4163,18 +4616,30 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
return 0;
pos += ret;
+ mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
+
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
if (ie)
pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie,
2 + ie[1]);
ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (ie2)
- pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2,
+ pos = wpa_supplicant_ie_txt(pos, end,
+ mesh ? "RSN" : "WPA2", ie2,
2 + ie2[1]);
osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
if (osen_ie)
pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
osen_ie, 2 + osen_ie[1]);
+ owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+ if (owe) {
+ ret = os_snprintf(
+ pos, end - pos,
+ ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
if (!ie && !ie2 && !osen_ie &&
(bss->caps & IEEE80211_CAP_PRIVACY)) {
@@ -4183,6 +4648,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
return 0;
pos += ret;
}
+
+ if (mesh) {
+ ret = os_snprintf(pos, end - pos, "[MESH]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
if (bss_is_dmg(bss)) {
const char *s;
ret = os_snprintf(pos, end - pos, "[DMG]");
@@ -4236,6 +4709,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
pos += ret;
}
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FILS
+ if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
+ ret = os_snprintf(pos, end - pos, "[FILS]");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_FILS */
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
@@ -4320,6 +4801,8 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
pos = anqp_add_hex(pos, end, "anqp_domain_name",
anqp->domain_name);
+ pos = anqp_add_hex(pos, end, "anqp_fils_realm_info",
+ anqp->fils_realm_info);
#ifdef CONFIG_HS20
pos = anqp_add_hex(pos, end, "hs20_capability_list",
anqp->hs20_capability_list);
@@ -4333,6 +4816,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
anqp->hs20_operating_class);
pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
anqp->hs20_osu_providers_list);
+ pos = anqp_add_hex(pos, end, "hs20_operator_icon_metadata",
+ anqp->hs20_operator_icon_metadata);
+ pos = anqp_add_hex(pos, end, "hs20_osu_providers_nai_list",
+ anqp->hs20_osu_providers_nai_list);
#endif /* CONFIG_HS20 */
dl_list_for_each(elem, &anqp->anqp_elems,
@@ -4381,6 +4868,44 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
}
#endif /* CONFIG_FST */
+ if (mask & WPA_BSS_MASK_UPDATE_IDX) {
+ ret = os_snprintf(pos, end - pos, "update_idx=%u\n",
+ bss->last_update_idx);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ if ((mask & WPA_BSS_MASK_BEACON_IE) && bss->beacon_ie_len) {
+ ret = os_snprintf(pos, end - pos, "beacon_ie=");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie = (const u8 *) (bss + 1);
+ ie += bss->ie_len;
+ for (i = 0; i < bss->beacon_ie_len; i++) {
+ ret = os_snprintf(pos, end - pos, "%02x", *ie++);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+#ifdef CONFIG_FILS
+ if (mask & WPA_BSS_MASK_FILS_INDICATION) {
+ ret = print_fils_indication(bss, pos, end);
+ if (ret < 0)
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_FILS */
+
if (mask & WPA_BSS_MASK_DELIM) {
ret = os_snprintf(pos, end - pos, "====\n");
if (os_snprintf_error(end - pos, ret))
@@ -4471,6 +4996,8 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
bss = dl_list_entry(next, struct wpa_bss,
list_id);
}
+ } else if (os_strncmp(cmd, "CURRENT", 7) == 0) {
+ bss = wpa_s->current_bss;
#ifdef CONFIG_P2P
} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
if (hwaddr_aton(cmd + 13, bssid) == 0)
@@ -5768,13 +6295,21 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
int max_oper_chwidth, chwidth = 0, freq2 = 0;
char *token, *context = NULL;
+#ifdef CONFIG_ACS
+ int acs = 0;
+#endif /* CONFIG_ACS */
while ((token = str_token(cmd, " ", &context))) {
- if (sscanf(token, "freq=%d", &freq) == 1 ||
- sscanf(token, "freq2=%d", &freq2) == 1 ||
+ if (sscanf(token, "freq2=%d", &freq2) == 1 ||
sscanf(token, "persistent=%d", &group_id) == 1 ||
sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
continue;
+#ifdef CONFIG_ACS
+ } else if (os_strcmp(token, "freq=acs") == 0) {
+ acs = 1;
+#endif /* CONFIG_ACS */
+ } else if (sscanf(token, "freq=%d", &freq) == 1) {
+ continue;
} else if (os_strcmp(token, "ht40") == 0) {
ht40 = 1;
} else if (os_strcmp(token, "vht") == 0) {
@@ -5790,6 +6325,24 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
}
}
+#ifdef CONFIG_ACS
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
+ (acs || freq == 2 || freq == 5)) {
+ if (freq == 2 && wpa_s->best_24_freq <= 0) {
+ wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211G;
+ wpa_s->p2p_go_do_acs = 1;
+ freq = 0;
+ } else if (freq == 5 && wpa_s->best_5_freq <= 0) {
+ wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211A;
+ wpa_s->p2p_go_do_acs = 1;
+ freq = 0;
+ } else {
+ wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211ANY;
+ wpa_s->p2p_go_do_acs = 1;
+ }
+ }
+#endif /* CONFIG_ACS */
+
max_oper_chwidth = parse_freq(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
@@ -5827,10 +6380,24 @@ static int p2p_ctrl_group_member(struct wpa_supplicant *wpa_s, const char *cmd,
}
+static int wpas_find_p2p_dev_addr_bss(struct wpa_global *global,
+ const u8 *p2p_dev_addr)
+{
+ struct wpa_supplicant *wpa_s;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_bss_get_p2p_dev_addr(wpa_s, p2p_dev_addr))
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
- u8 addr[ETH_ALEN], *addr_ptr;
+ u8 addr[ETH_ALEN], *addr_ptr, group_capab;
int next, res;
const struct p2p_peer_info *info;
char *pos, *end;
@@ -5859,6 +6426,16 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
if (info == NULL)
return -1;
+ group_capab = info->group_capab;
+
+ if (group_capab &&
+ !wpas_find_p2p_dev_addr_bss(wpa_s->global, info->p2p_device_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Could not find any BSS with p2p_dev_addr "
+ MACSTR ", hence override group_capab from 0x%x to 0",
+ MAC2STR(info->p2p_device_addr), group_capab);
+ group_capab = 0;
+ }
pos = buf;
end = buf + buflen;
@@ -5884,7 +6461,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
info->serial_number,
info->config_methods,
info->dev_capab,
- info->group_capab,
+ group_capab,
info->level);
if (os_snprintf_error(end - pos, res))
return pos - buf;
@@ -6165,6 +6742,20 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
return 0;
}
+ if (os_strcmp(cmd, "override_pref_op_chan") == 0) {
+ int op_class, chan;
+
+ op_class = atoi(param);
+ param = os_strchr(param, ':');
+ if (!param)
+ return -1;
+ param++;
+ chan = atoi(param);
+ p2p_set_override_pref_op_chan(wpa_s->global->p2p, op_class,
+ chan);
+ return 0;
+ }
+
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
cmd);
@@ -6176,6 +6767,12 @@ static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s)
{
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
wpa_s->force_long_sd = 0;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ os_free(wpa_s->get_pref_freq_list_override);
+ wpa_s->get_pref_freq_list_override = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+
wpas_p2p_stop_find(wpa_s);
wpa_s->parent->p2ps_method_config_any = 0;
if (wpa_s->global->p2p)
@@ -6383,7 +6980,7 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
u16 id[MAX_ANQP_INFO_ID];
size_t num_id = 0;
u32 subtypes = 0;
- int get_cell_pref = 0;
+ u32 mbo_subtypes = 0;
used = hwaddr_aton2(dst, dst_addr);
if (used < 0)
@@ -6404,9 +7001,10 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
} else if (os_strncmp(pos, "mbo:", 4) == 0) {
#ifdef CONFIG_MBO
int num = atoi(pos + 4);
- if (num != MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
+
+ if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE)
return -1;
- get_cell_pref = 1;
+ mbo_subtypes |= BIT(num);
#else /* CONFIG_MBO */
return -1;
#endif /* CONFIG_MBO */
@@ -6421,11 +7019,11 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
pos++;
}
- if (num_id == 0)
+ if (num_id == 0 && !subtypes && !mbo_subtypes)
return -1;
return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes,
- get_cell_pref);
+ mbo_subtypes);
}
@@ -6762,6 +7360,9 @@ static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
autoscan_init(wpa_s, 1);
else if (state == WPA_SCANNING)
wpa_supplicant_reinit_autoscan(wpa_s);
+ else
+ wpa_printf(MSG_DEBUG, "No autoscan update in state %s",
+ wpa_supplicant_state_txt(state));
return 0;
}
@@ -6824,26 +7425,41 @@ static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd)
static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
{
int query_reason, list = 0;
+ char *btm_candidates = NULL;
query_reason = atoi(cmd);
cmd = os_strchr(cmd, ' ');
if (cmd) {
- cmd++;
- if (os_strncmp(cmd, "list", 4) == 0) {
+ if (os_strncmp(cmd, " list", 5) == 0)
list = 1;
- } else {
- wpa_printf(MSG_DEBUG, "WNM Query: Invalid option %s",
- cmd);
- return -1;
- }
+ else
+ btm_candidates = cmd;
}
wpa_printf(MSG_DEBUG,
"CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s",
query_reason, list ? " candidate list" : "");
- return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, list);
+ return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason,
+ btm_candidates,
+ list);
+}
+
+
+static int wpas_ctrl_iface_coloc_intf_report(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ struct wpabuf *elems;
+ int ret;
+
+ elems = wpabuf_parse_bin(cmd);
+ if (!elems)
+ return -1;
+
+ ret = wnm_send_coloc_intf_report(wpa_s, 0, elems);
+ wpabuf_free(elems);
+ return ret;
}
#endif /* CONFIG_WNM */
@@ -6879,10 +7495,17 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
pos += ret;
}
- if (si.center_frq1 > 0 && si.center_frq2 > 0) {
- ret = os_snprintf(pos, end - pos,
- "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n",
- si.center_frq1, si.center_frq2);
+ if (si.center_frq1 > 0) {
+ ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n",
+ si.center_frq1);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ if (si.center_frq2 > 0) {
+ ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n",
+ si.center_frq2);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
@@ -6930,6 +7553,46 @@ static int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s,
}
+#ifdef CONFIG_TESTING_OPTIONS
+int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ unsigned int *freq_list)
+{
+ char *pos = wpa_s->get_pref_freq_list_override;
+ char *end;
+ unsigned int count = 0;
+
+ /* Override string format:
+ * <if_type1>:<freq1>,<freq2>,... <if_type2>:... */
+
+ while (pos) {
+ if (atoi(pos) == (int) if_type)
+ break;
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+ if (!pos)
+ return -1;
+ pos = os_strchr(pos, ':');
+ if (!pos)
+ return -1;
+ pos++;
+ end = os_strchr(pos, ' ');
+ while (pos && (!end || pos < end) && count < *num) {
+ freq_list[count++] = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos)
+ pos++;
+ }
+
+ *num = count;
+ return 0;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
static int wpas_ctrl_iface_get_pref_freq_list(
struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
{
@@ -7116,7 +7779,8 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
- wpas_abort_ongoing_scan(wpa_s);
+ if (wpas_abort_ongoing_scan(wpa_s) == 0)
+ wpa_s->ignore_post_flush_scan_res = 1;
if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
/*
@@ -7157,6 +7821,22 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
wpa_s->after_wps = 0;
wpa_s->known_wps_freq = 0;
+#ifdef CONFIG_DPP
+ wpas_dpp_deinit(wpa_s);
+ wpa_s->dpp_init_max_tries = 0;
+ wpa_s->dpp_init_retry_time = 0;
+ wpa_s->dpp_resp_wait_time = 0;
+ wpa_s->dpp_resp_max_tries = 0;
+ wpa_s->dpp_resp_retry_time = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ os_memset(dpp_pkex_own_mac_override, 0, ETH_ALEN);
+ os_memset(dpp_pkex_peer_mac_override, 0, ETH_ALEN);
+ dpp_pkex_ephemeral_key_override_len = 0;
+ dpp_protocol_key_override_len = 0;
+ dpp_nonce_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
+
#ifdef CONFIG_TDLS
#ifdef CONFIG_TDLS_TESTING
tdls_testing = 0;
@@ -7218,13 +7898,29 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
wpa_s->p2p_go_csa_on_inv = 0;
wpa_s->ignore_auth_resp = 0;
wpa_s->ignore_assoc_disallow = 0;
+ wpa_s->testing_resend_assoc = 0;
wpa_s->reject_btm_req_reason = 0;
wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
+ os_free(wpa_s->get_pref_freq_list_override);
+ wpa_s->get_pref_freq_list_override = NULL;
+ wpabuf_free(wpa_s->sae_commit_override);
+ wpa_s->sae_commit_override = NULL;
+#ifdef CONFIG_DPP
+ os_free(wpa_s->dpp_config_obj_override);
+ wpa_s->dpp_config_obj_override = NULL;
+ os_free(wpa_s->dpp_discovery_override);
+ wpa_s->dpp_discovery_override = NULL;
+ os_free(wpa_s->dpp_groups_override);
+ wpa_s->dpp_groups_override = NULL;
+ dpp_test = DPP_TEST_DISABLED;
+#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
wpa_s->disconnected = 0;
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
+ os_free(wpa_s->select_network_scan_freqs);
+ wpa_s->select_network_scan_freqs = NULL;
wpa_bss_flush(wpa_s);
if (!dl_list_empty(&wpa_s->bss)) {
@@ -7242,6 +7938,9 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
#ifdef CONFIG_SME
wpa_s->sme.last_unprot_disconnect.sec = 0;
#endif /* CONFIG_SME */
+
+ wpabuf_free(wpa_s->ric_ies);
+ wpa_s->ric_ies = NULL;
}
@@ -7539,6 +8238,19 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
goto done;
}
+ pos = os_strstr(params, "bssid=");
+ if (pos) {
+ u8 bssid[ETH_ALEN];
+
+ pos += 6;
+ if (hwaddr_aton(pos, bssid)) {
+ wpa_printf(MSG_ERROR, "Invalid BSSID %s", pos);
+ *reply_len = -1;
+ goto done;
+ }
+ os_memcpy(wpa_s->next_scan_bssid, bssid, ETH_ALEN);
+ }
+
pos = params;
while (pos && *pos != '\0') {
if (os_strncmp(pos, "ssid ", 5) == 0) {
@@ -7824,6 +8536,124 @@ static int wpas_ctrl_iface_mgmt_rx_process(struct wpa_supplicant *wpa_s,
}
+static int wpas_ctrl_iface_driver_scan_res(struct wpa_supplicant *wpa_s,
+ char *param)
+{
+ struct wpa_scan_res *res;
+ struct os_reltime now;
+ char *pos, *end;
+ int ret = -1;
+
+ if (!param)
+ return -1;
+
+ if (os_strcmp(param, "START") == 0) {
+ wpa_bss_update_start(wpa_s);
+ return 0;
+ }
+
+ if (os_strcmp(param, "END") == 0) {
+ wpa_bss_update_end(wpa_s, NULL, 1);
+ return 0;
+ }
+
+ if (os_strncmp(param, "BSS ", 4) != 0)
+ return -1;
+ param += 3;
+
+ res = os_zalloc(sizeof(*res) + os_strlen(param) / 2);
+ if (!res)
+ return -1;
+
+ pos = os_strstr(param, " flags=");
+ if (pos)
+ res->flags = strtol(pos + 7, NULL, 16);
+
+ pos = os_strstr(param, " bssid=");
+ if (pos && hwaddr_aton(pos + 7, res->bssid))
+ goto fail;
+
+ pos = os_strstr(param, " freq=");
+ if (pos)
+ res->freq = atoi(pos + 6);
+
+ pos = os_strstr(param, " beacon_int=");
+ if (pos)
+ res->beacon_int = atoi(pos + 12);
+
+ pos = os_strstr(param, " caps=");
+ if (pos)
+ res->caps = strtol(pos + 6, NULL, 16);
+
+ pos = os_strstr(param, " qual=");
+ if (pos)
+ res->qual = atoi(pos + 6);
+
+ pos = os_strstr(param, " noise=");
+ if (pos)
+ res->noise = atoi(pos + 7);
+
+ pos = os_strstr(param, " level=");
+ if (pos)
+ res->level = atoi(pos + 7);
+
+ pos = os_strstr(param, " tsf=");
+ if (pos)
+ res->tsf = strtoll(pos + 5, NULL, 16);
+
+ pos = os_strstr(param, " age=");
+ if (pos)
+ res->age = atoi(pos + 5);
+
+ pos = os_strstr(param, " est_throughput=");
+ if (pos)
+ res->est_throughput = atoi(pos + 16);
+
+ pos = os_strstr(param, " snr=");
+ if (pos)
+ res->snr = atoi(pos + 5);
+
+ pos = os_strstr(param, " parent_tsf=");
+ if (pos)
+ res->parent_tsf = strtoll(pos + 7, NULL, 16);
+
+ pos = os_strstr(param, " tsf_bssid=");
+ if (pos && hwaddr_aton(pos + 11, res->tsf_bssid))
+ goto fail;
+
+ pos = os_strstr(param, " ie=");
+ if (pos) {
+ pos += 4;
+ end = os_strchr(pos, ' ');
+ if (!end)
+ end = pos + os_strlen(pos);
+ res->ie_len = (end - pos) / 2;
+ if (hexstr2bin(pos, (u8 *) (res + 1), res->ie_len))
+ goto fail;
+ }
+
+ pos = os_strstr(param, " beacon_ie=");
+ if (pos) {
+ pos += 11;
+ end = os_strchr(pos, ' ');
+ if (!end)
+ end = pos + os_strlen(pos);
+ res->beacon_ie_len = (end - pos) / 2;
+ if (hexstr2bin(pos, ((u8 *) (res + 1)) + res->ie_len,
+ res->beacon_ie_len))
+ goto fail;
+ }
+
+ os_get_reltime(&now);
+ wpa_bss_update_scan_res(wpa_s, res, &now);
+ ret = 0;
+fail:
+ os_free(res);
+
+ return ret;
+}
+
+
static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos, *param;
@@ -7854,6 +8684,8 @@ static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
wpa_supplicant_event(wpa_s, ev, &event);
os_free(event.freq_range.range);
return 0;
+ } else if (os_strcmp(cmd, "SCAN_RES") == 0) {
+ return wpas_ctrl_iface_driver_scan_res(wpa_s, param);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s",
cmd);
@@ -8218,6 +9050,79 @@ static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s,
return 0;
}
+
+static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
+{
+ u8 zero[WPA_TK_MAX_LEN];
+
+ if (wpa_s->last_tk_alg == WPA_ALG_NONE)
+ return -1;
+
+ wpa_printf(MSG_INFO, "TESTING: Reset PN");
+ os_memset(zero, 0, sizeof(zero));
+
+ /* First, use a zero key to avoid any possible duplicate key avoidance
+ * in the driver. */
+ if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+ wpa_s->last_tk_key_idx, 1, zero, 6,
+ zero, wpa_s->last_tk_len) < 0)
+ return -1;
+
+ /* Set the previously configured key to reset its TSC/RSC */
+ return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+ wpa_s->last_tk_key_idx, 1, zero, 6,
+ wpa_s->last_tk, wpa_s->last_tk_len);
+}
+
+
+static int wpas_ctrl_key_request(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ const char *pos = cmd;
+ int error, pairwise;
+
+ error = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pairwise = atoi(pos);
+ wpa_sm_key_request(wpa_s->wpa, error, pairwise);
+ return 0;
+}
+
+
+static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_SME
+ struct wpa_driver_associate_params params;
+ int ret;
+
+ os_memset(&params, 0, sizeof(params));
+ params.bssid = wpa_s->bssid;
+ params.ssid = wpa_s->sme.ssid;
+ params.ssid_len = wpa_s->sme.ssid_len;
+ params.freq.freq = wpa_s->sme.freq;
+ if (wpa_s->last_assoc_req_wpa_ie) {
+ params.wpa_ie = wpabuf_head(wpa_s->last_assoc_req_wpa_ie);
+ params.wpa_ie_len = wpabuf_len(wpa_s->last_assoc_req_wpa_ie);
+ }
+ params.pairwise_suite = wpa_s->pairwise_cipher;
+ params.group_suite = wpa_s->group_cipher;
+ params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
+ params.key_mgmt_suite = wpa_s->key_mgmt;
+ params.wpa_proto = wpa_s->wpa_proto;
+ params.mgmt_frame_protection = wpa_s->sme.mfp;
+ params.rrm_used = wpa_s->rrm.rrm_used;
+ if (wpa_s->sme.prev_bssid_set)
+ params.prev_bssid = wpa_s->sme.prev_bssid;
+ wpa_printf(MSG_INFO, "TESTING: Resend association request");
+ ret = wpa_drv_associate(wpa_s, &params);
+ wpa_s->testing_resend_assoc = 1;
+ return ret;
+#else /* CONFIG_SME */
+ return -1;
+#endif /* CONFIG_SME */
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
@@ -8641,6 +9546,248 @@ static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s)
}
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+
+static int wpas_ctrl_iface_pmksa_get(struct wpa_supplicant *wpa_s,
+ const char *cmd, char *buf, size_t buflen)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ struct wpa_ssid *ssid;
+ char *pos, *pos2, *end;
+ int ret;
+ struct os_reltime now;
+
+ ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
+ if (!ssid)
+ return -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ os_get_reltime(&now);
+
+ /*
+ * Entry format:
+ * <BSSID> <PMKID> <PMK> <reauth_time in seconds>
+ * <expiration in seconds> <akmp> <opportunistic>
+ * [FILS Cache Identifier]
+ */
+
+ for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry;
+ entry = entry->next) {
+ if (entry->network_ctx != ssid)
+ continue;
+
+ pos2 = pos;
+ ret = os_snprintf(pos2, end - pos2, MACSTR " ",
+ MAC2STR(entry->aa));
+ if (os_snprintf_error(end - pos2, ret))
+ break;
+ pos2 += ret;
+
+ pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmkid,
+ PMKID_LEN);
+
+ ret = os_snprintf(pos2, end - pos2, " ");
+ if (os_snprintf_error(end - pos2, ret))
+ break;
+ pos2 += ret;
+
+ pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmk,
+ entry->pmk_len);
+
+ ret = os_snprintf(pos2, end - pos2, " %d %d %d %d",
+ (int) (entry->reauth_time - now.sec),
+ (int) (entry->expiration - now.sec),
+ entry->akmp,
+ entry->opportunistic);
+ if (os_snprintf_error(end - pos2, ret))
+ break;
+ pos2 += ret;
+
+ if (entry->fils_cache_id_set) {
+ ret = os_snprintf(pos2, end - pos2, " %02x%02x",
+ entry->fils_cache_id[0],
+ entry->fils_cache_id[1]);
+ if (os_snprintf_error(end - pos2, ret))
+ break;
+ pos2 += ret;
+ }
+
+ ret = os_snprintf(pos2, end - pos2, "\n");
+ if (os_snprintf_error(end - pos2, ret))
+ break;
+ pos2 += ret;
+
+ pos = pos2;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ struct wpa_ssid *ssid;
+ char *pos, *pos2;
+ int ret = -1;
+ struct os_reltime now;
+ int reauth_time = 0, expiration = 0, i;
+
+ /*
+ * Entry format:
+ * <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds>
+ * <expiration in seconds> <akmp> <opportunistic>
+ * [FILS Cache Identifier]
+ */
+
+ ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
+ if (!ssid)
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (!entry)
+ return -1;
+
+ if (hwaddr_aton(pos, entry->aa))
+ goto fail;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ goto fail;
+ pos++;
+
+ if (hexstr2bin(pos, entry->pmkid, PMKID_LEN) < 0)
+ goto fail;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ goto fail;
+ pos++;
+
+ pos2 = os_strchr(pos, ' ');
+ if (!pos2)
+ goto fail;
+ entry->pmk_len = (pos2 - pos) / 2;
+ if (entry->pmk_len < PMK_LEN || entry->pmk_len > PMK_LEN_MAX ||
+ hexstr2bin(pos, entry->pmk, entry->pmk_len) < 0)
+ goto fail;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ goto fail;
+ pos++;
+
+ if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration,
+ &entry->akmp, &entry->opportunistic) != 4)
+ goto fail;
+ for (i = 0; i < 4; i++) {
+ pos = os_strchr(pos, ' ');
+ if (!pos) {
+ if (i < 3)
+ goto fail;
+ break;
+ }
+ pos++;
+ }
+ if (pos) {
+ if (hexstr2bin(pos, entry->fils_cache_id,
+ FILS_CACHE_ID_LEN) < 0)
+ goto fail;
+ entry->fils_cache_id_set = 1;
+ }
+ os_get_reltime(&now);
+ entry->expiration = now.sec + expiration;
+ entry->reauth_time = now.sec + reauth_time;
+
+ entry->network_ctx = ssid;
+
+ wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+ entry = NULL;
+ ret = 0;
+fail:
+ os_free(entry);
+ return ret;
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpas_ctrl_iface_mesh_pmksa_get(struct wpa_supplicant *wpa_s,
+ const char *cmd, char *buf,
+ size_t buflen)
+{
+ u8 spa[ETH_ALEN];
+
+ if (!wpa_s->ifmsh)
+ return -1;
+
+ if (os_strcasecmp(cmd, "any") == 0)
+ return wpas_ap_pmksa_cache_list_mesh(wpa_s, NULL, buf, buflen);
+
+ if (hwaddr_aton(cmd, spa))
+ return -1;
+
+ return wpas_ap_pmksa_cache_list_mesh(wpa_s, spa, buf, buflen);
+}
+
+
+static int wpas_ctrl_iface_mesh_pmksa_add(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ /*
+ * We do not check mesh interface existance because PMKSA should be
+ * stored before wpa_s->ifmsh creation to suppress commit message
+ * creation.
+ */
+ return wpas_ap_pmksa_cache_add_external(wpa_s, cmd);
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+#ifdef CONFIG_FILS
+static int wpas_ctrl_iface_fils_hlp_req_add(struct wpa_supplicant *wpa_s,
+ const char *cmd)
+{
+ struct fils_hlp_req *req;
+ const char *pos;
+
+ /* format: <dst> <packet starting from ethertype> */
+
+ req = os_zalloc(sizeof(*req));
+ if (!req)
+ return -1;
+
+ if (hwaddr_aton(cmd, req->dst))
+ goto fail;
+
+ pos = os_strchr(cmd, ' ');
+ if (!pos)
+ goto fail;
+ pos++;
+ req->pkt = wpabuf_parse_bin(pos);
+ if (!req->pkt)
+ goto fail;
+
+ dl_list_add_tail(&wpa_s->fils_hlp_req, &req->list);
+ return 0;
+fail:
+ wpabuf_free(req->pkt);
+ os_free(req);
+ return -1;
+}
+#endif /* CONFIG_FILS */
+
+
static int wpas_ctrl_cmd_debug_level(const char *cmd)
{
if (os_strcmp(cmd, "PING") == 0 ||
@@ -8662,7 +9809,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
int reply_len;
if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
- os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+ os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
+ os_strncmp(buf, "PMKSA_ADD ", 10) == 0 ||
+ os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
if (wpa_debug_show_keys)
wpa_dbg(wpa_s, MSG_DEBUG,
"Control interface command '%s'", buf);
@@ -8671,7 +9820,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
"Control interface command '%s [REMOVED]'",
os_strncmp(buf, WPA_CTRL_RSP,
os_strlen(WPA_CTRL_RSP)) == 0 ?
- WPA_CTRL_RSP : "SET_NETWORK");
+ WPA_CTRL_RSP :
+ (os_strncmp(buf, "SET_NETWORK ", 12) == 0 ?
+ "SET_NETWORK" : "key-add"));
} else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
@@ -8715,6 +9866,22 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size);
} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
wpas_ctrl_iface_pmksa_flush(wpa_s);
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+ } else if (os_strncmp(buf, "PMKSA_GET ", 10) == 0) {
+ reply_len = wpas_ctrl_iface_pmksa_get(wpa_s, buf + 10,
+ reply, reply_size);
+ } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
+ if (wpas_ctrl_iface_pmksa_add(wpa_s, buf + 10) < 0)
+ reply_len = -1;
+#ifdef CONFIG_MESH
+ } else if (os_strncmp(buf, "MESH_PMKSA_GET ", 15) == 0) {
+ reply_len = wpas_ctrl_iface_mesh_pmksa_get(wpa_s, buf + 15,
+ reply, reply_size);
+ } else if (os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
+ if (wpas_ctrl_iface_mesh_pmksa_add(wpa_s, buf + 15) < 0)
+ reply_len = -1;
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
} else if (os_strncmp(buf, "SET ", 4) == 0) {
if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
reply_len = -1;
@@ -8751,11 +9918,6 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
reply_len = -1;
#endif /* IEEE8021X_EAPOL */
-#ifdef CONFIG_PEERKEY
- } else if (os_strncmp(buf, "STKSTART ", 9) == 0) {
- if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9))
- reply_len = -1;
-#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_IEEE80211R
} else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
@@ -9286,6 +10448,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) {
if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14))
reply_len = -1;
+ } else if (os_strncmp(buf, "COLOC_INTF_REPORT ", 18) == 0) {
+ if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
+ reply_len = -1;
#endif /* CONFIG_WNM */
} else if (os_strcmp(buf, "FLUSH") == 0) {
wpa_supplicant_ctrl_iface_flush(wpa_s);
@@ -9332,6 +10497,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) {
if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0)
reply_len = -1;
+ } else if (os_strcmp(buf, "RESET_PN") == 0) {
+ if (wpas_ctrl_reset_pn(wpa_s) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "KEY_REQUEST ", 12) == 0) {
+ if (wpas_ctrl_key_request(wpa_s, buf + 12) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "RESEND_ASSOC") == 0) {
+ if (wpas_ctrl_resend_assoc(wpa_s) < 0)
+ reply_len = -1;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
@@ -9353,6 +10527,97 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
reply_len = wpas_ctrl_iface_get_pref_freq_list(
wpa_s, buf + 19, reply, reply_size);
+#ifdef CONFIG_FILS
+ } else if (os_strncmp(buf, "FILS_HLP_REQ_ADD ", 17) == 0) {
+ if (wpas_ctrl_iface_fils_hlp_req_add(wpa_s, buf + 17))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) {
+ wpas_flush_fils_hlp_req(wpa_s);
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_DPP
+ } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
+ int res;
+
+ res = wpas_dpp_qr_code(wpa_s, 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) {
+ int res;
+
+ res = wpas_dpp_bootstrap_gen(wpa_s, 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 (wpas_dpp_bootstrap_remove(wpa_s, buf + 21) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
+ const char *uri;
+
+ uri = wpas_dpp_bootstrap_get_uri(wpa_s, atoi(buf + 22));
+ 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 = wpas_dpp_bootstrap_info(wpa_s, atoi(buf + 19),
+ reply, reply_size);
+ } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
+ if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
+ if (wpas_dpp_listen(wpa_s, buf + 11) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
+ wpas_dpp_stop(wpa_s);
+ wpas_dpp_listen_stop(wpa_s);
+ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
+ int res;
+
+ res = wpas_dpp_configurator_add(wpa_s, 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 (wpas_dpp_configurator_remove(wpa_s, buf + 24) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
+ if (wpas_dpp_configurator_sign(wpa_s, buf + 22) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) {
+ reply_len = wpas_dpp_configurator_get_key(wpa_s, atoi(buf + 25),
+ reply, reply_size);
+ } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
+ int res;
+
+ res = wpas_dpp_pkex_add(wpa_s, 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 (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+#endif /* CONFIG_DPP */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -9662,12 +10927,16 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
"P2P_CANCEL",
"P2P_PRESENCE_REQ",
"P2P_EXT_LISTEN",
+#ifdef CONFIG_AP
+ "STA-FIRST",
+#endif /* CONFIG_AP */
NULL
};
static const char * prefix[] = {
#ifdef ANDROID
"DRIVER ",
#endif /* ANDROID */
+ "GET_CAPABILITY ",
"GET_NETWORK ",
"REMOVE_NETWORK ",
"P2P_FIND ",
@@ -9699,6 +10968,10 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
"NFC_REPORT_HANDOVER ",
"P2P_ASP_PROVISION ",
"P2P_ASP_PROVISION_RESP ",
+#ifdef CONFIG_AP
+ "STA ",
+ "STA-NEXT ",
+#endif /* CONFIG_AP */
NULL
};
int found = 0;
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
index 54e0e2fac583..9c0a47e63936 100644
--- a/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -319,13 +319,12 @@ static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len)
}
os_free(dst->rsp_buf);
- dst->rsp_buf = os_malloc(send_len);
+ dst->rsp_buf = os_memdup(send_buf, send_len);
if (dst->rsp_buf == NULL) {
ctrl_close_pipe(dst);
os_free(reply);
return;
}
- os_memcpy(dst->rsp_buf, send_buf, send_len);
os_free(reply);
if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
@@ -739,13 +738,12 @@ static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst,
}
os_free(dst->rsp_buf);
- dst->rsp_buf = os_malloc(send_len);
+ dst->rsp_buf = os_memdup(send_buf, send_len);
if (dst->rsp_buf == NULL) {
global_close_pipe(dst);
os_free(reply);
return;
}
- os_memcpy(dst->rsp_buf, send_buf, send_len);
os_free(reply);
if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 0dc0937ff0aa..8a6057a82bfe 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -219,7 +219,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
- char buf[256], *pos;
+ char buf[4096], *pos;
int res;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 from;
@@ -600,7 +600,7 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
{
struct wpa_global *global = eloop_ctx;
struct ctrl_iface_global_priv *priv = sock_ctx;
- char buf[256], *pos;
+ char buf[4096], *pos;
int res;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 from;
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 4db712fff7bb..b88c80a99551 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -103,7 +103,7 @@ static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
struct sockaddr_storage *from,
socklen_t fromlen, int global)
{
- return ctrl_iface_attach(ctrl_dst, from, fromlen);
+ return ctrl_iface_attach(ctrl_dst, from, fromlen, NULL);
}
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 27b3012aede8..d4deb0fe35f0 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -793,6 +793,144 @@ nomem:
#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_MESH
+
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (!iface || !wpa_s->dbus_new_path)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_MESH,
+ "MeshGroupStarted");
+ if (!msg)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+ (const char *) ssid->ssid,
+ ssid->ssid_len) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+ const u8 *meshid, u8 meshid_len,
+ int reason)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (!iface || !wpa_s->dbus_new_path)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_MESH,
+ "MeshGroupRemoved");
+ if (!msg)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+ (const char *) meshid,
+ meshid_len) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "DisconnectReason",
+ reason) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (!iface || !wpa_s->dbus_new_path)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_MESH,
+ "MeshPeerConnected");
+ if (!msg)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "PeerAddress",
+ (const char *) peer_addr,
+ ETH_ALEN) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int reason)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (!iface || !wpa_s->dbus_new_path)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_MESH,
+ "MeshPeerDisconnected");
+ if (!msg)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "PeerAddress",
+ (const char *) peer_addr,
+ ETH_ALEN) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "DisconnectReason",
+ reason) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_MESH */
+
+
void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
int depth, const char *subject,
const char *altsubject[],
@@ -1256,9 +1394,12 @@ static void peer_groups_changed(struct wpa_supplicant *wpa_s)
* @wpa_s: %wpa_supplicant network interface data
* @client: this device is P2P client
* @persistent: 0 - non persistent group, 1 - persistent group
+ * @ip: When group role is client, it contains local IP address, netmask, and
+ * GO's IP address, if assigned; otherwise, NULL
*/
void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
- int client, int persistent)
+ int client, int persistent,
+ const u8 *ip)
{
DBusMessage *msg;
DBusMessageIter iter, dict_iter;
@@ -1300,6 +1441,13 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
!wpa_dbus_dict_append_bool(&dict_iter, "persistent", persistent) ||
!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
wpa_s->dbus_groupobj_path) ||
+ (ip &&
+ (!wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddr",
+ (char *) ip, 4) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrMask",
+ (char *) ip + 4, 4) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrGo",
+ (char *) ip + 8, 4))) ||
!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
} else {
@@ -1879,6 +2027,9 @@ void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
if (iface == NULL)
return;
+ if (wpa_s->p2p_mgmt)
+ wpa_s = wpa_s->parent;
+
msg = dbus_message_new_signal(wpa_s->dbus_new_path,
WPAS_DBUS_NEW_IFACE_P2PDEVICE,
"GroupFormationFailure");
@@ -1920,6 +2071,9 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
if (iface == NULL)
return;
+ if (wpa_s->p2p_mgmt)
+ wpa_s = wpa_s->parent;
+
msg = dbus_message_new_signal(wpa_s->dbus_new_path,
WPAS_DBUS_NEW_IFACE_P2PDEVICE,
"InvitationReceived");
@@ -3071,6 +3225,20 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
END_ARGS
}
},
+ { "TDLSChannelSwitch", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_tdls_channel_switch,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "TDLSCancelChannelSwitch", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_tdls_cancel_channel_switch,
+ {
+ { "peer_address", "s", ARG_IN },
+ END_ARGS
+ }
+ },
#endif /* CONFIG_TDLS */
{ "VendorElemAdd", WPAS_DBUS_NEW_IFACE_INTERFACE,
(WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_add,
@@ -3104,6 +3272,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
}
},
#endif /* CONFIG_NO_CONFIG_WRITE */
+ { "AbortScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_abort_scan,
+ {
+ END_ARGS
+ }
+ },
{ NULL, NULL, NULL, { END_ARGS } }
};
@@ -3224,6 +3398,42 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
wpas_dbus_setter_config_methods,
NULL
},
+ {
+ "DeviceName", WPAS_DBUS_NEW_IFACE_WPS, "s",
+ wpas_dbus_getter_wps_device_name,
+ wpas_dbus_setter_wps_device_name,
+ NULL
+ },
+ {
+ "Manufacturer", WPAS_DBUS_NEW_IFACE_WPS, "s",
+ wpas_dbus_getter_wps_manufacturer,
+ wpas_dbus_setter_wps_manufacturer,
+ NULL
+ },
+ {
+ "ModelName", WPAS_DBUS_NEW_IFACE_WPS, "s",
+ wpas_dbus_getter_wps_device_model_name,
+ wpas_dbus_setter_wps_device_model_name,
+ NULL
+ },
+ {
+ "ModelNumber", WPAS_DBUS_NEW_IFACE_WPS, "s",
+ wpas_dbus_getter_wps_device_model_number,
+ wpas_dbus_setter_wps_device_model_number,
+ NULL
+ },
+ {
+ "SerialNumber", WPAS_DBUS_NEW_IFACE_WPS, "s",
+ wpas_dbus_getter_wps_device_serial_number,
+ wpas_dbus_setter_wps_device_serial_number,
+ NULL
+ },
+ {
+ "DeviceType", WPAS_DBUS_NEW_IFACE_WPS, "ay",
+ wpas_dbus_getter_wps_device_device_type,
+ wpas_dbus_setter_wps_device_device_type,
+ NULL
+ },
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
{ "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
@@ -3267,6 +3477,18 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
NULL,
NULL
},
+#ifdef CONFIG_MESH
+ { "MeshPeers", WPAS_DBUS_NEW_IFACE_MESH, "aay",
+ wpas_dbus_getter_mesh_peers,
+ NULL,
+ NULL
+ },
+ { "MeshGroup", WPAS_DBUS_NEW_IFACE_MESH, "ay",
+ wpas_dbus_getter_mesh_group,
+ NULL,
+ NULL
+ },
+#endif /* CONFIG_MESH */
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -3544,6 +3766,32 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
END_ARGS
}
},
+#ifdef CONFIG_MESH
+ { "MeshGroupStarted", WPAS_DBUS_NEW_IFACE_MESH,
+ {
+ { "args", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "MeshGroupRemoved", WPAS_DBUS_NEW_IFACE_MESH,
+ {
+ { "args", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "MeshPeerConnected", WPAS_DBUS_NEW_IFACE_MESH,
+ {
+ { "args", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "MeshPeerDisconnected", WPAS_DBUS_NEW_IFACE_MESH,
+ {
+ { "args", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_MESH */
{ NULL, NULL, { END_ARGS } }
};
@@ -4012,7 +4260,13 @@ void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s)
iface = wpa_s->global->dbus;
/* Do nothing if the control interface is not turned on */
- if (iface == NULL || !wpa_s->dbus_new_path)
+ if (iface == NULL)
+ return;
+
+ if (wpa_s->p2p_mgmt)
+ wpa_s = wpa_s->parent;
+
+ if (!wpa_s->dbus_new_path)
return;
msg = dbus_message_new_signal(wpa_s->dbus_new_path,
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index d64fceef718c..40ae133b225e 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -64,6 +64,8 @@ enum wpas_dbus_bss_prop {
#define WPAS_DBUS_NEW_IFACE_P2PDEVICE \
WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
+#define WPAS_DBUS_NEW_IFACE_MESH WPAS_DBUS_NEW_IFACE_INTERFACE ".Mesh"
+
/*
* Groups correspond to P2P groups where this device is a GO (owner)
*/
@@ -190,7 +192,8 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
const u8 *src, u16 dev_passwd_id,
u8 go_intent);
void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
- int client, int persistent);
+ int client, int persistent,
+ const u8 *ip);
void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
const char *reason);
void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
@@ -237,6 +240,15 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
const u8 *sa, const u8 *dev_addr,
const u8 *bssid, int id,
int op_freq);
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+ const u8 *meshid, u8 meshid_len,
+ int reason);
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr);
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int reason);
#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
@@ -400,7 +412,8 @@ static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
static inline void
wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
- int client, int persistent)
+ int client, int persistent,
+ const u8 *ip)
{
}
@@ -551,6 +564,31 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
{
}
+static inline
+void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
+ const u8 *meshid, u8 meshid_len,
+ int reason)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr)
+{
+}
+
+static inline
+void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int reason)
+{
+}
+
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
#endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index e11dd36ca23c..94773b329133 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -28,6 +28,10 @@
#include "dbus_dict_helpers.h"
#include "dbus_common_i.h"
#include "drivers/driver.h"
+#ifdef CONFIG_MESH
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#endif /* CONFIG_MESH */
static const char * const debug_strings[] = {
"excessive", "msgdump", "debug", "info", "warning", "error", NULL
@@ -517,6 +521,27 @@ dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
/**
+ * wpas_dbus_string_property_getter - Get string type property
+ * @iter: Message iter to use when appending arguments
+ * @val: Pointer to place holding property value, can be %NULL
+ * @error: On failure an error describing the failure
+ * Returns: TRUE if the request was successful, FALSE if it failed
+ *
+ * Generic getter for string type properties. %NULL is converted to an empty
+ * string.
+ */
+dbus_bool_t wpas_dbus_string_property_getter(DBusMessageIter *iter,
+ const void *val,
+ DBusError *error)
+{
+ if (!val)
+ val = "";
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &val, error);
+}
+
+
+/**
* wpas_dbus_handler_create_interface - Request registration of a network iface
* @message: Pointer to incoming dbus message
* @global: %wpa_supplicant global data structure
@@ -955,8 +980,21 @@ dbus_bool_t wpas_dbus_getter_global_capabilities(
const struct wpa_dbus_property_desc *property_desc,
DBusMessageIter *iter, DBusError *error, void *user_data)
{
- const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
+ const char *capabilities[10] = { NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL };
size_t num_items = 0;
+#ifdef CONFIG_FILS
+ struct wpa_global *global = user_data;
+ struct wpa_supplicant *wpa_s;
+ int fils_supported = 0, fils_sk_pfs_supported = 0;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_is_fils_supported(wpa_s))
+ fils_supported = 1;
+ if (wpa_is_fils_sk_pfs_supported(wpa_s))
+ fils_sk_pfs_supported = 1;
+ }
+#endif /* CONFIG_FILS */
#ifdef CONFIG_AP
capabilities[num_items++] = "ap";
@@ -970,6 +1008,24 @@ dbus_bool_t wpas_dbus_getter_global_capabilities(
#ifdef CONFIG_INTERWORKING
capabilities[num_items++] = "interworking";
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_IEEE80211W
+ capabilities[num_items++] = "pmf";
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_MESH
+ capabilities[num_items++] = "mesh";
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_FILS
+ if (fils_supported)
+ capabilities[num_items++] = "fils";
+ if (fils_sk_pfs_supported)
+ capabilities[num_items++] = "fils_sk_pfs";
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+ capabilities[num_items++] = "ft";
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SHA384
+ capabilities[num_items++] = "sha384";
+#endif /* CONFIG_SHA384 */
return wpas_dbus_simple_array_property_getter(iter,
DBUS_TYPE_STRING,
@@ -1052,12 +1108,11 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
}
if (len != 0) {
- ssid = os_malloc(len);
+ ssid = os_memdup(val, len);
if (ssid == NULL) {
*reply = wpas_dbus_error_no_memory(message);
return -1;
}
- os_memcpy(ssid, val, len);
} else {
/* Allow zero-length SSIDs */
ssid = NULL;
@@ -1396,6 +1451,27 @@ out:
}
+/*
+ * wpas_dbus_handler_abort_scan - Request an ongoing scan to be aborted
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: Abort failed or no scan in progress DBus error message on failure
+ * or NULL otherwise.
+ *
+ * Handler function for "AbortScan" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ if (wpas_abort_ongoing_scan(wpa_s) < 0)
+ return dbus_message_new_error(
+ message, WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
+ "Abort failed or no scan in progress");
+
+ return NULL;
+}
+
+
/**
* wpas_dbus_handler_signal_poll - Request immediate signal properties
* @message: Pointer to incoming dbus message
@@ -1903,13 +1979,12 @@ DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
goto err;
}
- blob->data = os_malloc(blob_len);
+ blob->data = os_memdup(blob_data, blob_len);
blob->name = os_strdup(blob_name);
if (!blob->data || !blob->name) {
reply = wpas_dbus_error_no_memory(message);
goto err;
}
- os_memcpy(blob->data, blob_data, blob_len);
blob->len = blob_len;
wpa_config_set_blob(wpa_s->conf, blob);
@@ -2270,6 +2345,156 @@ DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
return NULL;
}
+/*
+ * wpas_dbus_handler_tdls_channel_switch - Enable channel switching with TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSChannelSwitch" method call of network interface.
+ */
+DBusMessage *
+wpas_dbus_handler_tdls_channel_switch(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter, iter_dict;
+ struct wpa_dbus_dict_entry entry;
+ u8 peer[ETH_ALEN];
+ struct hostapd_freq_params freq_params;
+ u8 oper_class = 0;
+ int ret;
+ int is_peer_present = 0;
+
+ if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+ wpa_printf(MSG_INFO,
+ "tdls_chanswitch: Only supported with external setup");
+ return wpas_dbus_error_unknown_error(message, "TDLS is not using external setup");
+ }
+
+ os_memset(&freq_params, 0, sizeof(freq_params));
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (os_strcmp(entry.key, "PeerAddress") == 0 &&
+ entry.type == DBUS_TYPE_STRING) {
+ if (hwaddr_aton(entry.str_value, peer)) {
+ wpa_printf(MSG_DEBUG,
+ "tdls_chanswitch: Invalid address '%s'",
+ entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(message,
+ NULL);
+ }
+
+ is_peer_present = 1;
+ } else if (os_strcmp(entry.key, "OperClass") == 0 &&
+ entry.type == DBUS_TYPE_BYTE) {
+ oper_class = entry.byte_value;
+ } else if (os_strcmp(entry.key, "Frequency") == 0 &&
+ entry.type == DBUS_TYPE_UINT32) {
+ freq_params.freq = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "SecChannelOffset") == 0 &&
+ entry.type == DBUS_TYPE_UINT32) {
+ freq_params.sec_channel_offset = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "CenterFrequency1") == 0 &&
+ entry.type == DBUS_TYPE_UINT32) {
+ freq_params.center_freq1 = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "CenterFrequency2") == 0 &&
+ entry.type == DBUS_TYPE_UINT32) {
+ freq_params.center_freq2 = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "Bandwidth") == 0 &&
+ entry.type == DBUS_TYPE_UINT32) {
+ freq_params.bandwidth = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "HT") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ freq_params.ht_enabled = entry.bool_value;
+ } else if (os_strcmp(entry.key, "VHT") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ freq_params.vht_enabled = entry.bool_value;
+ } else {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(message, NULL);
+ }
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (oper_class == 0) {
+ wpa_printf(MSG_INFO,
+ "tdls_chanswitch: Invalid op class provided");
+ return wpas_dbus_error_invalid_args(
+ message, "Invalid op class provided");
+ }
+
+ if (freq_params.freq == 0) {
+ wpa_printf(MSG_INFO,
+ "tdls_chanswitch: Invalid freq provided");
+ return wpas_dbus_error_invalid_args(message,
+ "Invalid freq provided");
+ }
+
+ if (is_peer_present == 0) {
+ wpa_printf(MSG_DEBUG,
+ "tdls_chanswitch: peer address not provided");
+ return wpas_dbus_error_invalid_args(
+ message, "peer address not provided");
+ }
+
+ wpa_printf(MSG_DEBUG, "dbus: TDLS_CHAN_SWITCH " MACSTR
+ " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
+ MAC2STR(peer), oper_class, freq_params.freq,
+ freq_params.center_freq1, freq_params.center_freq2,
+ freq_params.bandwidth, freq_params.sec_channel_offset,
+ freq_params.ht_enabled ? " HT" : "",
+ freq_params.vht_enabled ? " VHT" : "");
+
+ ret = wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
+ &freq_params);
+ if (ret)
+ return wpas_dbus_error_unknown_error(
+ message, "error processing TDLS channel switch");
+
+ return NULL;
+}
+
+/*
+ * wpas_dbus_handler_tdls_cancel_channel_switch - Disable channel switching with TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSCancelChannelSwitch" method call of network
+ * interface.
+ */
+DBusMessage *
+wpas_dbus_handler_tdls_cancel_channel_switch(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ u8 peer[ETH_ALEN];
+ DBusMessage *error_reply;
+ int ret;
+
+ if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+ return error_reply;
+
+ wpa_printf(MSG_DEBUG, "dbus: TDLS_CANCEL_CHAN_SWITCH " MACSTR,
+ MAC2STR(peer));
+
+ ret = wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
+ if (ret)
+ return wpas_dbus_error_unknown_error(
+ message, "error canceling TDLS channel switch");
+
+ return NULL;
+}
+
#endif /* CONFIG_TDLS */
@@ -2468,6 +2693,28 @@ dbus_bool_t wpas_dbus_getter_capabilities(
goto nomem;
}
+ if (!wpa_dbus_dict_begin_string_array(&iter_dict, "GroupMgmt",
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_array) ||
+ (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP) &&
+ !wpa_dbus_dict_string_array_add_element(
+ &iter_array, "aes-128-cmac")) ||
+ (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_GMAC_128) &&
+ !wpa_dbus_dict_string_array_add_element(
+ &iter_array, "bip-gmac-128")) ||
+ (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_GMAC_256) &&
+ !wpa_dbus_dict_string_array_add_element(
+ &iter_array, "bip-gmac-256")) ||
+ (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_CMAC_256) &&
+ !wpa_dbus_dict_string_array_add_element(
+ &iter_array, "bip-cmac-256")) ||
+ !wpa_dbus_dict_end_string_array(&iter_dict,
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_array))
+ goto nomem;
+
/***** key management */
if (res < 0) {
const char *args[] = {
@@ -2627,6 +2874,11 @@ dbus_bool_t wpas_dbus_getter_capabilities(
!wpa_s->conf->p2p_disabled &&
!wpa_dbus_dict_string_array_add_element(
&iter_array, "p2p")) ||
+#ifdef CONFIG_MESH
+ (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_MESH) &&
+ !wpa_dbus_dict_string_array_add_element(
+ &iter_array, "mesh")) ||
+#endif /* CONFIG_MESH */
!wpa_dbus_dict_end_string_array(&iter_dict,
&iter_dict_entry,
&iter_dict_val,
@@ -3086,10 +3338,8 @@ dbus_bool_t wpas_dbus_getter_ifname(
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
- const char *ifname = wpa_s->ifname;
- return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
- &ifname, error);
+ return wpas_dbus_string_property_getter(iter, wpa_s->ifname, error);
}
@@ -3107,7 +3357,6 @@ dbus_bool_t wpas_dbus_getter_driver(
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
- const char *driver;
if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
wpa_printf(MSG_DEBUG, "%s[dbus]: wpa_s has no driver set",
@@ -3117,9 +3366,8 @@ dbus_bool_t wpas_dbus_getter_driver(
return FALSE;
}
- driver = wpa_s->driver->name;
- return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
- &driver, error);
+ return wpas_dbus_string_property_getter(iter, wpa_s->driver->name,
+ error);
}
@@ -3232,10 +3480,9 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname(
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
- const char *bridge_ifname = wpa_s->bridge_ifname;
- return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
- &bridge_ifname, error);
+ return wpas_dbus_string_property_getter(iter, wpa_s->bridge_ifname,
+ error);
}
@@ -3253,13 +3500,8 @@ dbus_bool_t wpas_dbus_getter_config_file(
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
- char *confname = "";
- if (wpa_s->confname)
- confname = wpa_s->confname;
-
- return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
- &confname, error);
+ return wpas_dbus_string_property_getter(iter, wpa_s->confname, error);
}
@@ -3399,14 +3641,10 @@ dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
- const char *pkcs11_engine_path;
- if (wpa_s->conf->pkcs11_engine_path == NULL)
- pkcs11_engine_path = "";
- else
- pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
- return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
- &pkcs11_engine_path, error);
+ return wpas_dbus_string_property_getter(iter,
+ wpa_s->conf->pkcs11_engine_path,
+ error);
}
@@ -3424,14 +3662,10 @@ dbus_bool_t wpas_dbus_getter_pkcs11_module_path(
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
- const char *pkcs11_module_path;
- if (wpa_s->conf->pkcs11_module_path == NULL)
- pkcs11_module_path = "";
- else
- pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
- return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
- &pkcs11_module_path, error);
+ return wpas_dbus_string_property_getter(iter,
+ wpa_s->conf->pkcs11_module_path,
+ error);
}
@@ -3683,6 +3917,7 @@ dbus_bool_t wpas_dbus_getter_bss_mode(
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
const char *mode;
+ const u8 *mesh;
res = get_bss_helper(args, error, __func__);
if (!res)
@@ -3696,9 +3931,15 @@ dbus_bool_t wpas_dbus_getter_bss_mode(
case IEEE80211_CAP_DMG_AP:
mode = "infrastructure";
break;
+ default:
+ mode = "";
+ break;
}
} else {
- if (res->caps & IEEE80211_CAP_IBSS)
+ mesh = wpa_bss_get_ie(res, WLAN_EID_MESH_ID);
+ if (mesh)
+ mode = "mesh";
+ else if (res->caps & IEEE80211_CAP_IBSS)
mode = "ad-hoc";
else
mode = "infrastructure";
@@ -3826,7 +4067,7 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(
DBusMessageIter iter_dict, variant_iter;
const char *group;
const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
- const char *key_mgmt[9]; /* max 9 key managements may be supported */
+ const char *key_mgmt[13]; /* max 13 key managements may be supported */
int n;
if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
@@ -3858,6 +4099,16 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(
if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
key_mgmt[n++] = "wpa-eap-suite-b-192";
#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+ if (ie_data->key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+ key_mgmt[n++] = "wpa-fils-sha256";
+ if (ie_data->key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+ key_mgmt[n++] = "wpa-fils-sha384";
+ if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ key_mgmt[n++] = "wpa-ft-fils-sha256";
+ if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ key_mgmt[n++] = "wpa-ft-fils-sha384";
+#endif /* CONFIG_FILS */
if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE)
key_mgmt[n++] = "wpa-none";
@@ -4534,3 +4785,100 @@ DBusMessage * wpas_dbus_handler_vendor_elem_remove(DBusMessage *message,
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Not found");
}
+
+
+#ifdef CONFIG_MESH
+
+/**
+ * wpas_dbus_getter_mesh_peers - Get connected mesh peers
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "MeshPeers" property.
+ */
+dbus_bool_t wpas_dbus_getter_mesh_peers(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct hostapd_data *hapd;
+ struct sta_info *sta;
+ DBusMessageIter variant_iter, array_iter;
+ int i;
+ DBusMessageIter inner_array_iter;
+
+ if (!wpa_s->ifmsh)
+ return FALSE;
+ hapd = wpa_s->ifmsh->bss[0];
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &variant_iter) ||
+ !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &array_iter))
+ return FALSE;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!dbus_message_iter_open_container(
+ &array_iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &inner_array_iter))
+ return FALSE;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ if (!dbus_message_iter_append_basic(&inner_array_iter,
+ DBUS_TYPE_BYTE,
+ &(sta->addr[i])))
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(
+ &array_iter, &inner_array_iter))
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+ !dbus_message_iter_close_container(iter, &variant_iter))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_mesh_group - Get mesh group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "MeshGroup" property.
+ */
+dbus_bool_t wpas_dbus_getter_mesh_group(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (!wpa_s->ifmsh || !ssid)
+ return FALSE;
+
+ if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ (char *) ssid->ssid,
+ ssid->ssid_len, error)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: error constructing reply", __func__);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#endif /* CONFIG_MESH */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 1d6235d6f3e4..6f952cc39091 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -43,6 +43,10 @@ dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
size_t array_len,
DBusError *error);
+dbus_bool_t wpas_dbus_string_property_getter(DBusMessageIter *iter,
+ const void *val,
+ DBusError *error);
+
DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
struct wpa_global *global);
@@ -70,6 +74,9 @@ DECLARE_ACCESSOR(wpas_dbus_setter_iface_global);
DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -186,6 +193,21 @@ DECLARE_ACCESSOR(wpas_dbus_getter_process_credentials);
DECLARE_ACCESSOR(wpas_dbus_setter_process_credentials);
DECLARE_ACCESSOR(wpas_dbus_getter_config_methods);
DECLARE_ACCESSOR(wpas_dbus_setter_config_methods);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_name);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_name);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_manufacturer);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_manufacturer);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_model_name);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_model_name);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_model_number);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_model_number);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_serial_number);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_serial_number);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_device_type);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_device_type);
+
+DECLARE_ACCESSOR(wpas_dbus_getter_mesh_peers);
+DECLARE_ACCESSOR(wpas_dbus_getter_mesh_group);
DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -195,6 +217,12 @@ DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message,
struct wpa_supplicant *wpa_s);
DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_tdls_channel_switch(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_tdls_cancel_channel_switch(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message,
struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 73b9e20c20b0..9305b9a4f37d 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -28,6 +28,18 @@
#include "../p2p_supplicant.h"
#include "../wifi_display.h"
+
+static int wpas_dbus_validate_dbus_ipaddr(struct wpa_dbus_dict_entry entry)
+{
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != DBUS_TYPE_BYTE ||
+ entry.array_len != 4)
+ return 0;
+
+ return 1;
+}
+
+
/**
* Parses out the mac address from the peer object path.
* @peer_path - object path of the form
@@ -78,6 +90,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
int num_req_dev_types = 0;
unsigned int i;
u8 *req_dev_types = NULL;
+ unsigned int freq = 0;
dbus_message_iter_init(message, &iter);
entry.key = NULL;
@@ -122,6 +135,10 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
type = P2P_FIND_PROGRESSIVE;
else
goto error_clear;
+ } else if (os_strcmp(entry.key, "freq") == 0 &&
+ (entry.type == DBUS_TYPE_INT32 ||
+ entry.type == DBUS_TYPE_UINT32)) {
+ freq = entry.uint32_value;
} else
goto error_clear;
wpa_dbus_dict_entry_clear(&entry);
@@ -129,8 +146,11 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
wpa_s = wpa_s->global->p2p_init_wpa_s;
- wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
- NULL, 0, 0, NULL, 0);
+ if (wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types,
+ req_dev_types, NULL, 0, 0, NULL, freq))
+ reply = wpas_dbus_error_unknown_error(
+ message, "Could not start P2P find");
+
os_free(req_dev_types);
return reply;
@@ -867,6 +887,35 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(
goto err_no_mem;
}
+ /* GO IP address */
+ if (WPA_GET_BE32(wpa_s->conf->ip_addr_go) &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrGo",
+ (char *) wpa_s->conf->ip_addr_go,
+ 4))
+ goto err_no_mem;
+
+ /* IP address mask */
+ if (WPA_GET_BE32(wpa_s->conf->ip_addr_mask) &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrMask",
+ (char *) wpa_s->conf->ip_addr_mask,
+ 4))
+ goto err_no_mem;
+
+ /* IP address start */
+ if (WPA_GET_BE32(wpa_s->conf->ip_addr_start) &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrStart",
+ (char *)
+ wpa_s->conf->ip_addr_start,
+ 4))
+ goto err_no_mem;
+
+ /* IP address end */
+ if (WPA_GET_BE32(wpa_s->conf->ip_addr_end) &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrEnd",
+ (char *) wpa_s->conf->ip_addr_end,
+ 4))
+ goto err_no_mem;
+
/* Vendor Extensions */
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
if (wpa_s->conf->wps_vendor_ext[i] == NULL)
@@ -1051,6 +1100,26 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(
wpa_s->conf->p2p_intra_bss = entry.bool_value;
wpa_s->conf->changed_parameters |=
CFG_CHANGED_P2P_INTRA_BSS;
+ } else if (os_strcmp(entry.key, "IpAddrGo") == 0) {
+ if (!wpas_dbus_validate_dbus_ipaddr(entry))
+ goto error;
+ os_memcpy(wpa_s->conf->ip_addr_go,
+ entry.bytearray_value, 4);
+ } else if (os_strcmp(entry.key, "IpAddrMask") == 0) {
+ if (!wpas_dbus_validate_dbus_ipaddr(entry))
+ goto error;
+ os_memcpy(wpa_s->conf->ip_addr_mask,
+ entry.bytearray_value, 4);
+ } else if (os_strcmp(entry.key, "IpAddrStart") == 0) {
+ if (!wpas_dbus_validate_dbus_ipaddr(entry))
+ goto error;
+ os_memcpy(wpa_s->conf->ip_addr_start,
+ entry.bytearray_value, 4);
+ } else if (os_strcmp(entry.key, "IpAddrEnd") == 0) {
+ if (!wpas_dbus_validate_dbus_ipaddr(entry))
+ goto error;
+ os_memcpy(wpa_s->conf->ip_addr_end,
+ entry.bytearray_value, 4);
} else if (os_strcmp(entry.key, "GroupIdle") == 0 &&
entry.type == DBUS_TYPE_UINT32)
wpa_s->conf->p2p_group_idle = entry.uint32_value;
@@ -2286,19 +2355,12 @@ dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
- char *p_pass;
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid == NULL)
return FALSE;
- p_pass = ssid->passphrase;
- if (!p_pass)
- p_pass = "";
-
- return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
- &p_pass, error);
-
+ return wpas_dbus_string_property_getter(iter, ssid->passphrase, error);
}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index f16e2290c7ed..f762b3f2ef5c 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -412,12 +412,10 @@ dbus_bool_t wpas_dbus_getter_config_methods(
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
- char *methods = wpa_s->conf->config_methods;
- if (methods == NULL)
- methods = "";
- return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
- &methods, error);
+ return wpas_dbus_string_property_getter(iter,
+ wpa_s->conf->config_methods,
+ error);
}
@@ -454,3 +452,349 @@ dbus_bool_t wpas_dbus_setter_config_methods(
return TRUE;
}
+
+
+/**
+ * wpas_dbus_getter_wps_device_name - Get current WPS device name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DeviceName" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_name(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+
+ return wpas_dbus_string_property_getter(iter, wpa_s->conf->device_name,
+ error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_name - Set current WPS device name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "DeviceName" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_name(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char *methods, *devname;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &methods))
+ return FALSE;
+
+ if (os_strlen(methods) > WPS_DEV_NAME_MAX_LEN)
+ return FALSE;
+
+ devname = os_strdup(methods);
+ if (!devname)
+ return FALSE;
+
+ os_free(wpa_s->conf->device_name);
+ wpa_s->conf->device_name = devname;
+ wpa_s->conf->changed_parameters |= CFG_CHANGED_DEVICE_NAME;
+ wpa_supplicant_update_config(wpa_s);
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_manufacturer - Get current manufacturer name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Manufacturer" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_manufacturer(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+
+ return wpas_dbus_string_property_getter(iter, wpa_s->conf->manufacturer,
+ error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_manufacturer - Set current manufacturer name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "Manufacturer" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_manufacturer(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char *methods, *manufacturer;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &methods))
+ return FALSE;
+
+ if (os_strlen(methods) > WPS_MANUFACTURER_MAX_LEN)
+ return FALSE;
+
+ manufacturer = os_strdup(methods);
+ if (!manufacturer)
+ return FALSE;
+
+ os_free(wpa_s->conf->manufacturer);
+ wpa_s->conf->manufacturer = manufacturer;
+ wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING;
+ wpa_supplicant_update_config(wpa_s);
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_device_model_name - Get current device model name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ModelName" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_model_name(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+
+ return wpas_dbus_string_property_getter(iter, wpa_s->conf->model_name,
+ error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_model_name - Set current device model name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "ModelName" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_model_name(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char *methods, *model_name;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &methods))
+ return FALSE;
+
+ if (os_strlen(methods) > WPS_MODEL_NAME_MAX_LEN)
+ return FALSE;
+
+ model_name = os_strdup(methods);
+ if (!model_name)
+ return FALSE;
+ os_free(wpa_s->conf->model_name);
+ wpa_s->conf->model_name = model_name;
+ wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING;
+ wpa_supplicant_update_config(wpa_s);
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_device_model_number - Get current device model number
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ModelNumber" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_model_number(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+
+ return wpas_dbus_string_property_getter(iter, wpa_s->conf->model_number,
+ error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_model_number - Set current device model number
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "ModelNumber" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_model_number(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char *methods, *model_number;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &methods))
+ return FALSE;
+
+ if (os_strlen(methods) > WPS_MODEL_NUMBER_MAX_LEN)
+ return FALSE;
+
+ model_number = os_strdup(methods);
+ if (!model_number)
+ return FALSE;
+
+ os_free(wpa_s->conf->model_number);
+ wpa_s->conf->model_number = model_number;
+ wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING;
+ wpa_supplicant_update_config(wpa_s);
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_device_serial_number - Get current device serial number
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "SerialNumber" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_serial_number(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+
+ return wpas_dbus_string_property_getter(iter,
+ wpa_s->conf->serial_number,
+ error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_serial_number - Set current device serial number
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "SerialNumber" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_serial_number(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char *methods, *serial_number;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &methods))
+ return FALSE;
+
+ if (os_strlen(methods) > WPS_SERIAL_NUMBER_MAX_LEN)
+ return FALSE;
+
+ serial_number = os_strdup(methods);
+ if (!serial_number)
+ return FALSE;
+ os_free(wpa_s->conf->serial_number);
+ wpa_s->conf->serial_number = serial_number;
+ wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING;
+ wpa_supplicant_update_config(wpa_s);
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_device_device_type - Get current device type
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DeviceType" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_device_type(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+
+ if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ (char *)
+ wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN, error)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: error constructing reply", __func__);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_device_type - Set current device type
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "DeviceType" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_device_type(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u8 *dev_type;
+ int dev_len;
+ DBusMessageIter variant, array_iter;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+ return FALSE;
+
+ dbus_message_iter_recurse(iter, &variant);
+ if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY)
+ return FALSE;
+
+ dbus_message_iter_recurse(&variant, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &dev_type, &dev_len);
+
+ if (dev_len != WPS_DEV_TYPE_LEN)
+ return FALSE;
+
+ os_memcpy(wpa_s->conf->device_type, dev_type, WPS_DEV_TYPE_LEN);
+ wpa_s->conf->changed_parameters |= CFG_CHANGED_DEVICE_TYPE;
+ wpa_supplicant_update_config(wpa_s);
+
+ return TRUE;
+}
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 1d05198f849a..af281e56d2e1 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -1,9 +1,9 @@
# Example wpa_supplicant build time configuration
#
# This file lists the configuration options that are used when building the
-# hostapd binary. All lines starting with # are ignored. Configuration option
-# lines must be commented out complete, if they are not to be included, i.e.,
-# just setting VARIABLE=n is not disabling that variable.
+# wpa_supplicant binary. All lines starting with # are ignored. Configuration
+# option lines must be commented out complete, if they are not to be included,
+# i.e., just setting VARIABLE=n is not disabling that variable.
#
# This file is included in Makefile, so variables like CFLAGS and LIBS can also
# be modified from here. In most cases, these lines should use += in order not
@@ -44,7 +44,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)
@@ -73,6 +73,12 @@ CONFIG_DRIVER_NL80211=y
# Driver interface for wired Ethernet drivers
CONFIG_DRIVER_WIRED=y
+# Driver interface for MACsec capable Qualcomm Atheros drivers
+#CONFIG_DRIVER_MACSEC_QCA=y
+
+# Driver interface for Linux MACsec drivers
+#CONFIG_DRIVER_MACSEC_LINUX=y
+
# Driver interface for the Broadcom RoboSwitch family
#CONFIG_DRIVER_ROBOSWITCH=y
@@ -83,8 +89,8 @@ CONFIG_DRIVER_WIRED=y
#LIBS += -lsocket -ldlpi -lnsl
#LIBS_c += -lsocket
-# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
-# included)
+# Enable IEEE 802.1X Supplicant (automatically included if any EAP method or
+# MACsec is included)
CONFIG_IEEE8021X_EAPOL=y
# EAP-MD5
@@ -166,6 +172,9 @@ CONFIG_EAP_LEAP=y
# EAP-EKE
#CONFIG_EAP_EKE=y
+# MACsec
+#CONFIG_MACSEC=y
+
# PKCS#12 (PFX) support (used to read private key and certificate file from
# a file that usually has extension .p12 or .pfx)
CONFIG_PKCS12=y
@@ -288,9 +297,6 @@ CONFIG_BACKEND=file
# bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
-
# IEEE 802.11w (management frame protection), also known as PMF
# Driver support is also needed for IEEE 802.11w.
#CONFIG_IEEE80211W=y
@@ -299,6 +305,7 @@ CONFIG_PEERKEY=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
@@ -316,6 +323,10 @@ CONFIG_PEERKEY=y
# will be used)
#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
@@ -370,7 +381,7 @@ CONFIG_PEERKEY=y
# amount of memory/flash.
#CONFIG_DYNAMIC_EAP_METHODS=y
-# IEEE Std 802.11r-2008 (Fast BSS Transition)
+# IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode
#CONFIG_IEEE80211R=y
# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
@@ -548,3 +559,37 @@ CONFIG_PEERKEY=y
# Support Multi Band Operation
#CONFIG_MBO=y
+
+# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
+# Note: This is an experimental and not yet complete implementation. This
+# should not be enabled for production use.
+#CONFIG_FILS=y
+# FILS shared key authentication with PFS
+#CONFIG_FILS_SK_PFS=y
+
+# Support RSN on IBSS networks
+# This is needed to be able to use mode=1 network profile with proto=RSN and
+# key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None).
+#CONFIG_IBSS_RSN=y
+
+# External PMKSA cache control
+# This can be used to enable control interface commands that allow the current
+# PMKSA cache entries to be fetched and new entries to be added.
+#CONFIG_PMKSA_CACHE_EXTERNAL=y
+
+# Mesh Networking (IEEE 802.11s)
+#CONFIG_MESH=y
+
+# Background scanning modules
+# These can be used to request wpa_supplicant to perform background scanning
+# operations for roaming within an ESS (same SSID). See the bgscan parameter in
+# the wpa_supplicant.conf file for more details.
+# Periodic background scans based on signal strength
+#CONFIG_BGSCAN_SIMPLE=y
+# Learn channels used by the network and try to avoid bgscans on other
+# channels (experimental)
+#CONFIG_BGSCAN_LEARN=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
diff --git a/wpa_supplicant/doc/docbook/eapol_test.8 b/wpa_supplicant/doc/docbook/eapol_test.8
index af232826cb47..1ae1ac1c93ea 100644
--- a/wpa_supplicant/doc/docbook/eapol_test.8
+++ b/wpa_supplicant/doc/docbook/eapol_test.8
@@ -3,7 +3,7 @@
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "EAPOL_TEST" "8" "02 October 2016" "" ""
+.TH "EAPOL_TEST" "8" "02 December 2018" "" ""
.SH NAME
eapol_test \- EAP peer and RADIUS client testing
@@ -115,7 +115,7 @@ Save configuration after authentication.
\fBwpa_supplicant\fR(8)
.SH "LEGAL"
.PP
-wpa_supplicant is copyright (c) 2003-2016,
+wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <j@w1.fi> and
contributors.
All Rights Reserved.
diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml
index 3f224133a301..ae6bafecfa15 100644
--- a/wpa_supplicant/doc/docbook/eapol_test.sgml
+++ b/wpa_supplicant/doc/docbook/eapol_test.sgml
@@ -194,7 +194,7 @@ eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2016,
+ <para>wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_background.8 b/wpa_supplicant/doc/docbook/wpa_background.8
index 9283266d405e..e2b894b6ae24 100644
--- a/wpa_supplicant/doc/docbook/wpa_background.8
+++ b/wpa_supplicant/doc/docbook/wpa_background.8
@@ -3,7 +3,7 @@
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_BACKGROUND" "8" "02 October 2016" "" ""
+.TH "WPA_BACKGROUND" "8" "02 December 2018" "" ""
.SH NAME
wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i
@@ -75,7 +75,7 @@ pre-authentication, and PMKSA caching).
\fBwpa_supplicant\fR(8)
.SH "LEGAL"
.PP
-wpa_supplicant is copyright (c) 2003-2016,
+wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <j@w1.fi> and
contributors.
All Rights Reserved.
diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml
index 13c9f4514ff8..d3e4dbe2a5a8 100644
--- a/wpa_supplicant/doc/docbook/wpa_background.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_background.sgml
@@ -90,7 +90,7 @@
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2016,
+ <para>wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_cli.8 b/wpa_supplicant/doc/docbook/wpa_cli.8
index 440bfc5bdfcc..ec96a4dfa93e 100644
--- a/wpa_supplicant/doc/docbook/wpa_cli.8
+++ b/wpa_supplicant/doc/docbook/wpa_cli.8
@@ -3,7 +3,7 @@
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_CLI" "8" "02 October 2016" "" ""
+.TH "WPA_CLI" "8" "02 December 2018" "" ""
.SH NAME
wpa_cli \- WPA command line client
@@ -210,7 +210,7 @@ exit wpa_cli
\fBwpa_supplicant\fR(8)
.SH "LEGAL"
.PP
-wpa_supplicant is copyright (c) 2003-2016,
+wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <j@w1.fi> and
contributors.
All Rights Reserved.
diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml
index 15400f04730a..766dd2cc1370 100644
--- a/wpa_supplicant/doc/docbook/wpa_cli.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml
@@ -345,7 +345,7 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2016,
+ <para>wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.8 b/wpa_supplicant/doc/docbook/wpa_gui.8
index 73bf362a8d46..2d7e74e48e61 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.8
+++ b/wpa_supplicant/doc/docbook/wpa_gui.8
@@ -3,7 +3,7 @@
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_GUI" "8" "02 October 2016" "" ""
+.TH "WPA_GUI" "8" "02 December 2018" "" ""
.SH NAME
wpa_gui \- WPA Graphical User Interface
@@ -51,7 +51,7 @@ icon pop-up messages.
\fBwpa_supplicant\fR(8)
.SH "LEGAL"
.PP
-wpa_supplicant is copyright (c) 2003-2016,
+wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <j@w1.fi> and
contributors.
All Rights Reserved.
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
index 352d3d28cf5b..91662d54a1fc 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -91,7 +91,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2016,
+ <para>wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/wpa_supplicant/doc/docbook/wpa_passphrase.8
index ed0347af2c47..20e7155cf725 100644
--- a/wpa_supplicant/doc/docbook/wpa_passphrase.8
+++ b/wpa_supplicant/doc/docbook/wpa_passphrase.8
@@ -3,7 +3,7 @@
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_PASSPHRASE" "8" "02 October 2016" "" ""
+.TH "WPA_PASSPHRASE" "8" "02 December 2018" "" ""
.SH NAME
wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID
@@ -31,7 +31,7 @@ passphrase will be read from standard input.
\fBwpa_supplicant\fR(8)
.SH "LEGAL"
.PP
-wpa_supplicant is copyright (c) 2003-2016,
+wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <j@w1.fi> and
contributors.
All Rights Reserved.
diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
index faf1f2799ea6..2f86b0bdf987 100644
--- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
@@ -18,7 +18,7 @@
</refsynopsisdiv>
<refsect1>
- <title>Overview</title>
+ <title>Overview</title>
<para><command>wpa_passphrase</command> pre-computes PSK entries for
network configuration blocks of a
@@ -62,7 +62,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2016,
+ <para>wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_priv.8 b/wpa_supplicant/doc/docbook/wpa_priv.8
index 9a9fe82bbaef..4064d1ba9164 100644
--- a/wpa_supplicant/doc/docbook/wpa_priv.8
+++ b/wpa_supplicant/doc/docbook/wpa_priv.8
@@ -3,7 +3,7 @@
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_PRIV" "8" "02 October 2016" "" ""
+.TH "WPA_PRIV" "8" "02 December 2018" "" ""
.SH NAME
wpa_priv \- wpa_supplicant privilege separation helper
@@ -111,7 +111,7 @@ processes at the same time, if desired.
\fBwpa_supplicant\fR(8)
.SH "LEGAL"
.PP
-wpa_supplicant is copyright (c) 2003-2016,
+wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <j@w1.fi> and
contributors.
All Rights Reserved.
diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml
index 403c9b2d2f84..4a5f319db3b2 100644
--- a/wpa_supplicant/doc/docbook/wpa_priv.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml
@@ -137,7 +137,7 @@ wpa_supplicant -i ath0 -c wpa_supplicant.conf
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2016,
+ <para>wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/wpa_supplicant/doc/docbook/wpa_supplicant.8
index 95ff5ff8c2a1..16a94058be90 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.8
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.8
@@ -3,7 +3,7 @@
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_SUPPLICANT" "8" "02 October 2016" "" ""
+.TH "WPA_SUPPLICANT" "8" "02 December 2018" "" ""
.SH NAME
wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant
@@ -532,7 +532,7 @@ in.
\fBwpa_passphrase\fR(8)
.SH "LEGAL"
.PP
-wpa_supplicant is copyright (c) 2003-2016,
+wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <j@w1.fi> and
contributors.
All Rights Reserved.
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5
index f4c997a1077b..07461130085e 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5
@@ -3,7 +3,7 @@
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_SUPPLICANT.CONF" "5" "02 October 2016" "" ""
+.TH "WPA_SUPPLICANT.CONF" "5" "02 December 2018" "" ""
.SH NAME
wpa_supplicant.conf \- configuration file for wpa_supplicant
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index 11e0e90d7652..eeb9c07305b9 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -565,7 +565,7 @@ wpa_supplicant \
using ap_scan=0 option in configuration file.</para>
</listitem>
</varlistentry>
-
+
<varlistentry>
<term>Wired Ethernet drivers</term>
<listitem>
@@ -590,7 +590,7 @@ wpa_supplicant \
</varlistentry>
</variablelist>
-
+
<para>wpa_supplicant was designed to be portable for different
drivers and operating systems. Hopefully, support for more wlan
cards and OSes will be added in the future. See developer.txt for
@@ -729,7 +729,7 @@ fi
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2016,
+ <para>wpa_supplicant is copyright (c) 2003-2018,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
new file mode 100644
index 000000000000..7bc46610a971
--- /dev/null
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -0,0 +1,2613 @@
+/*
+ * wpa_supplicant - DPP
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation
+ *
+ * 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/gas_server.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "driver_i.h"
+#include "offchannel.h"
+#include "gas_query.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "dpp_supplicant.h"
+
+
+static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator);
+static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result result);
+static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpas_dpp_auth_init_next(struct wpa_supplicant *wpa_s);
+static void
+wpas_dpp_tx_pkex_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result result);
+
+static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+/* Use a hardcoded Transaction ID 1 in Peer Discovery frames since there is only
+ * a single transaction in progress at any point in time. */
+static const u8 TRANSACTION_ID = 1;
+
+
+static struct dpp_configurator *
+dpp_configurator_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ struct dpp_configurator *conf;
+
+ dl_list_for_each(conf, &wpa_s->dpp_configurator,
+ struct dpp_configurator, list) {
+ if (conf->id == id)
+ return conf;
+ }
+ return NULL;
+}
+
+
+static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_bootstrap_info *bi;
+ unsigned int max_id = 0;
+
+ dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+ list) {
+ if (bi->id > max_id)
+ max_id = bi->id;
+ }
+ return max_id + 1;
+}
+
+
+/**
+ * wpas_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ struct dpp_bootstrap_info *bi;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ bi = dpp_parse_qr_code(cmd);
+ if (!bi)
+ return -1;
+
+ bi->id = wpas_dpp_next_id(wpa_s);
+ dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+
+ if (auth && auth->response_pending &&
+ dpp_notify_new_qr_code(auth, bi) == 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Sending out pending authentication response");
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ offchannel_send_action(wpa_s, auth->curr_freq,
+ auth->peer_mac_addr, wpa_s->own_addr,
+ broadcast,
+ wpabuf_head(auth->resp_msg),
+ wpabuf_len(auth->resp_msg),
+ 500, wpas_dpp_tx_status, 0);
+ }
+
+ 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 wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
+ char *key = NULL;
+ u8 *privkey = NULL;
+ size_t privkey_len = 0;
+ size_t len;
+ int ret = -1;
+ struct dpp_bootstrap_info *bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ goto fail;
+
+ if (os_strstr(cmd, "type=qrcode"))
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ else if (os_strstr(cmd, "type=pkex"))
+ bi->type = DPP_BOOTSTRAP_PKEX;
+ else
+ goto fail;
+
+ chan = get_param(cmd, " chan=");
+ mac = get_param(cmd, " mac=");
+ info = get_param(cmd, " info=");
+ curve = get_param(cmd, " curve=");
+ key = get_param(cmd, " key=");
+
+ if (key) {
+ privkey_len = os_strlen(key) / 2;
+ privkey = os_malloc(privkey_len);
+ if (!privkey ||
+ hexstr2bin(key, privkey, privkey_len) < 0)
+ goto fail;
+ }
+
+ pk = dpp_keygen(bi, curve, privkey, privkey_len);
+ if (!pk)
+ goto fail;
+
+ len = 4; /* "DPP:" */
+ if (chan) {
+ if (dpp_parse_uri_chan_list(bi, chan) < 0)
+ goto fail;
+ len += 3 + os_strlen(chan); /* C:...; */
+ }
+ if (mac) {
+ if (dpp_parse_uri_mac(bi, mac) < 0)
+ goto fail;
+ len += 3 + os_strlen(mac); /* M:...; */
+ }
+ if (info) {
+ if (dpp_parse_uri_info(bi, info) < 0)
+ goto fail;
+ len += 3 + os_strlen(info); /* I:...; */
+ }
+ len += 4 + os_strlen(pk);
+ bi->uri = os_malloc(len + 1);
+ if (!bi->uri)
+ goto fail;
+ os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
+ chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
+ mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
+ info ? "I:" : "", info ? info : "", info ? ";" : "",
+ pk);
+ bi->id = wpas_dpp_next_id(wpa_s);
+ dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+ ret = bi->id;
+ bi = NULL;
+fail:
+ os_free(curve);
+ os_free(pk);
+ os_free(chan);
+ os_free(mac);
+ os_free(info);
+ str_clear_free(key);
+ bin_clear_free(privkey, privkey_len);
+ dpp_bootstrap_info_free(bi);
+ return ret;
+}
+
+
+static struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ struct dpp_bootstrap_info *bi;
+
+ dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+ list) {
+ if (bi->id == id)
+ return bi;
+ }
+ return NULL;
+}
+
+
+static int dpp_bootstrap_del(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ struct dpp_bootstrap_info *bi, *tmp;
+ int found = 0;
+
+ dl_list_for_each_safe(bi, tmp, &wpa_s->dpp_bootstrap,
+ struct dpp_bootstrap_info, list) {
+ if (id && bi->id != id)
+ continue;
+ found = 1;
+ dl_list_del(&bi->list);
+ dpp_bootstrap_info_free(bi);
+ }
+
+ if (id == 0)
+ return 0; /* flush succeeds regardless of entries found */
+ return found ? 0 : -1;
+}
+
+
+int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id)
+{
+ unsigned int id_val;
+
+ if (os_strcmp(id, "*") == 0) {
+ id_val = 0;
+ } else {
+ id_val = atoi(id);
+ if (id_val == 0)
+ return -1;
+ }
+
+ return dpp_bootstrap_del(wpa_s, id_val);
+}
+
+
+const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_bootstrap_get_id(wpa_s, id);
+ if (!bi)
+ return NULL;
+ return bi->uri;
+}
+
+
+int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id,
+ char *reply, int reply_size)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_bootstrap_get_id(wpa_s, id);
+ if (!bi)
+ return -1;
+ return os_snprintf(reply, reply_size, "type=%s\n"
+ "mac_addr=" MACSTR "\n"
+ "info=%s\n"
+ "num_freq=%u\n"
+ "curve=%s\n",
+ dpp_bootstrap_type_txt(bi->type),
+ MAC2STR(bi->mac_addr),
+ bi->info ? bi->info : "",
+ bi->num_freq,
+ bi->curve->name);
+}
+
+
+static void wpas_dpp_auth_resp_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!auth || !auth->resp_msg)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Retry Authentication Response after timeout");
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ offchannel_send_action(wpa_s, auth->curr_freq, auth->peer_mac_addr,
+ wpa_s->own_addr, broadcast,
+ wpabuf_head(auth->resp_msg),
+ wpabuf_len(auth->resp_msg),
+ 500, wpas_dpp_tx_status, 0);
+}
+
+
+static void wpas_dpp_auth_resp_retry(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+ unsigned int wait_time, max_tries;
+
+ if (!auth || !auth->resp_msg)
+ return;
+
+ if (wpa_s->dpp_resp_max_tries)
+ max_tries = wpa_s->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");
+ offchannel_send_action_done(wpa_s);
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ return;
+ }
+
+ if (wpa_s->dpp_resp_retry_time)
+ wait_time = wpa_s->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(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL);
+ eloop_register_timeout(wait_time / 1000,
+ (wait_time % 1000) * 1000,
+ wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL);
+}
+
+
+static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result result)
+{
+ const char *res_txt;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+ (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+ "FAILED");
+ wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+ " result=%s", freq, MAC2STR(dst), res_txt);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+ " freq=%u result=%s", MAC2STR(dst), freq, res_txt);
+
+ if (!wpa_s->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore TX status since there is no ongoing authentication exchange");
+ return;
+ }
+
+ if (wpa_s->dpp_auth->remove_on_tx_status) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate authentication exchange due to an earlier error");
+ eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s,
+ NULL);
+ offchannel_send_action_done(wpa_s);
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ return;
+ }
+
+ if (wpa_s->dpp_auth_ok_on_ack)
+ wpas_dpp_auth_success(wpa_s, 1);
+
+ if (!is_broadcast_ether_addr(dst) &&
+ result != OFFCHANNEL_SEND_ACTION_SUCCESS) {
+ 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. */
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_auth_init_next(wpa_s);
+ return;
+ }
+ if (auth->waiting_auth_conf) {
+ wpas_dpp_auth_resp_retry(wpa_s);
+ return;
+ }
+ }
+
+ if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp &&
+ result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+ /* 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 (!wpa_s->dpp_auth_ok_on_ack && wpa_s->dpp_auth->neg_freq > 0 &&
+ wpa_s->dpp_auth->curr_freq != wpa_s->dpp_auth->neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
+ wpa_s->dpp_auth->curr_freq,
+ wpa_s->dpp_auth->neg_freq);
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->neg_freq);
+ }
+
+ if (wpa_s->dpp_auth_ok_on_ack)
+ wpa_s->dpp_auth_ok_on_ack = 0;
+}
+
+
+static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->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 = wpa_s->dpp_resp_wait_time ?
+ wpa_s->dpp_resp_wait_time : 2000;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->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(wpa_s, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_listen_stop(wpa_s);
+ dpp_auth_deinit(auth);
+ wpa_s->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");
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_listen_stop(wpa_s);
+ wpas_dpp_auth_init_next(wpa_s);
+ 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);
+ wpa_s->dpp_in_response_listen = 1;
+ wpas_dpp_listen_start(wpa_s, freq);
+
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+}
+
+
+static void wpas_dpp_set_testing_options(struct wpa_supplicant *wpa_s,
+ struct dpp_authentication *auth)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->dpp_config_obj_override)
+ auth->config_obj_override =
+ os_strdup(wpa_s->dpp_config_obj_override);
+ if (wpa_s->dpp_discovery_override)
+ auth->discovery_override =
+ os_strdup(wpa_s->dpp_discovery_override);
+ if (wpa_s->dpp_groups_override)
+ auth->groups_override =
+ os_strdup(wpa_s->dpp_groups_override);
+ auth->ignore_netaccesskey_mismatch =
+ wpa_s->dpp_ignore_netaccesskey_mismatch;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static int wpas_dpp_set_configurator(struct wpa_supplicant *wpa_s,
+ struct dpp_authentication *auth,
+ const char *cmd)
+{
+ const char *pos, *end;
+ struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+ struct dpp_configurator *conf = NULL;
+ u8 ssid[32] = { "test" };
+ size_t ssid_len = 4;
+ char pass[64] = { };
+ size_t pass_len = 0;
+ u8 psk[PMK_LEN];
+ int psk_set = 0;
+ char *group_id = NULL;
+
+ if (!cmd)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
+ pos = os_strstr(cmd, " ssid=");
+ if (pos) {
+ pos += 6;
+ end = os_strchr(pos, ' ');
+ ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
+ ssid_len /= 2;
+ if (ssid_len > sizeof(ssid) ||
+ hexstr2bin(pos, ssid, ssid_len) < 0)
+ goto fail;
+ }
+
+ pos = os_strstr(cmd, " pass=");
+ if (pos) {
+ pos += 6;
+ end = os_strchr(pos, ' ');
+ pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
+ pass_len /= 2;
+ if (pass_len > sizeof(pass) - 1 || pass_len < 8 ||
+ hexstr2bin(pos, (u8 *) pass, pass_len) < 0)
+ goto fail;
+ }
+
+ pos = os_strstr(cmd, " psk=");
+ if (pos) {
+ pos += 5;
+ if (hexstr2bin(pos, psk, PMK_LEN) < 0)
+ goto fail;
+ psk_set = 1;
+ }
+
+ pos = os_strstr(cmd, " group_id=");
+ if (pos) {
+ size_t group_id_len;
+
+ pos += 10;
+ end = os_strchr(pos, ' ');
+ group_id_len = end ? (size_t) (end - pos) : os_strlen(pos);
+ group_id = os_malloc(group_id_len + 1);
+ if (!group_id)
+ goto fail;
+ os_memcpy(group_id, pos, group_id_len);
+ group_id[group_id_len] = '\0';
+ }
+
+ if (os_strstr(cmd, " conf=sta-")) {
+ conf_sta = os_zalloc(sizeof(struct dpp_configuration));
+ if (!conf_sta)
+ goto fail;
+ os_memcpy(conf_sta->ssid, ssid, ssid_len);
+ conf_sta->ssid_len = ssid_len;
+ if (os_strstr(cmd, " conf=sta-psk") ||
+ os_strstr(cmd, " conf=sta-sae") ||
+ os_strstr(cmd, " conf=sta-psk-sae")) {
+ if (os_strstr(cmd, " conf=sta-psk-sae"))
+ conf_sta->akm = DPP_AKM_PSK_SAE;
+ else if (os_strstr(cmd, " conf=sta-sae"))
+ conf_sta->akm = DPP_AKM_SAE;
+ else
+ conf_sta->akm = DPP_AKM_PSK;
+ if (psk_set) {
+ os_memcpy(conf_sta->psk, psk, PMK_LEN);
+ } else if (pass_len > 0) {
+ conf_sta->passphrase = os_strdup(pass);
+ if (!conf_sta->passphrase)
+ goto fail;
+ } else {
+ goto fail;
+ }
+ } else if (os_strstr(cmd, " conf=sta-dpp")) {
+ conf_sta->akm = DPP_AKM_DPP;
+ } else {
+ goto fail;
+ }
+ if (os_strstr(cmd, " group_id=")) {
+ conf_sta->group_id = group_id;
+ group_id = NULL;
+ }
+ }
+
+ if (os_strstr(cmd, " conf=ap-")) {
+ conf_ap = os_zalloc(sizeof(struct dpp_configuration));
+ if (!conf_ap)
+ goto fail;
+ os_memcpy(conf_ap->ssid, ssid, ssid_len);
+ conf_ap->ssid_len = ssid_len;
+ if (os_strstr(cmd, " conf=ap-psk") ||
+ os_strstr(cmd, " conf=ap-sae") ||
+ os_strstr(cmd, " conf=ap-psk-sae")) {
+ if (os_strstr(cmd, " conf=ap-psk-sae"))
+ conf_ap->akm = DPP_AKM_PSK_SAE;
+ else if (os_strstr(cmd, " conf=ap-sae"))
+ conf_ap->akm = DPP_AKM_SAE;
+ else
+ conf_ap->akm = DPP_AKM_PSK;
+ if (psk_set) {
+ os_memcpy(conf_ap->psk, psk, PMK_LEN);
+ } else {
+ conf_ap->passphrase = os_strdup(pass);
+ if (!conf_ap->passphrase)
+ goto fail;
+ }
+ } else if (os_strstr(cmd, " conf=ap-dpp")) {
+ conf_ap->akm = DPP_AKM_DPP;
+ } else {
+ goto fail;
+ }
+ if (os_strstr(cmd, " group_id=")) {
+ conf_ap->group_id = group_id;
+ group_id = NULL;
+ }
+ }
+
+ pos = os_strstr(cmd, " expiry=");
+ if (pos) {
+ long int val;
+
+ pos += 8;
+ val = strtol(pos, NULL, 0);
+ if (val <= 0)
+ goto fail;
+ if (conf_sta)
+ conf_sta->netaccesskey_expiry = val;
+ if (conf_ap)
+ conf_ap->netaccesskey_expiry = val;
+ }
+
+ pos = os_strstr(cmd, " configurator=");
+ if (pos) {
+ pos += 14;
+ conf = dpp_configurator_get_id(wpa_s, atoi(pos));
+ if (!conf) {
+ wpa_printf(MSG_INFO,
+ "DPP: Could not find the specified configurator");
+ goto fail;
+ }
+ }
+ auth->conf_sta = conf_sta;
+ auth->conf_ap = conf_ap;
+ auth->conf = conf;
+ os_free(group_id);
+ return 0;
+
+fail:
+ wpa_msg(wpa_s, MSG_INFO, "DPP: Failed to set configurator parameters");
+ dpp_configuration_free(conf_sta);
+ dpp_configuration_free(conf_ap);
+ os_free(group_id);
+ return -1;
+}
+
+
+static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (!wpa_s->dpp_auth)
+ return;
+ wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
+ wpas_dpp_auth_init_next(wpa_s);
+}
+
+
+static int wpas_dpp_auth_init_next(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+ const u8 *dst;
+ unsigned int wait_time, max_wait_time, freq, max_tries, used;
+ struct os_reltime now, diff;
+
+ wpa_s->dpp_in_response_listen = 0;
+ if (!auth)
+ return -1;
+
+ if (auth->freq_idx == 0)
+ os_get_reltime(&wpa_s->dpp_init_iter_start);
+
+ if (auth->freq_idx >= auth->num_freq) {
+ auth->num_freq_iters++;
+ if (wpa_s->dpp_init_max_tries)
+ max_tries = wpa_s->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(wpa_s, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
+ eloop_cancel_timeout(wpas_dpp_reply_wait_timeout,
+ wpa_s, NULL);
+ offchannel_send_action_done(wpa_s);
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ return -1;
+ }
+ auth->freq_idx = 0;
+ eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
+ if (wpa_s->dpp_init_retry_time)
+ wait_time = wpa_s->dpp_init_retry_time;
+ else
+ wait_time = 10000;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->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,
+ wpas_dpp_init_timeout, wpa_s,
+ 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;
+ wpa_s->dpp_auth_ok_on_ack = 0;
+ eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+ wait_time = wpa_s->max_remain_on_chan;
+ max_wait_time = wpa_s->dpp_resp_wait_time ?
+ wpa_s->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,
+ wpas_dpp_reply_wait_timeout,
+ wpa_s, 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(wpa_s, 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(&wpa_s->dpp_last_init);
+ return offchannel_send_action(wpa_s, freq, dst,
+ wpa_s->own_addr, broadcast,
+ wpabuf_head(auth->req_msg),
+ wpabuf_len(auth->req_msg),
+ wait_time, wpas_dpp_tx_status, 0);
+}
+
+
+int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, 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;
+
+ wpa_s->dpp_gas_client = 0;
+
+ pos = os_strstr(cmd, " peer=");
+ if (!pos)
+ return -1;
+ pos += 6;
+ peer_bi = dpp_bootstrap_get_id(wpa_s, 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(wpa_s, 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, " netrole=");
+ if (pos) {
+ pos += 9;
+ wpa_s->dpp_netrole_ap = os_strncmp(pos, "ap", 2) == 0;
+ }
+
+ pos = os_strstr(cmd, " neg_freq=");
+ if (pos)
+ neg_freq = atoi(pos + 10);
+
+ if (wpa_s->dpp_auth) {
+ eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s,
+ NULL);
+ offchannel_send_action_done(wpa_s);
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ }
+ wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, allowed_roles,
+ neg_freq,
+ wpa_s->hw.modes, wpa_s->hw.num_modes);
+ if (!wpa_s->dpp_auth)
+ goto fail;
+ wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth);
+ if (wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, cmd) < 0) {
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ goto fail;
+ }
+
+ wpa_s->dpp_auth->neg_freq = neg_freq;
+
+ if (!is_zero_ether_addr(peer_bi->mac_addr))
+ os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
+ ETH_ALEN);
+
+ return wpas_dpp_auth_init_next(wpa_s);
+fail:
+ return -1;
+}
+
+
+struct wpas_dpp_listen_work {
+ unsigned int freq;
+ unsigned int duration;
+ struct wpabuf *probe_resp_ie;
+};
+
+
+static void wpas_dpp_listen_work_free(struct wpas_dpp_listen_work *lwork)
+{
+ if (!lwork)
+ return;
+ os_free(lwork);
+}
+
+
+static void wpas_dpp_listen_work_done(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_dpp_listen_work *lwork;
+
+ if (!wpa_s->dpp_listen_work)
+ return;
+
+ lwork = wpa_s->dpp_listen_work->ctx;
+ wpas_dpp_listen_work_free(lwork);
+ radio_work_done(wpa_s->dpp_listen_work);
+ wpa_s->dpp_listen_work = NULL;
+}
+
+
+static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct wpas_dpp_listen_work *lwork = work->ctx;
+
+ if (deinit) {
+ if (work->started) {
+ wpa_s->dpp_listen_work = NULL;
+ wpas_dpp_listen_stop(wpa_s);
+ }
+ wpas_dpp_listen_work_free(lwork);
+ return;
+ }
+
+ wpa_s->dpp_listen_work = work;
+
+ wpa_s->dpp_pending_listen_freq = lwork->freq;
+
+ if (wpa_drv_remain_on_channel(wpa_s, lwork->freq,
+ wpa_s->max_remain_on_chan) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to request the driver to remain on channel (%u MHz) for listen",
+ lwork->freq);
+ wpas_dpp_listen_work_done(wpa_s);
+ wpa_s->dpp_pending_listen_freq = 0;
+ return;
+ }
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = lwork->freq;
+}
+
+
+static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ struct wpas_dpp_listen_work *lwork;
+
+ if (wpa_s->dpp_listen_work) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Reject start_listen since dpp_listen_work already exists");
+ return -1;
+ }
+
+ if (wpa_s->dpp_listen_freq)
+ wpas_dpp_listen_stop(wpa_s);
+ wpa_s->dpp_listen_freq = freq;
+
+ lwork = os_zalloc(sizeof(*lwork));
+ if (!lwork)
+ return -1;
+ lwork->freq = freq;
+
+ if (radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb,
+ lwork) < 0) {
+ wpas_dpp_listen_work_free(lwork);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ int freq;
+
+ freq = atoi(cmd);
+ if (freq <= 0)
+ return -1;
+
+ if (os_strstr(cmd, " role=configurator"))
+ wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strstr(cmd, " role=enrollee"))
+ wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+ else
+ wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ wpa_s->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+ wpa_s->dpp_netrole_ap = os_strstr(cmd, " netrole=ap") != NULL;
+ if (wpa_s->dpp_listen_freq == (unsigned int) freq) {
+ wpa_printf(MSG_DEBUG, "DPP: Already listening on %u MHz",
+ freq);
+ return 0;
+ }
+
+ return wpas_dpp_listen_start(wpa_s, freq);
+}
+
+
+void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->dpp_in_response_listen = 0;
+ if (!wpa_s->dpp_listen_freq)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Stop listen on %u MHz",
+ wpa_s->dpp_listen_freq);
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->dpp_listen_freq = 0;
+ wpas_dpp_listen_work_done(wpa_s);
+}
+
+
+void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ wpas_dpp_listen_work_done(wpa_s);
+
+ if (wpa_s->dpp_auth && wpa_s->dpp_in_response_listen) {
+ unsigned int new_freq;
+
+ /* Continue listen with a new remain-on-channel */
+ if (wpa_s->dpp_auth->neg_freq > 0)
+ new_freq = wpa_s->dpp_auth->neg_freq;
+ else
+ new_freq = wpa_s->dpp_auth->curr_freq;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue wait on %u MHz for the ongoing DPP provisioning session",
+ new_freq);
+ wpas_dpp_listen_start(wpa_s, new_freq);
+ return;
+ }
+
+ if (wpa_s->dpp_listen_freq) {
+ /* Continue listen with a new remain-on-channel */
+ wpas_dpp_listen_start(wpa_s, wpa_s->dpp_listen_freq);
+ }
+}
+
+
+static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, 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(wpa_s, 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(wpa_s, 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, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+ list) {
+ if (!own_bi && bi->own &&
+ os_memcmp(bi->pubkey_hash, r_bootstrap,
+ SHA256_MAC_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Found matching own bootstrapping information");
+ own_bi = bi;
+ }
+
+ if (!peer_bi && !bi->own &&
+ os_memcmp(bi->pubkey_hash, i_bootstrap,
+ SHA256_MAC_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Found matching peer bootstrapping information");
+ peer_bi = bi;
+ }
+
+ if (own_bi && peer_bi)
+ break;
+ }
+
+ if (!own_bi) {
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+ "No matching own bootstrapping key found - ignore message");
+ return;
+ }
+
+ if (wpa_s->dpp_auth) {
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+ "Already in DPP authentication exchange - ignore new one");
+ return;
+ }
+
+ wpa_s->dpp_gas_client = 0;
+ wpa_s->dpp_auth_ok_on_ack = 0;
+ wpa_s->dpp_auth = dpp_auth_req_rx(wpa_s, wpa_s->dpp_allowed_roles,
+ wpa_s->dpp_qr_mutual,
+ peer_bi, own_bi, freq, hdr, buf, len);
+ if (!wpa_s->dpp_auth) {
+ wpa_printf(MSG_DEBUG, "DPP: No response generated");
+ return;
+ }
+ wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth);
+ if (wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth,
+ wpa_s->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ return;
+ }
+ os_memcpy(wpa_s->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+ if (wpa_s->dpp_listen_freq &&
+ wpa_s->dpp_listen_freq != wpa_s->dpp_auth->curr_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Stop listen on %u MHz to allow response on the request %u MHz",
+ wpa_s->dpp_listen_freq, wpa_s->dpp_auth->curr_freq);
+ wpas_dpp_listen_stop(wpa_s);
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), wpa_s->dpp_auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
+ src, wpa_s->own_addr, broadcast,
+ wpabuf_head(wpa_s->dpp_auth->resp_msg),
+ wpabuf_len(wpa_s->dpp_auth->resp_msg),
+ 500, wpas_dpp_tx_status, 0);
+}
+
+
+static void wpas_dpp_start_gas_server(struct wpa_supplicant *wpa_s)
+{
+ /* TODO: stop wait and start ROC */
+}
+
+
+static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s,
+ struct dpp_authentication *auth)
+{
+ struct wpa_ssid *ssid;
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (!ssid)
+ return NULL;
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->disabled = 1;
+
+ ssid->ssid = os_malloc(auth->ssid_len);
+ if (!ssid->ssid)
+ goto fail;
+ os_memcpy(ssid->ssid, auth->ssid, auth->ssid_len);
+ ssid->ssid_len = auth->ssid_len;
+
+ if (auth->connector) {
+ ssid->key_mgmt = WPA_KEY_MGMT_DPP;
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+ ssid->dpp_connector = os_strdup(auth->connector);
+ if (!ssid->dpp_connector)
+ goto fail;
+ }
+
+ if (auth->c_sign_key) {
+ ssid->dpp_csign = os_malloc(wpabuf_len(auth->c_sign_key));
+ if (!ssid->dpp_csign)
+ goto fail;
+ os_memcpy(ssid->dpp_csign, wpabuf_head(auth->c_sign_key),
+ wpabuf_len(auth->c_sign_key));
+ ssid->dpp_csign_len = wpabuf_len(auth->c_sign_key);
+ }
+
+ if (auth->net_access_key) {
+ ssid->dpp_netaccesskey =
+ os_malloc(wpabuf_len(auth->net_access_key));
+ if (!ssid->dpp_netaccesskey)
+ goto fail;
+ os_memcpy(ssid->dpp_netaccesskey,
+ wpabuf_head(auth->net_access_key),
+ wpabuf_len(auth->net_access_key));
+ ssid->dpp_netaccesskey_len = wpabuf_len(auth->net_access_key);
+ ssid->dpp_netaccesskey_expiry = auth->net_access_key_expiry;
+ }
+
+ if (!auth->connector) {
+ ssid->key_mgmt = 0;
+ if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_PSK_SAE)
+ ssid->key_mgmt |= WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_FT_PSK;
+ if (auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE)
+ ssid->key_mgmt |= WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_FT_SAE;
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+ if (auth->passphrase[0]) {
+ if (wpa_config_set_quoted(ssid, "psk",
+ auth->passphrase) < 0)
+ goto fail;
+ wpa_config_update_psk(ssid);
+ ssid->export_keys = 1;
+ } else {
+ ssid->psk_set = auth->psk_set;
+ os_memcpy(ssid->psk, auth->psk, PMK_LEN);
+ }
+ }
+
+ return ssid;
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ return NULL;
+}
+
+
+static void wpas_dpp_process_config(struct wpa_supplicant *wpa_s,
+ struct dpp_authentication *auth)
+{
+ struct wpa_ssid *ssid;
+
+ if (wpa_s->conf->dpp_config_processing < 1)
+ return;
+
+ ssid = wpas_dpp_add_network(wpa_s, auth);
+ if (!ssid)
+ return;
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id);
+ if (wpa_s->conf->dpp_config_processing < 2)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network");
+ ssid->disabled = 0;
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+ wpa_s->scan_runs = 0;
+ wpa_s->normal_scans = 0;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s,
+ struct dpp_authentication *auth)
+{
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+ if (auth->ssid_len)
+ wpa_msg(wpa_s, 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(wpa_s, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
+ auth->connector);
+ }
+ 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(wpa_s, 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(wpa_s, MSG_INFO,
+ DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
+ (long unsigned)
+ auth->net_access_key_expiry);
+ else
+ wpa_msg(wpa_s, MSG_INFO,
+ DPP_EVENT_NET_ACCESS_KEY "%s", hex);
+ os_free(hex);
+ }
+ }
+
+ wpas_dpp_process_config(wpa_s, auth);
+}
+
+
+static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ wpa_s->dpp_gas_dialog_token = -1;
+
+ if (!auth || !auth->auth_success) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return;
+ }
+ if (result != GAS_QUERY_SUCCESS ||
+ !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;
+ }
+
+ wpas_dpp_handle_config_obj(wpa_s, auth);
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ return;
+
+fail:
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+}
+
+
+static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+ struct wpabuf *buf, *conf_req;
+ char json[100];
+ int res;
+
+ wpa_s->dpp_gas_client = 1;
+ os_snprintf(json, sizeof(json),
+ "{\"name\":\"Test\","
+ "\"wi-fi_tech\":\"infra\","
+ "\"netRole\":\"%s\"}",
+ wpa_s->dpp_netrole_ap ? "ap" : "sta");
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr");
+ json[29] = 'k'; /* replace "infra" with "knfra" */
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
+
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_listen_stop(wpa_s);
+
+ 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_req(wpa_s->gas, auth->peer_mac_addr, auth->curr_freq,
+ 1, buf, wpas_dpp_gas_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_msg(wpa_s, 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);
+ wpa_s->dpp_gas_dialog_token = res;
+ }
+}
+
+
+static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator)
+{
+ wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+ wpa_msg(wpa_s, 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 (wpa_s->dpp_auth->configurator) {
+ /* Prevent GAS response */
+ wpa_s->dpp_auth->auth_success = 0;
+ }
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wpa_s->dpp_auth->configurator)
+ wpas_dpp_start_gas_server(wpa_s);
+ else
+ wpas_dpp_start_gas_client(wpa_s);
+}
+
+
+static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR
+ " (freq %u MHz)", MAC2STR(src), freq);
+
+ 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(wpas_dpp_reply_wait_timeout, wpa_s, 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(wpas_dpp_init_timeout, wpa_s, 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: Start wait for full response");
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_listen_start(wpa_s, auth->curr_freq);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+ return;
+ }
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), auth->curr_freq, DPP_PA_AUTHENTICATION_CONF);
+ offchannel_send_action(wpa_s, auth->curr_freq,
+ src, wpa_s->own_addr, broadcast,
+ wpabuf_head(msg), wpabuf_len(msg),
+ 500, wpas_dpp_tx_status, 0);
+ wpabuf_free(msg);
+ wpa_s->dpp_auth_ok_on_ack = 1;
+}
+
+
+static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ 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;
+ }
+
+ wpas_dpp_auth_success(wpa_s, 0);
+}
+
+
+static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ const u8 *buf, size_t len)
+{
+ struct wpa_ssid *ssid;
+ const u8 *connector, *trans_id, *status;
+ u16 connector_len, trans_id_len, status_len;
+ struct dpp_introduction intro;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_time now;
+ struct os_reltime rnow;
+ os_time_t expiry;
+ unsigned int seconds;
+ enum dpp_status_error res;
+
+ wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Response from " MACSTR,
+ MAC2STR(src));
+ if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) ||
+ os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from "
+ MACSTR " - drop", MAC2STR(src));
+ return;
+ }
+ offchannel_send_action_done(wpa_s);
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == wpa_s->dpp_intro_network)
+ break;
+ }
+ if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+ !ssid->dpp_csign) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Profile not found for network introduction");
+ 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");
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+ " fail=missing_transaction_id", MAC2STR(src));
+ goto fail;
+ }
+ if (trans_id[0] != TRANSACTION_ID) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore frame with unexpected Transaction ID %u",
+ trans_id[0]);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+ " fail=transaction_id_mismatch", MAC2STR(src));
+ goto fail;
+ }
+
+ status = dpp_get_attr(buf, len, DPP_ATTR_STATUS, &status_len);
+ if (!status || status_len != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Peer did not include Status");
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+ " fail=missing_status", MAC2STR(src));
+ goto fail;
+ }
+ if (status[0] != DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer rejected network introduction: Status %u",
+ status[0]);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+ " status=%u", MAC2STR(src), status[0]);
+ goto fail;
+ }
+
+ connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+ if (!connector) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include its Connector");
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+ " fail=missing_connector", MAC2STR(src));
+ goto fail;
+ }
+
+ res = dpp_peer_intro(&intro, ssid->dpp_connector,
+ ssid->dpp_netaccesskey,
+ ssid->dpp_netaccesskey_len,
+ ssid->dpp_csign,
+ ssid->dpp_csign_len,
+ connector, connector_len, &expiry);
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in failure");
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+ " fail=peer_connector_validation_failed", MAC2STR(src));
+ goto fail;
+ }
+
+ entry = os_zalloc(sizeof(*entry));
+ if (!entry)
+ goto fail;
+ os_memcpy(entry->aa, src, ETH_ALEN);
+ os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
+ os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
+ entry->pmk_len = intro.pmk_len;
+ entry->akmp = WPA_KEY_MGMT_DPP;
+ if (expiry) {
+ os_get_time(&now);
+ seconds = expiry - now.sec;
+ } else {
+ seconds = 86400 * 7;
+ }
+ os_get_reltime(&rnow);
+ entry->expiration = rnow.sec + seconds;
+ entry->reauth_time = rnow.sec + seconds;
+ entry->network_ctx = ssid;
+ wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+ " status=%u", MAC2STR(src), status[0]);
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Try connection again after successful network introduction");
+ if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+fail:
+ os_memset(&intro, 0, sizeof(intro));
+}
+
+
+static int wpas_dpp_allow_ir(struct wpa_supplicant *wpa_s, unsigned int freq)
+{
+ int i, j;
+
+ if (!wpa_s->hw.modes)
+ return -1;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
+
+ for (j = 0; j < mode->num_channels; j++) {
+ struct hostapd_channel_data *chan = &mode->channels[j];
+
+ if (chan->freq != (int) freq)
+ continue;
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Frequency %u MHz not supported or does not allow PKEX initiation in the current channel list",
+ freq);
+
+ return 0;
+}
+
+
+static int wpas_dpp_pkex_next_channel(struct wpa_supplicant *wpa_s,
+ struct dpp_pkex *pkex)
+{
+ if (pkex->freq == 2437)
+ pkex->freq = 5745;
+ else if (pkex->freq == 5745)
+ pkex->freq = 5220;
+ else if (pkex->freq == 5220)
+ pkex->freq = 60480;
+ else
+ return -1; /* no more channels to try */
+
+ if (wpas_dpp_allow_ir(wpa_s, pkex->freq) == 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Try to initiate on %u MHz",
+ pkex->freq);
+ return 0;
+ }
+
+ /* Could not use this channel - try the next one */
+ return wpas_dpp_pkex_next_channel(wpa_s, pkex);
+}
+
+
+static void wpas_dpp_pkex_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_pkex *pkex = wpa_s->dpp_pkex;
+
+ if (!pkex || !pkex->exchange_req)
+ return;
+ if (pkex->exch_req_tries >= 5) {
+ if (wpas_dpp_pkex_next_channel(wpa_s, pkex) < 0) {
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+ "No response from PKEX peer");
+ dpp_pkex_free(pkex);
+ wpa_s->dpp_pkex = NULL;
+ return;
+ }
+ pkex->exch_req_tries = 0;
+ }
+
+ pkex->exch_req_tries++;
+ wpa_printf(MSG_DEBUG, "DPP: Retransmit PKEX Exchange Request (try %u)",
+ pkex->exch_req_tries);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(broadcast), pkex->freq, DPP_PA_PKEX_EXCHANGE_REQ);
+ offchannel_send_action(wpa_s, pkex->freq, broadcast,
+ wpa_s->own_addr, broadcast,
+ wpabuf_head(pkex->exchange_req),
+ wpabuf_len(pkex->exchange_req),
+ pkex->exch_req_wait_time,
+ wpas_dpp_tx_pkex_status, 0);
+}
+
+
+static void
+wpas_dpp_tx_pkex_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result result)
+{
+ const char *res_txt;
+ struct dpp_pkex *pkex = wpa_s->dpp_pkex;
+
+ res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+ (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+ "FAILED");
+ wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+ " result=%s (PKEX)",
+ freq, MAC2STR(dst), res_txt);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+ " freq=%u result=%s", MAC2STR(dst), freq, res_txt);
+
+ if (!pkex) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore TX status since there is no ongoing PKEX exchange");
+ return;
+ }
+
+ if (pkex->failed) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate PKEX exchange due to an earlier error");
+ if (pkex->t > pkex->own_bi->pkex_t)
+ pkex->own_bi->pkex_t = pkex->t;
+ dpp_pkex_free(pkex);
+ wpa_s->dpp_pkex = NULL;
+ return;
+ }
+
+ if (pkex->exch_req_wait_time && pkex->exchange_req) {
+ /* Wait for PKEX Exchange Response frame and retry request if
+ * no response is seen. */
+ eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL);
+ eloop_register_timeout(pkex->exch_req_wait_time / 1000,
+ (pkex->exch_req_wait_time % 1000) * 1000,
+ wpas_dpp_pkex_retry_timeout, wpa_s,
+ NULL);
+ }
+}
+
+
+static void
+wpas_dpp_rx_pkex_exchange_req(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq)
+{
+ struct wpabuf *msg;
+ unsigned int wait_time;
+
+ 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 (!wpa_s->dpp_pkex_code || !wpa_s->dpp_pkex_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PKEX code configured - ignore request");
+ return;
+ }
+
+ if (wpa_s->dpp_pkex) {
+ /* TODO: Support parallel operations */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Already in PKEX session - ignore new request");
+ return;
+ }
+
+ wpa_s->dpp_pkex = dpp_pkex_rx_exchange_req(wpa_s, wpa_s->dpp_pkex_bi,
+ wpa_s->own_addr, src,
+ wpa_s->dpp_pkex_identifier,
+ wpa_s->dpp_pkex_code,
+ buf, len);
+ if (!wpa_s->dpp_pkex) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to process the request - ignore it");
+ return;
+ }
+
+ msg = wpa_s->dpp_pkex->exchange_resp;
+ wait_time = wpa_s->max_remain_on_chan;
+ if (wait_time > 2000)
+ wait_time = 2000;
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_PKEX_EXCHANGE_RESP);
+ offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+ broadcast,
+ wpabuf_head(msg), wpabuf_len(msg),
+ wait_time, wpas_dpp_tx_pkex_status, 0);
+}
+
+
+static void
+wpas_dpp_rx_pkex_exchange_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq)
+{
+ struct wpabuf *msg;
+ unsigned int wait_time;
+
+ 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 (!wpa_s->dpp_pkex || !wpa_s->dpp_pkex->initiator ||
+ wpa_s->dpp_pkex->exchange_done) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+ return;
+ }
+
+ eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL);
+ wpa_s->dpp_pkex->exch_req_wait_time = 0;
+
+ msg = dpp_pkex_rx_exchange_resp(wpa_s->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));
+
+ wait_time = wpa_s->max_remain_on_chan;
+ if (wait_time > 2000)
+ wait_time = 2000;
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_PKEX_COMMIT_REVEAL_REQ);
+ offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+ broadcast,
+ wpabuf_head(msg), wpabuf_len(msg),
+ wait_time, wpas_dpp_tx_pkex_status, 0);
+ wpabuf_free(msg);
+}
+
+
+static struct dpp_bootstrap_info *
+wpas_dpp_pkex_finish(struct wpa_supplicant *wpa_s, const u8 *peer,
+ unsigned int freq)
+{
+ struct dpp_pkex *pkex = wpa_s->dpp_pkex;
+ struct dpp_bootstrap_info *bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return NULL;
+ bi->id = wpas_dpp_next_id(wpa_s);
+ bi->type = DPP_BOOTSTRAP_PKEX;
+ os_memcpy(bi->mac_addr, peer, ETH_ALEN);
+ bi->num_freq = 1;
+ bi->freq[0] = freq;
+ bi->curve = pkex->own_bi->curve;
+ bi->pubkey = pkex->peer_bootstrap_key;
+ pkex->peer_bootstrap_key = NULL;
+ dpp_pkex_free(pkex);
+ wpa_s->dpp_pkex = NULL;
+ if (dpp_bootstrap_key_hash(bi) < 0) {
+ dpp_bootstrap_info_free(bi);
+ return NULL;
+ }
+ dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+ return bi;
+}
+
+
+static void
+wpas_dpp_rx_pkex_commit_reveal_req(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct wpabuf *msg;
+ unsigned int wait_time;
+ struct dpp_pkex *pkex = wpa_s->dpp_pkex;
+
+ 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 (pkex->failed) {
+ wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange");
+ if (pkex->t > pkex->own_bi->pkex_t)
+ pkex->own_bi->pkex_t = pkex->t;
+ dpp_pkex_free(wpa_s->dpp_pkex);
+ wpa_s->dpp_pkex = NULL;
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
+ MACSTR, MAC2STR(src));
+
+ wait_time = wpa_s->max_remain_on_chan;
+ if (wait_time > 2000)
+ wait_time = 2000;
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_PKEX_COMMIT_REVEAL_RESP);
+ offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
+ broadcast,
+ wpabuf_head(msg), wpabuf_len(msg),
+ wait_time, wpas_dpp_tx_pkex_status, 0);
+ wpabuf_free(msg);
+
+ wpas_dpp_pkex_finish(wpa_s, src, freq);
+}
+
+
+static void
+wpas_dpp_rx_pkex_commit_reveal_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ int res;
+ struct dpp_bootstrap_info *bi;
+ struct dpp_pkex *pkex = wpa_s->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;
+ }
+
+ bi = wpas_dpp_pkex_finish(wpa_s, src, freq);
+ if (!bi)
+ return;
+
+ os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
+ bi->id,
+ wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start authentication after PKEX with parameters: %s",
+ cmd);
+ if (wpas_dpp_auth_init(wpa_s, cmd) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication initialization failed");
+ return;
+ }
+}
+
+
+void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, 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(wpa_s, 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(wpa_s, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d ignore=invalid-attributes",
+ MAC2STR(src), freq, type);
+ return;
+ }
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_RX "src=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), freq, type);
+
+ switch (type) {
+ case DPP_PA_AUTHENTICATION_REQ:
+ wpas_dpp_rx_auth_req(wpa_s, src, hdr, buf, len, freq);
+ break;
+ case DPP_PA_AUTHENTICATION_RESP:
+ wpas_dpp_rx_auth_resp(wpa_s, src, hdr, buf, len, freq);
+ break;
+ case DPP_PA_AUTHENTICATION_CONF:
+ wpas_dpp_rx_auth_conf(wpa_s, src, hdr, buf, len);
+ break;
+ case DPP_PA_PEER_DISCOVERY_RESP:
+ wpas_dpp_rx_peer_disc_resp(wpa_s, src, buf, len);
+ break;
+ case DPP_PA_PKEX_EXCHANGE_REQ:
+ wpas_dpp_rx_pkex_exchange_req(wpa_s, src, buf, len, freq);
+ break;
+ case DPP_PA_PKEX_EXCHANGE_RESP:
+ wpas_dpp_rx_pkex_exchange_resp(wpa_s, src, buf, len, freq);
+ break;
+ case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
+ wpas_dpp_rx_pkex_commit_reveal_req(wpa_s, src, hdr, buf, len,
+ freq);
+ break;
+ case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
+ wpas_dpp_rx_pkex_commit_reveal_resp(wpa_s, src, hdr, buf, len,
+ freq);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignored unsupported frame subtype %d", type);
+ break;
+ }
+
+ if (wpa_s->dpp_pkex)
+ pkex_t = wpa_s->dpp_pkex->t;
+ else if (wpa_s->dpp_pkex_bi)
+ pkex_t = wpa_s->dpp_pkex_bi->pkex_t;
+ else
+ pkex_t = 0;
+ if (pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0");
+ wpas_dpp_pkex_remove(wpa_s, "*");
+ }
+}
+
+
+static struct wpabuf *
+wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query,
+ size_t query_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct dpp_authentication *auth = wpa_s->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(wpa_s, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
+ MAC2STR(sa));
+ resp = dpp_conf_req_rx(auth, query, query_len);
+ if (!resp)
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ auth->conf_resp = resp;
+ return resp;
+}
+
+
+static void
+wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!auth) {
+ wpabuf_free(resp);
+ return;
+ }
+ if (auth->conf_resp != resp) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore GAS status report (ok=%d) for unknown response",
+ ok);
+ wpabuf_free(resp);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
+ ok);
+ eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL);
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_listen_stop(wpa_s);
+ if (ok)
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
+ else
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ wpabuf_free(resp);
+}
+
+
+static unsigned int wpas_dpp_next_configurator_id(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_configurator *conf;
+ unsigned int max_id = 0;
+
+ dl_list_for_each(conf, &wpa_s->dpp_configurator,
+ struct dpp_configurator, list) {
+ if (conf->id > max_id)
+ max_id = conf->id;
+ }
+ return max_id + 1;
+}
+
+
+int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ char *curve = NULL;
+ char *key = NULL;
+ u8 *privkey = NULL;
+ size_t privkey_len = 0;
+ int ret = -1;
+ struct dpp_configurator *conf = NULL;
+
+ curve = get_param(cmd, " curve=");
+ key = get_param(cmd, " key=");
+
+ if (key) {
+ privkey_len = os_strlen(key) / 2;
+ privkey = os_malloc(privkey_len);
+ if (!privkey ||
+ hexstr2bin(key, privkey, privkey_len) < 0)
+ goto fail;
+ }
+
+ conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+ if (!conf)
+ goto fail;
+
+ conf->id = wpas_dpp_next_configurator_id(wpa_s);
+ dl_list_add(&wpa_s->dpp_configurator, &conf->list);
+ ret = conf->id;
+ conf = NULL;
+fail:
+ os_free(curve);
+ str_clear_free(key);
+ bin_clear_free(privkey, privkey_len);
+ dpp_configurator_free(conf);
+ return ret;
+}
+
+
+static int dpp_configurator_del(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ struct dpp_configurator *conf, *tmp;
+ int found = 0;
+
+ dl_list_for_each_safe(conf, tmp, &wpa_s->dpp_configurator,
+ struct dpp_configurator, list) {
+ if (id && conf->id != id)
+ continue;
+ found = 1;
+ dl_list_del(&conf->list);
+ dpp_configurator_free(conf);
+ }
+
+ if (id == 0)
+ return 0; /* flush succeeds regardless of entries found */
+ return found ? 0 : -1;
+}
+
+
+int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id)
+{
+ unsigned int id_val;
+
+ if (os_strcmp(id, "*") == 0) {
+ id_val = 0;
+ } else {
+ id_val = atoi(id);
+ if (id_val == 0)
+ return -1;
+ }
+
+ return dpp_configurator_del(wpa_s, id_val);
+}
+
+
+int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ struct dpp_authentication *auth;
+ int ret = -1;
+ char *curve = NULL;
+
+ auth = os_zalloc(sizeof(*auth));
+ if (!auth)
+ return -1;
+
+ curve = get_param(cmd, " curve=");
+ wpas_dpp_set_testing_options(wpa_s, auth);
+ if (wpas_dpp_set_configurator(wpa_s, auth, cmd) == 0 &&
+ dpp_configurator_own_config(auth, curve, 0) == 0) {
+ wpas_dpp_handle_config_obj(wpa_s, auth);
+ ret = 0;
+ }
+
+ dpp_auth_deinit(auth);
+ os_free(curve);
+
+ return ret;
+}
+
+
+int wpas_dpp_configurator_get_key(struct wpa_supplicant *wpa_s, unsigned int id,
+ char *buf, size_t buflen)
+{
+ struct dpp_configurator *conf;
+
+ conf = dpp_configurator_get_id(wpa_s, id);
+ if (!conf)
+ return -1;
+
+ return dpp_configurator_get_key(conf, buf, buflen);
+}
+
+
+static void
+wpas_dpp_tx_introduction_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result result)
+{
+ const char *res_txt;
+
+ res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+ (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+ "FAILED");
+ wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+ " result=%s (DPP Peer Discovery Request)",
+ freq, MAC2STR(dst), res_txt);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+ " freq=%u result=%s", MAC2STR(dst), freq, res_txt);
+ /* TODO: Time out wait for response more quickly in error cases? */
+}
+
+
+int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_bss *bss)
+{
+ struct os_time now;
+ struct wpabuf *msg;
+ unsigned int wait_time;
+
+ if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss)
+ return 0; /* Not using DPP AKM - continue */
+ if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid))
+ return 0; /* PMKSA exists for DPP AKM - continue */
+
+ if (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+ !ssid->dpp_csign) {
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR
+ "missing %s",
+ !ssid->dpp_connector ? "Connector" :
+ (!ssid->dpp_netaccesskey ? "netAccessKey" :
+ "C-sign-key"));
+ return -1;
+ }
+
+ os_get_time(&now);
+
+ if (ssid->dpp_netaccesskey_expiry &&
+ (os_time_t) ssid->dpp_netaccesskey_expiry < now.sec) {
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR
+ "netAccessKey expired");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Starting network introduction protocol to derive PMKSA for "
+ MACSTR, MAC2STR(bss->bssid));
+
+ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ,
+ 5 + 4 + os_strlen(ssid->dpp_connector));
+ if (!msg)
+ return -1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID");
+ goto skip_trans_id;
+ }
+ if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID");
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 0);
+ goto skip_trans_id;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, TRANSACTION_ID);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_trans_id:
+ if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
+ goto skip_connector;
+ }
+ if (dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_REQ) {
+ char *connector;
+
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector");
+ connector = dpp_corrupt_connector_signature(
+ ssid->dpp_connector);
+ if (!connector) {
+ wpabuf_free(msg);
+ return -1;
+ }
+ 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 */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(ssid->dpp_connector));
+ wpabuf_put_str(msg, ssid->dpp_connector);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_connector:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* TODO: Timeout on AP response */
+ wait_time = wpa_s->max_remain_on_chan;
+ if (wait_time > 2000)
+ wait_time = 2000;
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(bss->bssid), bss->freq, DPP_PA_PEER_DISCOVERY_REQ);
+ offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr,
+ broadcast,
+ wpabuf_head(msg), wpabuf_len(msg),
+ wait_time, wpas_dpp_tx_introduction_status, 0);
+ wpabuf_free(msg);
+
+ /* Request this connection attempt to terminate - new one will be
+ * started when network introduction protocol completes */
+ os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN);
+ wpa_s->dpp_intro_network = ssid;
+ return 1;
+}
+
+
+int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ struct dpp_bootstrap_info *own_bi;
+ const char *pos, *end;
+ unsigned int wait_time;
+
+ pos = os_strstr(cmd, " own=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ own_bi = dpp_bootstrap_get_id(wpa_s, 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;
+ }
+ wpa_s->dpp_pkex_bi = own_bi;
+ own_bi->pkex_t = 0; /* clear pending errors on new code */
+
+ os_free(wpa_s->dpp_pkex_identifier);
+ wpa_s->dpp_pkex_identifier = NULL;
+ pos = os_strstr(cmd, " identifier=");
+ if (pos) {
+ pos += 12;
+ end = os_strchr(pos, ' ');
+ if (!end)
+ return -1;
+ wpa_s->dpp_pkex_identifier = os_malloc(end - pos + 1);
+ if (!wpa_s->dpp_pkex_identifier)
+ return -1;
+ os_memcpy(wpa_s->dpp_pkex_identifier, pos, end - pos);
+ wpa_s->dpp_pkex_identifier[end - pos] = '\0';
+ }
+
+ pos = os_strstr(cmd, " code=");
+ if (!pos)
+ return -1;
+ os_free(wpa_s->dpp_pkex_code);
+ wpa_s->dpp_pkex_code = os_strdup(pos + 6);
+ if (!wpa_s->dpp_pkex_code)
+ return -1;
+
+ if (os_strstr(cmd, " init=1")) {
+ struct dpp_pkex *pkex;
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
+ dpp_pkex_free(wpa_s->dpp_pkex);
+ wpa_s->dpp_pkex = dpp_pkex_init(wpa_s, own_bi, wpa_s->own_addr,
+ wpa_s->dpp_pkex_identifier,
+ wpa_s->dpp_pkex_code);
+ pkex = wpa_s->dpp_pkex;
+ if (!pkex)
+ return -1;
+
+ msg = pkex->exchange_req;
+ wait_time = wpa_s->max_remain_on_chan;
+ if (wait_time > 2000)
+ wait_time = 2000;
+ pkex->freq = 2437;
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(broadcast), pkex->freq,
+ DPP_PA_PKEX_EXCHANGE_REQ);
+ offchannel_send_action(wpa_s, pkex->freq, broadcast,
+ wpa_s->own_addr, broadcast,
+ wpabuf_head(msg), wpabuf_len(msg),
+ wait_time, wpas_dpp_tx_pkex_status, 0);
+ if (wait_time == 0)
+ wait_time = 2000;
+ pkex->exch_req_wait_time = wait_time;
+ pkex->exch_req_tries = 1;
+ }
+
+ /* TODO: Support multiple PKEX info entries */
+
+ os_free(wpa_s->dpp_pkex_auth_cmd);
+ wpa_s->dpp_pkex_auth_cmd = os_strdup(cmd);
+
+ return 1;
+}
+
+
+int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id)
+{
+ unsigned int id_val;
+
+ if (os_strcmp(id, "*") == 0) {
+ id_val = 0;
+ } else {
+ id_val = atoi(id);
+ if (id_val == 0)
+ return -1;
+ }
+
+ if ((id_val != 0 && id_val != 1) || !wpa_s->dpp_pkex_code)
+ return -1;
+
+ /* TODO: Support multiple PKEX entries */
+ os_free(wpa_s->dpp_pkex_code);
+ wpa_s->dpp_pkex_code = NULL;
+ os_free(wpa_s->dpp_pkex_identifier);
+ wpa_s->dpp_pkex_identifier = NULL;
+ os_free(wpa_s->dpp_pkex_auth_cmd);
+ wpa_s->dpp_pkex_auth_cmd = NULL;
+ wpa_s->dpp_pkex_bi = NULL;
+ /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
+ dpp_pkex_free(wpa_s->dpp_pkex);
+ wpa_s->dpp_pkex = NULL;
+ return 0;
+}
+
+
+void wpas_dpp_stop(struct wpa_supplicant *wpa_s)
+{
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ dpp_pkex_free(wpa_s->dpp_pkex);
+ wpa_s->dpp_pkex = NULL;
+ if (wpa_s->dpp_gas_client && wpa_s->dpp_gas_dialog_token >= 0)
+ gas_query_stop(wpa_s->gas, wpa_s->dpp_gas_dialog_token);
+}
+
+
+int wpas_dpp_init(struct wpa_supplicant *wpa_s)
+{
+ u8 adv_proto_id[7];
+
+ adv_proto_id[0] = WLAN_EID_VENDOR_SPECIFIC;
+ adv_proto_id[1] = 5;
+ WPA_PUT_BE24(&adv_proto_id[2], OUI_WFA);
+ adv_proto_id[5] = DPP_OUI_TYPE;
+ adv_proto_id[6] = 0x01;
+
+ if (gas_server_register(wpa_s->gas_server, adv_proto_id,
+ sizeof(adv_proto_id), wpas_dpp_gas_req_handler,
+ wpas_dpp_gas_status_handler, wpa_s) < 0)
+ return -1;
+ dl_list_init(&wpa_s->dpp_bootstrap);
+ dl_list_init(&wpa_s->dpp_configurator);
+ wpa_s->dpp_init_done = 1;
+ return 0;
+}
+
+
+void wpas_dpp_deinit(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ os_free(wpa_s->dpp_config_obj_override);
+ wpa_s->dpp_config_obj_override = NULL;
+ os_free(wpa_s->dpp_discovery_override);
+ wpa_s->dpp_discovery_override = NULL;
+ os_free(wpa_s->dpp_groups_override);
+ wpa_s->dpp_groups_override = NULL;
+ wpa_s->dpp_ignore_netaccesskey_mismatch = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!wpa_s->dpp_init_done)
+ return;
+ eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL);
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_listen_stop(wpa_s);
+ dpp_bootstrap_del(wpa_s, 0);
+ dpp_configurator_del(wpa_s, 0);
+ wpas_dpp_stop(wpa_s);
+ wpas_dpp_pkex_remove(wpa_s, "*");
+ os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN);
+ os_free(wpa_s->dpp_configurator_params);
+ wpa_s->dpp_configurator_params = NULL;
+}
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
new file mode 100644
index 000000000000..5a4f06e2e97e
--- /dev/null
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -0,0 +1,39 @@
+/*
+ * wpa_supplicant - DPP
+ * 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_SUPPLICANT_H
+#define DPP_SUPPLICANT_H
+
+int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id);
+const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
+ unsigned int id);
+int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id,
+ char *reply, int reply_size);
+int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd);
+void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s);
+void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq);
+int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id);
+int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_configurator_get_key(struct wpa_supplicant *wpa_s, unsigned int id,
+ char *buf, size_t buflen);
+int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id);
+void wpas_dpp_stop(struct wpa_supplicant *wpa_s);
+int wpas_dpp_init(struct wpa_supplicant *wpa_s);
+void wpas_dpp_deinit(struct wpa_supplicant *wpa_s);
+int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_bss *bss);
+
+#endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 220b7ba3ddca..078de23f794f 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -189,20 +189,19 @@ static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s,
}
static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
- const u8 *bssid, const u8 *pmkid)
+ struct wpa_pmkid_params *params)
{
if (wpa_s->driver->add_pmkid) {
- return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid);
+ return wpa_s->driver->add_pmkid(wpa_s->drv_priv, params);
}
return -1;
}
static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s,
- const u8 *bssid, const u8 *pmkid)
+ struct wpa_pmkid_params *params)
{
if (wpa_s->driver->remove_pmkid) {
- return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid,
- pmkid);
+ return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, params);
}
return -1;
}
@@ -276,11 +275,12 @@ static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s,
static inline struct hostapd_hw_modes *
wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
- u16 *flags)
+ u16 *flags, u8 *dfs_domain)
{
if (wpa_s->driver->get_hw_feature_data)
return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv,
- num_modes, flags);
+ num_modes, flags,
+ dfs_domain);
return NULL;
}
@@ -689,6 +689,14 @@ static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed,
return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid);
}
+static inline int wpa_drv_disable_fils(struct wpa_supplicant *wpa_s,
+ int disable)
+{
+ if (!wpa_s->driver->disable_fils)
+ return -1;
+ return wpa_s->driver->disable_fils(wpa_s->drv_priv, disable);
+}
+
static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s,
const u8 *addr)
{
@@ -715,6 +723,14 @@ static inline int wpa_drv_macsec_deinit(struct wpa_supplicant *wpa_s)
return wpa_s->driver->macsec_deinit(wpa_s->drv_priv);
}
+static inline int wpa_drv_macsec_get_capability(struct wpa_supplicant *wpa_s,
+ enum macsec_cap *cap)
+{
+ if (!wpa_s->driver->macsec_get_capability)
+ return -1;
+ return wpa_s->driver->macsec_get_capability(wpa_s->drv_priv, cap);
+}
+
static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
Boolean enabled)
{
@@ -723,6 +739,14 @@ static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled);
}
+static inline int wpa_drv_enable_encrypt(struct wpa_supplicant *wpa_s,
+ Boolean enabled)
+{
+ if (!wpa_s->driver->enable_encrypt)
+ return -1;
+ return wpa_s->driver->enable_encrypt(wpa_s->drv_priv, enabled);
+}
+
static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s,
Boolean enabled, u32 window)
{
@@ -749,145 +773,127 @@ static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s,
}
static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an,
- u32 *lowest_pn)
+ struct receive_sa *sa)
{
if (!wpa_s->driver->get_receive_lowest_pn)
return -1;
- return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel,
- an, lowest_pn);
+ return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, sa);
}
static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an,
- u32 *next_pn)
+ struct transmit_sa *sa)
{
if (!wpa_s->driver->get_transmit_next_pn)
return -1;
- return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, channel,
- an, next_pn);
+ return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, sa);
}
static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an,
- u32 next_pn)
+ struct transmit_sa *sa)
{
if (!wpa_s->driver->set_transmit_next_pn)
return -1;
- return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, channel,
- an, next_pn);
-}
-
-static inline int wpa_drv_get_available_receive_sc(struct wpa_supplicant *wpa_s,
- u32 *channel)
-{
- if (!wpa_s->driver->get_available_receive_sc)
- return -1;
- return wpa_s->driver->get_available_receive_sc(wpa_s->drv_priv,
- channel);
+ return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, sa);
}
static inline int
-wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel,
- const u8 *sci_addr, u16 sci_port,
+wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, struct receive_sc *sc,
unsigned int conf_offset, int validation)
{
if (!wpa_s->driver->create_receive_sc)
return -1;
- return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, channel,
- sci_addr, sci_port, conf_offset,
- validation);
+ return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, sc,
+ conf_offset, validation);
}
static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s,
- u32 channel)
+ struct receive_sc *sc)
{
if (!wpa_s->driver->delete_receive_sc)
return -1;
- return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel);
+ return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, sc);
}
static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an,
- u32 lowest_pn, const u8 *sak)
+ struct receive_sa *sa)
{
if (!wpa_s->driver->create_receive_sa)
return -1;
- return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, channel, an,
- lowest_pn, sak);
+ return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, sa);
}
-static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an)
+static inline int wpa_drv_delete_receive_sa(struct wpa_supplicant *wpa_s,
+ struct receive_sa *sa)
{
- if (!wpa_s->driver->enable_receive_sa)
+ if (!wpa_s->driver->delete_receive_sa)
return -1;
- return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an);
+ return wpa_s->driver->delete_receive_sa(wpa_s->drv_priv, sa);
}
-static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an)
+static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s,
+ struct receive_sa *sa)
{
- if (!wpa_s->driver->disable_receive_sa)
+ if (!wpa_s->driver->enable_receive_sa)
return -1;
- return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an);
+ return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, sa);
}
-static inline int
-wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel)
+static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s,
+ struct receive_sa *sa)
{
- if (!wpa_s->driver->get_available_transmit_sc)
+ if (!wpa_s->driver->disable_receive_sa)
return -1;
- return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv,
- channel);
+ return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, sa);
}
static inline int
-wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel,
- const u8 *sci_addr, u16 sci_port,
+wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, struct transmit_sc *sc,
unsigned int conf_offset)
{
if (!wpa_s->driver->create_transmit_sc)
return -1;
- return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, channel,
- sci_addr, sci_port,
+ return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, sc,
conf_offset);
}
static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s,
- u32 channel)
+ struct transmit_sc *sc)
{
if (!wpa_s->driver->delete_transmit_sc)
return -1;
- return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel);
+ return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, sc);
}
static inline int wpa_drv_create_transmit_sa(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an,
- u32 next_pn,
- Boolean confidentiality,
- const u8 *sak)
+ struct transmit_sa *sa)
{
if (!wpa_s->driver->create_transmit_sa)
return -1;
- return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, channel, an,
- next_pn, confidentiality, sak);
+ return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, sa);
+}
+
+static inline int wpa_drv_delete_transmit_sa(struct wpa_supplicant *wpa_s,
+ struct transmit_sa *sa)
+{
+ if (!wpa_s->driver->delete_transmit_sa)
+ return -1;
+ return wpa_s->driver->delete_transmit_sa(wpa_s->drv_priv, sa);
}
static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an)
+ struct transmit_sa *sa)
{
if (!wpa_s->driver->enable_transmit_sa)
return -1;
- return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an);
+ return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, sa);
}
static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s,
- u32 channel, u8 an)
+ struct transmit_sa *sa)
{
if (!wpa_s->driver->disable_transmit_sa)
return -1;
- return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an);
+ return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, sa);
}
#endif /* CONFIG_MACSEC */
@@ -904,6 +910,11 @@ static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
unsigned int *num,
unsigned int *freq_list)
{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->get_pref_freq_list_override)
+ return wpas_ctrl_iface_get_pref_freq_list_override(
+ wpa_s, if_type, num, freq_list);
+#endif /* CONFIG_TESTING_OPTIONS */
if (!wpa_s->driver->get_pref_freq_list)
return -1;
return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
@@ -918,11 +929,12 @@ static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq);
}
-static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s)
+static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s,
+ u64 scan_cookie)
{
if (!wpa_s->driver->abort_scan)
return -1;
- return wpa_s->driver->abort_scan(wpa_s->drv_priv);
+ return wpa_s->driver->abort_scan(wpa_s->drv_priv, scan_cookie);
}
static inline int wpa_drv_configure_frame_filters(struct wpa_supplicant *wpa_s,
@@ -976,4 +988,62 @@ static inline int wpa_drv_set_default_scan_ies(struct wpa_supplicant *wpa_s,
return wpa_s->driver->set_default_scan_ies(wpa_s->drv_priv, ies, len);
}
+static inline int wpa_drv_set_tdls_mode(struct wpa_supplicant *wpa_s,
+ int tdls_external_control)
+{
+ if (!wpa_s->driver->set_tdls_mode)
+ return -1;
+ return wpa_s->driver->set_tdls_mode(wpa_s->drv_priv,
+ tdls_external_control);
+}
+
+static inline struct wpa_bss_candidate_info *
+wpa_drv_get_bss_trans_status(struct wpa_supplicant *wpa_s,
+ struct wpa_bss_trans_info *params)
+{
+ if (!wpa_s->driver->get_bss_transition_status)
+ return NULL;
+ return wpa_s->driver->get_bss_transition_status(wpa_s->drv_priv,
+ params);
+}
+
+static inline int wpa_drv_ignore_assoc_disallow(struct wpa_supplicant *wpa_s,
+ int val)
+{
+ if (!wpa_s->driver->ignore_assoc_disallow)
+ return -1;
+ return wpa_s->driver->ignore_assoc_disallow(wpa_s->drv_priv, val);
+}
+
+static inline int wpa_drv_set_bssid_blacklist(struct wpa_supplicant *wpa_s,
+ unsigned int num_bssid,
+ const u8 *bssids)
+{
+ if (!wpa_s->driver->set_bssid_blacklist)
+ return -1;
+ return wpa_s->driver->set_bssid_blacklist(wpa_s->drv_priv, num_bssid,
+ bssids);
+}
+
+static inline int wpa_drv_update_connect_params(
+ struct wpa_supplicant *wpa_s,
+ struct wpa_driver_associate_params *params,
+ enum wpa_drv_update_connect_params_mask mask)
+{
+ if (!wpa_s->driver->update_connect_params)
+ return -1;
+ return wpa_s->driver->update_connect_params(wpa_s->drv_priv, params,
+ mask);
+}
+
+static inline int
+wpa_drv_send_external_auth_status(struct wpa_supplicant *wpa_s,
+ struct external_auth *params)
+{
+ if (!wpa_s->driver->send_external_auth_status)
+ return -1;
+ return wpa_s->driver->send_external_auth_status(wpa_s->drv_priv,
+ params);
+}
+
#endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index abe3b476773d..37d429d33022 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -28,6 +28,7 @@
#include "notify.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/gas_server.h"
#include "crypto/random.h"
#include "blacklist.h"
#include "wpas_glue.h"
@@ -46,6 +47,10 @@
#include "mesh.h"
#include "mesh_mpm.h"
#include "wmm_ac.h"
+#include "dpp_supplicant.h"
+
+
+#define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5
#ifndef CONFIG_NO_SCAN_PROCESSING
@@ -54,8 +59,7 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_NO_SCAN_PROCESSING */
-static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid)
+int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
struct os_reltime now;
@@ -302,7 +306,9 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
- if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_DPP)
eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
wpa_s->ap_ies_from_associnfo = 0;
wpa_s->current_ssid = NULL;
@@ -311,6 +317,13 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
wpas_rrm_reset(wpa_s);
wpa_s->wnmsleep_used = 0;
+ wnm_clear_coloc_intf_reporting(wpa_s);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ wpa_s->last_tk_alg = WPA_ALG_NONE;
+ os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk));
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_s->ieee80211ac = 0;
}
@@ -327,7 +340,7 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
for (i = 0; i < ie.num_pmkid; i++) {
pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
ie.pmkid + i * PMKID_LEN,
- NULL, NULL, 0);
+ NULL, NULL, 0, NULL, 0);
if (pmksa_set == 0) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
break;
@@ -479,6 +492,11 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
return 1;
#endif /* CONFIG_WPS */
+#ifdef CONFIG_OWE
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only)
+ return 1;
+#endif /* CONFIG_OWE */
+
if (has_wep_key(ssid))
privacy = 1;
@@ -503,7 +521,7 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- struct wpa_bss *bss)
+ struct wpa_bss *bss, int debug_print)
{
struct wpa_ie_data ie;
int proto_match = 0;
@@ -522,44 +540,59 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
- while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
+ while ((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) && rsn_ie) {
proto_match++;
if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse "
- "failed");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - parse failed");
break;
}
if (wep_ok &&
(ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
{
- wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN "
- "in RSN IE");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " selected based on TSN in RSN IE");
return 1;
}
- if (!(ie.proto & ssid->proto)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto "
- "mismatch");
+ if (!(ie.proto & ssid->proto) &&
+ !(ssid->proto & WPA_PROTO_OSEN)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - proto mismatch");
break;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK "
- "cipher mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - PTK cipher mismatch");
break;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK "
- "cipher mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - GTK cipher mismatch");
+ break;
+ }
+
+ if (ssid->group_mgmt_cipher &&
+ !(ie.mgmt_group_cipher & ssid->group_mgmt_cipher)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - group mgmt cipher mismatch");
break;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt "
- "mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - key mgmt mismatch");
break;
}
@@ -567,16 +600,18 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
wpas_get_ssid_pmf(wpa_s, ssid) ==
MGMT_FRAME_PROTECTION_REQUIRED) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt "
- "frame protection");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - no mgmt frame protection");
break;
}
#endif /* CONFIG_IEEE80211W */
if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
wpas_get_ssid_pmf(wpa_s, ssid) ==
NO_MGMT_FRAME_PROTECTION) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip RSN IE - no mgmt frame protection enabled but AP requires it");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - no mgmt frame protection enabled but AP requires it");
break;
}
#ifdef CONFIG_MBO
@@ -584,20 +619,25 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND) &&
wpas_get_ssid_pmf(wpa_s, ssid) !=
NO_MGMT_FRAME_PROTECTION) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip RSN IE - no mgmt frame protection enabled on MBO AP");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - no mgmt frame protection enabled on MBO AP");
break;
}
#endif /* CONFIG_MBO */
- wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " selected based on RSN IE");
return 1;
}
#ifdef CONFIG_IEEE80211W
- if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - MFP Required but network not MFP Capable");
+ if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED &&
+ (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE) || ssid->owe_only)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - MFP Required but network not MFP Capable");
return 0;
}
#endif /* CONFIG_IEEE80211W */
@@ -607,72 +647,110 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
proto_match++;
if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse "
- "failed");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip WPA IE - parse failed");
break;
}
if (wep_ok &&
(ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
{
- wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN "
- "in WPA IE");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " selected based on TSN in WPA IE");
return 1;
}
if (!(ie.proto & ssid->proto)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto "
- "mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip WPA IE - proto mismatch");
break;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK "
- "cipher mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip WPA IE - PTK cipher mismatch");
break;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK "
- "cipher mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip WPA IE - GTK cipher mismatch");
break;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt "
- "mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip WPA IE - key mgmt mismatch");
break;
}
- wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " selected based on WPA IE");
return 1;
}
if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie &&
!rsn_ie) {
- wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " allow for non-WPA IEEE 802.1X");
return 1;
}
+#ifdef CONFIG_OWE
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only &&
+ !wpa_ie && !rsn_ie) {
+ if (wpa_s->owe_transition_select &&
+ wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE) &&
+ ssid->owe_transition_bss_select_count + 1 <=
+ MAX_OWE_TRANSITION_BSS_SELECT_COUNT) {
+ ssid->owe_transition_bss_select_count++;
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip OWE transition BSS (selection count %d does not exceed %d)",
+ ssid->owe_transition_bss_select_count,
+ MAX_OWE_TRANSITION_BSS_SELECT_COUNT);
+ wpa_s->owe_transition_search = 1;
+ return 0;
+ }
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " allow in OWE transition mode");
+ return 1;
+ }
+#endif /* CONFIG_OWE */
+
if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no WPA/RSN proto match");
return 0;
}
if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN");
return 1;
}
if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2");
return 1;
}
- wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with "
- "WPA/WPA2");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " reject due to mismatch with WPA/WPA2");
return 0;
}
@@ -692,7 +770,8 @@ static int freq_allowed(int *freqs, int freq)
}
-static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ int debug_print)
{
const struct hostapd_hw_modes *mode = NULL, *modes;
const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
@@ -749,9 +828,9 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
if (flagged && ((rate_ie[j] & 0x7f) ==
BSS_MEMBERSHIP_SELECTOR_HT_PHY)) {
if (!ht_supported(mode)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " hardware does not support "
- "HT PHY");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support HT PHY");
return 0;
}
continue;
@@ -761,9 +840,9 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
if (flagged && ((rate_ie[j] & 0x7f) ==
BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) {
if (!vht_supported(mode)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " hardware does not support "
- "VHT PHY");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support VHT PHY");
return 0;
}
continue;
@@ -783,10 +862,11 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
* order to join a BSS all required rates
* have to be supported by the hardware.
*/
- wpa_dbg(wpa_s, MSG_DEBUG,
- " hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
- r / 10, r % 10,
- bss->freq, mode->mode, mode->num_rates);
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
+ r / 10, r % 10,
+ bss->freq, mode->mode, mode->num_rates);
return 0;
}
}
@@ -839,39 +919,124 @@ static int addr_in_list(const u8 *addr, const u8 *list, size_t num)
}
+static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ const u8 **ret_ssid, size_t *ret_ssid_len)
+{
+#ifdef CONFIG_OWE
+ const u8 *owe, *pos, *end, *bssid;
+ u8 ssid_len;
+ struct wpa_bss *open_bss;
+
+ owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+ if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN))
+ return;
+
+ pos = owe + 6;
+ end = owe + 2 + owe[1];
+
+ if (end - pos < ETH_ALEN + 1)
+ return;
+ bssid = pos;
+ pos += ETH_ALEN;
+ ssid_len = *pos++;
+ if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+ return;
+
+ /* Match the profile SSID against the OWE transition mode SSID on the
+ * open network. */
+ wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID: " MACSTR
+ " SSID: %s", MAC2STR(bssid), wpa_ssid_txt(pos, ssid_len));
+ *ret_ssid = pos;
+ *ret_ssid_len = ssid_len;
+
+ if (bss->ssid_len > 0)
+ return;
+
+ open_bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+ if (!open_bss)
+ return;
+ if (ssid_len != open_bss->ssid_len ||
+ os_memcmp(pos, open_bss->ssid, ssid_len) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "OWE: transition mode SSID mismatch: %s",
+ wpa_ssid_txt(open_bss->ssid, open_bss->ssid_len));
+ return;
+ }
+
+ owe = wpa_bss_get_vendor_ie(open_bss, OWE_IE_VENDOR_TYPE);
+ if (!owe || wpa_bss_get_ie(open_bss, WLAN_EID_RSN)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "OWE: transition mode open BSS unexpected info");
+ return;
+ }
+
+ pos = owe + 6;
+ end = owe + 2 + owe[1];
+
+ if (end - pos < ETH_ALEN + 1)
+ return;
+ if (os_memcmp(pos, bss->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "OWE: transition mode BSSID mismatch: " MACSTR,
+ MAC2STR(pos));
+ return;
+ }
+ pos += ETH_ALEN;
+ ssid_len = *pos++;
+ if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+ return;
+ wpa_dbg(wpa_s, MSG_DEBUG, "OWE: learned transition mode OWE SSID: %s",
+ wpa_ssid_txt(pos, ssid_len));
+ os_memcpy(bss->ssid, pos, ssid_len);
+ bss->ssid_len = ssid_len;
+#endif /* CONFIG_OWE */
+}
+
+
struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
struct wpa_ssid *group,
- int only_first_ssid)
+ int only_first_ssid, int debug_print)
{
u8 wpa_ie_len, rsn_ie_len;
int wpa;
struct wpa_blacklist *e;
const u8 *ie;
struct wpa_ssid *ssid;
- int osen;
+ int osen, rsn_osen = 0;
#ifdef CONFIG_MBO
const u8 *assoc_disallow;
#endif /* CONFIG_MBO */
+ const u8 *match_ssid;
+ size_t match_ssid_len;
+ struct wpa_ie_data data;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
wpa_ie_len = ie ? ie[1] : 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
rsn_ie_len = ie ? ie[1] : 0;
+ if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 &&
+ (data.key_mgmt & WPA_KEY_MGMT_OSEN))
+ rsn_osen = 1;
ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
osen = ie != NULL;
- wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
- "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
- i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
- wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq,
- wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
- (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
- wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
- " p2p" : "",
- osen ? " osen=1" : "");
+ if (debug_print) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR
+ " ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
+ i, MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len),
+ wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
+ bss->freq,
+ wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ?
+ " wps" : "",
+ (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+ wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE))
+ ? " p2p" : "",
+ osen ? " osen=1" : "");
+ }
e = wpa_blacklist_get(wpa_s, bss->bssid);
if (e) {
@@ -888,24 +1053,34 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
limit = 0;
}
if (e->count > limit) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted "
- "(count=%d limit=%d)", e->count, limit);
+ if (debug_print) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - blacklisted (count=%d limit=%d)",
+ e->count, limit);
+ }
return NULL;
}
}
- if (bss->ssid_len == 0) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known");
+ match_ssid = bss->ssid;
+ match_ssid_len = bss->ssid_len;
+ owe_trans_ssid(wpa_s, bss, &match_ssid, &match_ssid_len);
+
+ if (match_ssid_len == 0) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known");
return NULL;
}
if (disallowed_bssid(wpa_s, bss->bssid)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed");
return NULL;
}
- if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed");
+ if (disallowed_ssid(wpa_s, match_ssid, match_ssid_len)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed");
return NULL;
}
@@ -916,21 +1091,25 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int res;
if (wpas_network_disabled(wpa_s, ssid)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled");
continue;
}
res = wpas_temp_disabled(wpa_s, ssid);
if (res > 0) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled "
- "temporarily for %d second(s)", res);
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - disabled temporarily for %d second(s)",
+ res);
continue;
}
#ifdef CONFIG_WPS
if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted "
- "(WPS)");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - blacklisted (WPS)");
continue;
}
@@ -954,15 +1133,19 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
check_ssid = 0;
if (check_ssid &&
- (bss->ssid_len != ssid->ssid_len ||
- os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch");
+ (match_ssid_len != ssid->ssid_len ||
+ os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - SSID mismatch");
continue;
}
if (ssid->bssid_set &&
os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - BSSID mismatch");
continue;
}
@@ -970,8 +1153,9 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
if (ssid->num_bssid_blacklist &&
addr_in_list(bss->bssid, ssid->bssid_blacklist,
ssid->num_bssid_blacklist)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - BSSID blacklisted");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - BSSID blacklisted");
continue;
}
@@ -979,79 +1163,108 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
if (ssid->num_bssid_whitelist &&
!addr_in_list(bss->bssid, ssid->bssid_whitelist,
ssid->num_bssid_whitelist)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - BSSID not in whitelist");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - BSSID not in whitelist");
continue;
}
- if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+ if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss,
+ debug_print))
continue;
if (!osen && !wpa &&
!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network "
- "not allowed");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - non-WPA network not allowed");
continue;
}
if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
has_wep_key(ssid)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - ignore WPA/WPA2 AP for WEP network block");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - ignore WPA/WPA2 AP for WEP network block");
continue;
}
- if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-OSEN network "
- "not allowed");
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen &&
+ !rsn_osen) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - non-OSEN network not allowed");
continue;
}
if (!wpa_supplicant_match_privacy(bss, ssid)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy "
- "mismatch");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - privacy mismatch");
continue;
}
if (ssid->mode != IEEE80211_MODE_MESH && !bss_is_ess(bss) &&
!bss_is_pbss(bss)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - not ESS, PBSS, or MBSS");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - not ESS, PBSS, or MBSS");
continue;
}
if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - PBSS mismatch (ssid %d bss %d)",
- ssid->pbss, bss_is_pbss(bss));
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - PBSS mismatch (ssid %d bss %d)",
+ ssid->pbss, bss_is_pbss(bss));
continue;
}
if (!freq_allowed(ssid->freq_list, bss->freq)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not "
- "allowed");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - frequency not allowed");
continue;
}
#ifdef CONFIG_MESH
if (ssid->mode == IEEE80211_MODE_MESH && ssid->frequency > 0 &&
ssid->frequency != bss->freq) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not allowed (mesh)");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - frequency not allowed (mesh)");
continue;
}
#endif /* CONFIG_MESH */
- if (!rate_match(wpa_s, bss)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do "
- "not match");
+ if (!rate_match(wpa_s, bss, debug_print)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - rate sets do not match");
continue;
}
+#ifndef CONFIG_IBSS_RSN
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
+ WPA_KEY_MGMT_WPA_NONE))) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - IBSS RSN not supported in the build");
+ continue;
+ }
+#endif /* !CONFIG_IBSS_RSN */
+
#ifdef CONFIG_P2P
if (ssid->p2p_group &&
!wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
!wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no P2P IE seen");
continue;
}
@@ -1061,20 +1274,26 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
if (ie == NULL) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P element");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no P2P element");
continue;
}
p2p_ie = wpa_bss_get_vendor_ie_multi(
bss, P2P_IE_VENDOR_TYPE);
if (p2p_ie == NULL) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - could not fetch P2P element");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - could not fetch P2P element");
continue;
}
if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0
|| os_memcmp(dev_addr, ssid->go_p2p_dev_addr,
ETH_ALEN) != 0) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - no matching GO P2P Device Address in P2P element");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no matching GO P2P Device Address in P2P element");
wpabuf_free(p2p_ie);
continue;
}
@@ -1094,8 +1313,9 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
os_reltime_sub(&wpa_s->scan_min_time,
&bss->last_update, &diff);
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - scan result not recent enough (%u.%06u seconds too old)",
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - scan result not recent enough (%u.%06u seconds too old)",
(unsigned int) diff.sec,
(unsigned int) diff.usec);
continue;
@@ -1108,15 +1328,17 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
assoc_disallow = wpas_mbo_get_bss_attr(
bss, MBO_ATTR_ID_ASSOC_DISALLOW);
if (assoc_disallow && assoc_disallow[1] >= 1) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - MBO association disallowed (reason %u)",
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - MBO association disallowed (reason %u)",
assoc_disallow[2]);
continue;
}
if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - MBO retry delay has not passed yet");
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - MBO retry delay has not passed yet");
continue;
}
#ifdef CONFIG_TESTING_OPTIONS
@@ -1124,6 +1346,19 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_MBO */
+#ifdef CONFIG_DPP
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
+ !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
+ (!ssid->dpp_connector ||
+ !ssid->dpp_netaccesskey ||
+ !ssid->dpp_csign)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no PMKSA entry for DPP");
+ continue;
+ }
+#endif /* CONFIG_DPP */
+
/* Matching configuration found */
return ssid;
}
@@ -1141,6 +1376,25 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
{
unsigned int i;
+ if (wpa_s->current_ssid) {
+ struct wpa_ssid *ssid;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Scan results matching the currently selected network");
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ struct wpa_bss *bss = wpa_s->last_scan_res[i];
+
+ ssid = wpa_scan_res_match(wpa_s, i, bss, group,
+ only_first_ssid, 0);
+ if (ssid != wpa_s->current_ssid)
+ continue;
+ wpa_dbg(wpa_s, MSG_DEBUG, "%u: " MACSTR
+ " freq=%d level=%d snr=%d est_throughput=%u",
+ i, MAC2STR(bss->bssid), bss->freq, bss->level,
+ bss->snr, bss->est_throughput);
+ }
+ }
+
if (only_first_ssid)
wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
group->id);
@@ -1150,8 +1404,11 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
struct wpa_bss *bss = wpa_s->last_scan_res[i];
+
+ wpa_s->owe_transition_select = 1;
*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
- only_first_ssid);
+ only_first_ssid, 1);
+ wpa_s->owe_transition_select = 0;
if (!*selected_ssid)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR
@@ -1332,6 +1589,17 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
{
if (wpas_network_disabled(wpa_s, ssid))
continue;
+#ifndef CONFIG_IBSS_RSN
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
+ WPA_KEY_MGMT_WPA_NONE))) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "IBSS RSN not supported in the build - cannot use the profile for SSID '%s'",
+ wpa_ssid_txt(ssid->ssid,
+ ssid->ssid_len));
+ continue;
+ }
+#endif /* !CONFIG_IBSS_RSN */
if (ssid->mode == IEEE80211_MODE_IBSS ||
ssid->mode == IEEE80211_MODE_AP ||
ssid->mode == IEEE80211_MODE_MESH)
@@ -1375,8 +1643,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
{
struct wpa_bss *current_bss = NULL;
#ifndef CONFIG_NO_ROAMING
- int min_diff;
+ int min_diff, diff;
int to_5ghz;
+ int cur_est, sel_est;
#endif /* CONFIG_NO_ROAMING */
if (wpa_s->reassociate)
@@ -1410,12 +1679,13 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
#ifndef CONFIG_NO_ROAMING
wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
- " level=%d snr=%d est_throughput=%u",
- MAC2STR(current_bss->bssid), current_bss->level,
+ " freq=%d level=%d snr=%d est_throughput=%u",
+ MAC2STR(current_bss->bssid),
+ current_bss->freq, current_bss->level,
current_bss->snr, current_bss->est_throughput);
wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR
- " level=%d snr=%d est_throughput=%u",
- MAC2STR(selected->bssid), selected->level,
+ " freq=%d level=%d snr=%d est_throughput=%u",
+ MAC2STR(selected->bssid), selected->freq, selected->level,
selected->snr, selected->est_throughput);
if (wpa_s->current_ssid->bssid_set &&
@@ -1441,6 +1711,14 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
return 0;
}
+ if (current_bss->est_throughput > selected->est_throughput + 5000) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Skip roam - Current BSS has better estimated throughput");
+ return 0;
+ }
+
+ cur_est = current_bss->est_throughput;
+ sel_est = selected->est_throughput;
min_diff = 2;
if (current_bss->level < 0) {
if (current_bss->level < -85)
@@ -1453,20 +1731,42 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
min_diff = 4;
else
min_diff = 5;
+ if (cur_est > sel_est * 1.5)
+ min_diff += 10;
+ else if (cur_est > sel_est * 1.2)
+ min_diff += 5;
+ else if (cur_est > sel_est * 1.1)
+ min_diff += 2;
+ else if (cur_est > sel_est)
+ min_diff++;
}
if (to_5ghz) {
+ int reduce = 2;
+
/* Make it easier to move to 5 GHz band */
- if (min_diff > 2)
- min_diff -= 2;
+ if (sel_est > cur_est * 1.5)
+ reduce = 5;
+ else if (sel_est > cur_est * 1.2)
+ reduce = 4;
+ else if (sel_est > cur_est * 1.1)
+ reduce = 3;
+
+ if (min_diff > reduce)
+ min_diff -= reduce;
else
min_diff = 0;
}
- if (abs(current_bss->level - selected->level) < min_diff) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
- "in signal level");
+ diff = abs(current_bss->level - selected->level);
+ if (diff < min_diff) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Skip roam - too small difference in signal level (%d < %d)",
+ diff, min_diff);
return 0;
}
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Allow reassociation due to difference in signal level (%d >= %d)",
+ diff, min_diff);
return 1;
#else /* CONFIG_NO_ROAMING */
return 0;
@@ -1474,11 +1774,18 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
}
-/* Return != 0 if no scan results could be fetched or if scan results should not
- * be shared with other virtual interfaces. */
+/*
+ * Return a negative value if no scan results could be fetched or if scan
+ * results should not be shared with other virtual interfaces.
+ * Return 0 if scan results were fetched and may be shared with other
+ * interfaces.
+ * Return 1 if scan results may be shared with other virtual interfaces but may
+ * not trigger any operations.
+ * Return 2 if the interface was removed and cannot be used.
+ */
static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data,
- int own_request)
+ int own_request, int update_only)
{
struct wpa_scan_results *scan_res = NULL;
int ret = 0;
@@ -1528,6 +1835,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_NO_RANDOM_POOL */
+ if (update_only) {
+ ret = 1;
+ goto scan_work_done;
+ }
+
if (own_request && wpa_s->scan_res_handler &&
!(data && data->scan_info.external_scan)) {
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
@@ -1536,7 +1848,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
scan_res_handler = wpa_s->scan_res_handler;
wpa_s->scan_res_handler = NULL;
scan_res_handler(wpa_s, scan_res);
- ret = -2;
+ ret = 1;
goto scan_work_done;
}
@@ -1577,6 +1889,10 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
if (sme_proc_obss_scan(wpa_s) > 0)
goto scan_work_done;
+ if (own_request &&
+ wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0)
+ goto scan_work_done;
+
if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
goto scan_work_done;
@@ -1639,6 +1955,7 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
if (wpa_s->p2p_mgmt)
return 0; /* no normal connection on p2p_mgmt interface */
+ wpa_s->owe_transition_search = 0;
selected = wpa_supplicant_pick_network(wpa_s, &ssid);
#ifdef CONFIG_MESH
@@ -1672,8 +1989,9 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
/*
- * Do not notify other virtual radios of scan results since we do not
- * want them to start other associations at the same time.
+ * Do not allow other virtual radios to trigger operations based
+ * on these scan results since we do not want them to start
+ * other associations at the same time.
*/
return 1;
} else {
@@ -1739,6 +2057,17 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
return 0;
}
#endif /* CONFIG_WPS */
+#ifdef CONFIG_OWE
+ if (wpa_s->owe_transition_search) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "OWE: Use shorter wait during transition mode search");
+ timeout_sec = 0;
+ timeout_usec = 500000;
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+ return 0;
+ }
+#endif /* CONFIG_OWE */
if (wpa_supplicant_req_sched_scan(wpa_s))
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
@@ -1757,7 +2086,7 @@ static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
struct wpa_supplicant *ifs;
int res;
- res = _wpa_supplicant_event_scan_results(wpa_s, data, 1);
+ res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0);
if (res == 2) {
/*
* Interface may have been removed, so must not dereference
@@ -1765,7 +2094,8 @@ static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
*/
return 1;
}
- if (res != 0) {
+
+ if (res < 0) {
/*
* If no scan results could be fetched, then no need to
* notify those interfaces that did not actually request
@@ -1785,7 +2115,10 @@ static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
if (ifs != wpa_s) {
wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
"sibling", ifs->ifname);
- _wpa_supplicant_event_scan_results(ifs, data, 0);
+ res = _wpa_supplicant_event_scan_results(ifs, data, 0,
+ res > 0);
+ if (res < 0)
+ return 0;
}
}
@@ -1802,6 +2135,8 @@ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
#else /* CONFIG_NO_SCAN_PROCESSING */
struct os_reltime now;
+ wpa_s->ignore_post_flush_scan_res = 0;
+
if (wpa_s->last_scan_res_used == 0)
return -1;
@@ -1981,9 +2316,9 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
{
int l, len, found = 0, wpa_found, rsn_found;
const u8 *p;
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_OWE)
u8 bssid[ETH_ALEN];
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_OWE */
wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
if (data->assoc_info.req_ies)
@@ -2004,6 +2339,10 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_INTERWORKING */
+ if (wpa_s->hw_capab == CAPAB_VHT &&
+ get_ie(data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
+ wpa_s->ieee80211ac = 1;
}
if (data->assoc_info.beacon_ies)
wpa_hexdump(MSG_DEBUG, "beacon_ies",
@@ -2041,6 +2380,36 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
if (!found && data->assoc_info.req_ies)
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+#ifdef CONFIG_FILS
+#ifdef CONFIG_SME
+ if ((wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
+ wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) &&
+ (!data->assoc_info.resp_frame ||
+ fils_process_assoc_resp(wpa_s->wpa,
+ data->assoc_info.resp_frame,
+ data->assoc_info.resp_frame_len) < 0)) {
+ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
+ return -1;
+ }
+#endif /* CONFIG_SME */
+
+ /* Additional processing for FILS when SME is in driver */
+ if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+ wpa_sm_set_reset_fils_completed(wpa_s->wpa, 1);
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
+ (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+ owe_process_assoc_resp(wpa_s->wpa, bssid,
+ data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len) < 0)) {
+ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
+ return -1;
+ }
+#endif /* CONFIG_OWE */
+
#ifdef CONFIG_IEEE80211R
#ifdef CONFIG_SME
if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
@@ -2262,6 +2631,13 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
ft_completed = wpa_ft_is_completed(wpa_s->wpa);
if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
return;
+ /*
+ * FILS authentication can share the same mechanism to mark the
+ * connection fully authenticated, so set ft_completed also based on
+ * FILS result.
+ */
+ if (!ft_completed)
+ ft_completed = wpa_fils_is_completed(wpa_s->wpa);
if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
@@ -2331,7 +2707,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
}
- if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed ||
+ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || ft_completed ||
already_authorized)
eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
/* 802.1X::portControl = Auto */
@@ -2395,7 +2773,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
struct os_reltime now, age;
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
- if (age.sec == 0 && age.usec < 100000 &&
+ if (age.sec == 0 && age.usec < 200000 &&
os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
@@ -2446,6 +2824,16 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
if (wpa_s->reassoc_same_bss)
wmm_ac_restore_tspecs(wpa_s);
}
+
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) {
+ struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, bssid);
+ const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
+
+ if (fils_cache_id)
+ wpa_sm_set_fils_cache_id(wpa_s->wpa, fils_cache_id);
+ }
+#endif /* CONFIG_FILS */
}
@@ -2837,18 +3225,6 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
}
-#ifdef CONFIG_PEERKEY
-static void
-wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data)
-{
- if (data == NULL)
- return;
- wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer);
-}
-#endif /* CONFIG_PEERKEY */
-
-
#ifdef CONFIG_TDLS
static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
@@ -3211,6 +3587,7 @@ static void wpa_supplicant_update_channel_list(
struct wpa_supplicant *wpa_s, struct channel_list_changed *info)
{
struct wpa_supplicant *ifs;
+ u8 dfs_domain;
/*
* To allow backwards compatibility with higher level layers that
@@ -3235,7 +3612,7 @@ static void wpa_supplicant_update_channel_list(
ifs->ifname);
free_hw_features(ifs);
ifs->hw.modes = wpa_drv_get_hw_feature_data(
- ifs, &ifs->hw.num_modes, &ifs->hw.flags);
+ ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain);
/* Restart PNO/sched_scan with updated channel list */
if (ifs->pno) {
@@ -3310,6 +3687,15 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
return;
#endif /* CONFIG_GAS */
+#ifdef CONFIG_GAS_SERVER
+ if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
+ mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
+ gas_server_rx(wpa_s->gas_server, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ payload, plen, freq) == 0)
+ return;
+#endif /* CONFIG_GAS_SERVER */
+
#ifdef CONFIG_TDLS
if (category == WLAN_ACTION_PUBLIC && plen >= 4 &&
payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
@@ -3338,6 +3724,7 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa,
+ mgmt->da,
payload + 1,
plen - 1);
return;
@@ -3364,6 +3751,18 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_FST */
+#ifdef CONFIG_DPP
+ if (category == WLAN_ACTION_PUBLIC && plen >= 5 &&
+ payload[0] == WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(&payload[1]) == OUI_WFA &&
+ payload[4] == DPP_OUI_TYPE) {
+ payload++;
+ plen--;
+ wpas_dpp_rx_action(wpa_s, mgmt->sa, payload, plen, freq);
+ return;
+ }
+#endif /* CONFIG_DPP */
+
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
category, payload, plen, freq);
if (wpa_s->ifmsh)
@@ -3404,23 +3803,226 @@ static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
}
-static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data)
+static void wpa_supplicant_event_port_authorized(struct wpa_supplicant *wpa_s)
{
- wpa_dbg(wpa_s, MSG_DEBUG,
- "Connection authorized by device, previous state %d",
- wpa_s->wpa_state);
if (wpa_s->wpa_state == WPA_ASSOCIATED) {
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
}
+}
+
+
+static unsigned int wpas_event_cac_ms(const struct wpa_supplicant *wpa_s,
+ int freq)
+{
+ size_t i;
+ int j;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ const struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
+
+ for (j = 0; j < mode->num_channels; j++) {
+ const struct hostapd_channel_data *chan;
+
+ chan = &mode->channels[j];
+ if (chan->freq == freq)
+ return chan->dfs_cac_ms;
+ }
+ }
+
+ return 0;
+}
+
+
+static void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar)
+{
+#if defined(NEED_AP_MLME) && defined(CONFIG_AP)
+ if (wpa_s->ap_iface) {
+ wpas_ap_event_dfs_cac_started(wpa_s, radar);
+ } else
+#endif /* NEED_AP_MLME && CONFIG_AP */
+ {
+ unsigned int cac_time = wpas_event_cac_ms(wpa_s, radar->freq);
+
+ cac_time /= 1000; /* convert from ms to sec */
+ if (!cac_time)
+ cac_time = 10 * 60; /* max timeout: 10 minutes */
+
+ /* Restart auth timeout: CAC time added to initial timeout */
+ wpas_auth_timeout_restart(wpa_s, cac_time);
+ }
+}
+
+
+static void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar)
+{
+#if defined(NEED_AP_MLME) && defined(CONFIG_AP)
+ if (wpa_s->ap_iface) {
+ wpas_ap_event_dfs_cac_finished(wpa_s, radar);
+ } else
+#endif /* NEED_AP_MLME && CONFIG_AP */
+ {
+ /* Restart auth timeout with original value after CAC is
+ * finished */
+ wpas_auth_timeout_restart(wpa_s, 0);
+ }
+}
+
+
+static void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+ struct dfs_event *radar)
+{
+#if defined(NEED_AP_MLME) && defined(CONFIG_AP)
+ if (wpa_s->ap_iface) {
+ wpas_ap_event_dfs_cac_aborted(wpa_s, radar);
+ } else
+#endif /* NEED_AP_MLME && CONFIG_AP */
+ {
+ /* Restart auth timeout with original value after CAC is
+ * aborted */
+ wpas_auth_timeout_restart(wpa_s, 0);
+ }
+}
+
+
+static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Connection authorized by device, previous state %d",
+ wpa_s->wpa_state);
+
+ wpa_supplicant_event_port_authorized(wpa_s);
+
wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr);
wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck,
data->assoc_info.ptk_kck_len,
data->assoc_info.ptk_kek,
data->assoc_info.ptk_kek_len);
+#ifdef CONFIG_FILS
+ if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) {
+ struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
+ const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
+
+ /* Update ERP next sequence number */
+ eapol_sm_update_erp_next_seq_num(
+ wpa_s->eapol, data->assoc_info.fils_erp_next_seq_num);
+
+ if (data->assoc_info.fils_pmk && data->assoc_info.fils_pmkid) {
+ /* Add the new PMK and PMKID to the PMKSA cache */
+ wpa_sm_pmksa_cache_add(wpa_s->wpa,
+ data->assoc_info.fils_pmk,
+ data->assoc_info.fils_pmk_len,
+ data->assoc_info.fils_pmkid,
+ wpa_s->bssid, fils_cache_id);
+ } else if (data->assoc_info.fils_pmkid) {
+ /* Update the current PMKSA used for this connection */
+ pmksa_cache_set_current(wpa_s->wpa,
+ data->assoc_info.fils_pmkid,
+ NULL, NULL, 0, NULL, 0);
+ }
+ }
+#endif /* CONFIG_FILS */
+}
+
+
+static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ const u8 *bssid = data->assoc_reject.bssid;
+
+ if (!bssid || is_zero_ether_addr(bssid))
+ bssid = wpa_s->pending_bssid;
+
+ if (data->assoc_reject.bssid)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+ "bssid=" MACSTR " status_code=%u%s%s%s",
+ MAC2STR(data->assoc_reject.bssid),
+ data->assoc_reject.status_code,
+ data->assoc_reject.timed_out ? " timeout" : "",
+ data->assoc_reject.timeout_reason ? "=" : "",
+ data->assoc_reject.timeout_reason ?
+ data->assoc_reject.timeout_reason : "");
+ else
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+ "status_code=%u%s%s%s",
+ data->assoc_reject.status_code,
+ data->assoc_reject.timed_out ? " timeout" : "",
+ data->assoc_reject.timeout_reason ? "=" : "",
+ data->assoc_reject.timeout_reason ?
+ data->assoc_reject.timeout_reason : "");
+ wpa_s->assoc_status_code = data->assoc_reject.status_code;
+ wpas_notify_assoc_status_code(wpa_s);
+
+#ifdef CONFIG_OWE
+ if (data->assoc_reject.status_code ==
+ WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+ wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
+ wpa_s->current_ssid &&
+ wpa_s->current_ssid->owe_group == 0 &&
+ wpa_s->last_owe_group != 21) {
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ struct wpa_bss *bss = wpa_s->current_bss;
+
+ if (!bss) {
+ bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+ if (!bss) {
+ wpas_connection_failed(wpa_s, bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ return;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "OWE: Try next supported DH group");
+ wpas_connect_work_done(wpa_s);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_connect(wpa_s, bss, ssid);
+ return;
+ }
+#endif /* CONFIG_OWE */
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
+ sme_event_assoc_reject(wpa_s, data);
+ return;
+ }
+
+ /* Driver-based SME cases */
+
+#ifdef CONFIG_SAE
+ if (wpa_s->current_ssid &&
+ wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt) &&
+ !data->assoc_reject.timed_out) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SAE: Drop PMKSA cache entry");
+ wpa_sm_aborted_cached(wpa_s->wpa);
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_DPP
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP &&
+ !data->assoc_reject.timed_out) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "DPP: Drop PMKSA cache entry");
+ wpa_sm_aborted_cached(wpa_s->wpa);
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
+ }
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_FILS
+ /* Update ERP next sequence number */
+ if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) {
+ eapol_sm_update_erp_next_seq_num(
+ wpa_s->eapol,
+ data->assoc_reject.fils_erp_next_seq_num);
+ fils_connection_failure(wpa_s);
+ }
+#endif /* CONFIG_FILS */
+
+ wpas_connection_failed(wpa_s, bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
}
@@ -3429,6 +4031,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
{
struct wpa_supplicant *wpa_s = ctx;
int resched;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ int level = MSG_DEBUG;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
event != EVENT_INTERFACE_ENABLED &&
@@ -3442,9 +4047,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
}
#ifndef CONFIG_NO_STDOUT_DEBUG
-{
- int level = MSG_DEBUG;
-
if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) {
const struct ieee80211_hdr *hdr;
u16 fc;
@@ -3457,7 +4059,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpa_dbg(wpa_s, level, "Event %s (%d) received",
event_to_string(event), event);
-}
#endif /* CONFIG_NO_STDOUT_DEBUG */
switch (event) {
@@ -3477,9 +4078,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
"EVENT_ASSOC - ignore_auth_resp active!");
break;
}
+ if (wpa_s->testing_resend_assoc) {
+ wpa_printf(MSG_INFO,
+ "EVENT_DEAUTH - testing_resend_assoc");
+ break;
+ }
#endif /* CONFIG_TESTING_OPTIONS */
wpa_supplicant_event_assoc(wpa_s, data);
- if (data && data->assoc_info.authorized)
+ wpa_s->assoc_status_code = WLAN_STATUS_SUCCESS;
+ if (data &&
+ (data->assoc_info.authorized ||
+ (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ wpa_fils_is_completed(wpa_s->wpa))))
wpa_supplicant_event_assoc_auth(wpa_s, data);
if (data) {
wpa_msg(wpa_s, MSG_INFO,
@@ -3498,6 +4108,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
"EVENT_DEAUTH - ignore_auth_resp active!");
break;
}
+ if (wpa_s->testing_resend_assoc) {
+ wpa_printf(MSG_INFO,
+ "EVENT_DEAUTH - testing_resend_assoc");
+ break;
+ }
#endif /* CONFIG_TESTING_OPTIONS */
wpas_event_deauth(wpa_s,
data ? &data->deauth_info : NULL);
@@ -3570,11 +4185,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
case EVENT_PMKID_CANDIDATE:
wpa_supplicant_event_pmkid_candidate(wpa_s, data);
break;
-#ifdef CONFIG_PEERKEY
- case EVENT_STKSTART:
- wpa_supplicant_event_stkstart(wpa_s, data);
- break;
-#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_TDLS
case EVENT_TDLS:
wpa_supplicant_event_tdls(wpa_s, data);
@@ -3596,28 +4206,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
#endif /* CONFIG_IBSS_RSN */
case EVENT_ASSOC_REJECT:
- if (data->assoc_reject.bssid)
- wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
- "bssid=" MACSTR " status_code=%u%s",
- MAC2STR(data->assoc_reject.bssid),
- data->assoc_reject.status_code,
- data->assoc_reject.timed_out ? " timeout" : "");
- else
- wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
- "status_code=%u%s",
- data->assoc_reject.status_code,
- data->assoc_reject.timed_out ? " timeout" : "");
- wpa_s->assoc_status_code = data->assoc_reject.status_code;
- wpas_notify_assoc_status_code(wpa_s);
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
- sme_event_assoc_reject(wpa_s, data);
- else {
- const u8 *bssid = data->assoc_reject.bssid;
- if (bssid == NULL || is_zero_ether_addr(bssid))
- bssid = wpa_s->pending_bssid;
- wpas_connection_failed(wpa_s, bssid);
- wpa_supplicant_mark_disassoc(wpa_s);
- }
+ wpas_event_assoc_reject(wpa_s, data);
break;
case EVENT_AUTH_TIMED_OUT:
/* It is possible to get this event from earlier connection */
@@ -3719,6 +4308,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
data->rx_from_unknown.wds);
break;
+#endif /* CONFIG_AP */
case EVENT_CH_SWITCH:
if (!data || !wpa_s->current_ssid)
break;
@@ -3735,6 +4325,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpa_s->assoc_freq = data->ch_switch.freq;
wpa_s->current_ssid->frequency = data->ch_switch.freq;
+#ifdef CONFIG_AP
if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO ||
wpa_s->current_ssid->mode ==
@@ -3746,14 +4337,25 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->ch_switch.cf1,
data->ch_switch.cf2);
}
+#endif /* CONFIG_AP */
wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
+ wnm_clear_coloc_intf_reporting(wpa_s);
break;
+#ifdef CONFIG_AP
#ifdef NEED_AP_MLME
case EVENT_DFS_RADAR_DETECTED:
if (data)
- wpas_event_dfs_radar_detected(wpa_s, &data->dfs_event);
+ wpas_ap_event_dfs_radar_detected(wpa_s,
+ &data->dfs_event);
break;
+ case EVENT_DFS_NOP_FINISHED:
+ if (data)
+ wpas_ap_event_dfs_cac_nop_finished(wpa_s,
+ &data->dfs_event);
+ break;
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_AP */
case EVENT_DFS_CAC_STARTED:
if (data)
wpas_event_dfs_cac_started(wpa_s, &data->dfs_event);
@@ -3766,13 +4368,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (data)
wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event);
break;
- case EVENT_DFS_NOP_FINISHED:
- if (data)
- wpas_event_dfs_cac_nop_finished(wpa_s,
- &data->dfs_event);
- break;
-#endif /* NEED_AP_MLME */
-#endif /* CONFIG_AP */
case EVENT_RX_MGMT: {
u16 fc, stype;
const struct ieee80211_mgmt *mgmt;
@@ -3844,6 +4439,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
}
+#ifdef CONFIG_SAE
+ if (stype == WLAN_FC_STYPE_AUTH &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
+ sme_external_auth_mgmt_rx(
+ wpa_s, data->rx_mgmt.frame,
+ data->rx_mgmt.frame_len);
+ break;
+ }
+#endif /* CONFIG_SAE */
wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
"management frame in non-AP mode");
break;
@@ -3908,6 +4513,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
#endif /* CONFIG_OFFCHANNEL */
wpas_p2p_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq);
+#ifdef CONFIG_DPP
+ wpas_dpp_cancel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_DPP */
break;
case EVENT_EAPOL_RX:
wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
@@ -3929,6 +4538,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->signal_change.current_noise,
data->signal_change.current_txrate);
break;
+ case EVENT_INTERFACE_MAC_CHANGED:
+ wpa_supplicant_update_mac_addr(wpa_s);
+ break;
case EVENT_INTERFACE_ENABLED:
wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
@@ -4129,12 +4741,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
#endif /* CONFIG_AP */
break;
case EVENT_ACS_CHANNEL_SELECTED:
+#ifdef CONFIG_AP
#ifdef CONFIG_ACS
if (!wpa_s->ap_iface)
break;
hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0],
&data->acs_selected_channels);
#endif /* CONFIG_ACS */
+#endif /* CONFIG_AP */
break;
case EVENT_P2P_LO_STOP:
#ifdef CONFIG_P2P
@@ -4144,6 +4758,36 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->p2p_lo_stop.reason_code);
#endif /* CONFIG_P2P */
break;
+ case EVENT_BEACON_LOSS:
+ if (!wpa_s->current_bss || !wpa_s->current_ssid)
+ break;
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_BEACON_LOSS);
+ bgscan_notify_beacon_loss(wpa_s);
+ break;
+ case EVENT_EXTERNAL_AUTH:
+#ifdef CONFIG_SAE
+ if (!wpa_s->current_ssid) {
+ wpa_printf(MSG_DEBUG, "SAE: current_ssid is NULL");
+ break;
+ }
+ sme_external_auth_trigger(wpa_s, data);
+#endif /* CONFIG_SAE */
+ break;
+ case EVENT_PORT_AUTHORIZED:
+ wpa_supplicant_event_port_authorized(wpa_s);
+ break;
+ case EVENT_STATION_OPMODE_CHANGED:
+#ifdef CONFIG_AP
+ if (!wpa_s->ap_iface || !data)
+ break;
+
+ hostapd_event_sta_opmode_changed(wpa_s->ap_iface->bss[0],
+ data->sta_opmode.addr,
+ data->sta_opmode.smps_mode,
+ data->sta_opmode.chan_width,
+ data->sta_opmode.rx_nss);
+#endif /* CONFIG_AP */
+ break;
default:
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
break;
diff --git a/wpa_supplicant/examples/dpp-qrcode.py b/wpa_supplicant/examples/dpp-qrcode.py
new file mode 100755
index 000000000000..e2a00c910812
--- /dev/null
+++ b/wpa_supplicant/examples/dpp-qrcode.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python
+#
+# Example Android logcat to wpa_supplicant wrapper for QR Code scans
+# Copyright (c) 2017, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import argparse
+import logging
+import qrcode
+
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
+
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+
+def wpas_connect():
+ ifaces = []
+ if os.path.isdir(wpas_ctrl):
+ try:
+ ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+ except OSError, error:
+ print "Could not find wpa_supplicant: ", error
+ return None
+
+ if len(ifaces) < 1:
+ print "No wpa_supplicant control interface found"
+ return None
+
+ for ctrl in ifaces:
+ try:
+ wpas = wpaspy.Ctrl(ctrl)
+ return wpas
+ except Exception, e:
+ pass
+ return None
+
+def dpp_logcat():
+ for line in iter(sys.stdin.readline, ''):
+ if "ResultHandler: Launching intent: Intent" not in line:
+ continue
+ if "act=android.intent.action.VIEW" not in line:
+ continue
+ uri = None
+ for val in line.split(' '):
+ if val.startswith('dat='):
+ uri = val.split('=', 1)[1]
+ break
+ if not uri:
+ continue
+ if not uri.startswith('DPP:'):
+ continue
+ print "Found DPP bootstrap info URI:"
+ print uri
+ wpas = wpas_connect()
+ if not wpas:
+ print "Could not connect to wpa_supplicant"
+ print
+ continue
+ res = wpas.request("DPP_QR_CODE " + uri);
+ try:
+ id = int(res)
+ except ValueError:
+ print "QR Code URI rejected"
+ continue
+ print "QR Code URI accepted - ID=%d" % id
+ print wpas.request("DPP_BOOTSTRAP_INFO %d" % id)
+ del wpas
+
+def dpp_display(curve):
+ wpas = wpas_connect()
+ if not wpas:
+ print "Could not connect to wpa_supplicant"
+ return
+ res = wpas.request("STATUS")
+ addr = None
+ for line in res.splitlines():
+ if line.startswith("address="):
+ addr = line.split('=')[1]
+ break
+ cmd = "DPP_BOOTSTRAP_GEN type=qrcode"
+ cmd += " chan=81/1"
+ if addr:
+ cmd += " mac=" + addr.replace(':','')
+ if curve:
+ cmd += " curve=" + curve
+ res = wpas.request(cmd)
+ try:
+ id = int(res)
+ except ValueError:
+ print "Failed to generate bootstrap info URI"
+ return
+ print "Bootstrap information - ID=%d" % id
+ print wpas.request("DPP_BOOTSTRAP_INFO %d" % id)
+ uri = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ print uri
+ print "ID=%d" % id
+ qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M,
+ border=3)
+ qr.add_data(uri, optimize=5)
+ qr.print_ascii(tty=True)
+ print "ID=%d" % id
+ del wpas
+
+def main():
+ parser = argparse.ArgumentParser(description='Android logcat to wpa_supplicant integration for DPP QR Code operations')
+ parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+ action='store_const', dest='loglevel',
+ help='verbose debug output')
+ parser.add_argument('--curve', '-c',
+ help='set a specific curve (P-256, P-384, P-521, BP-256R1, BP-384R1, BP-512R1) for key generation')
+ parser.add_argument('command', choices=['logcat',
+ 'display'],
+ nargs='?')
+ args = parser.parse_args()
+
+ logging.basicConfig(level=args.loglevel)
+
+ if args.command == "logcat":
+ dpp_logcat()
+ elif args.command == "display":
+ dpp_display(args.curve)
+
+if __name__ == '__main__':
+ main()
diff --git a/wpa_supplicant/examples/wps-ap-cli b/wpa_supplicant/examples/wps-ap-cli
index cc2cff2ebc24..15d913ef1fae 100755
--- a/wpa_supplicant/examples/wps-ap-cli
+++ b/wpa_supplicant/examples/wps-ap-cli
@@ -14,12 +14,12 @@ pbc()
enter_pin()
{
echo "Enter a PIN from a station to be enrolled to the network."
- echo -n "Enrollee PIN: "
+ printf "Enrollee PIN: "
read pin
cpin=`$CLI wps_check_pin "$pin" | tail -1`
if [ "$cpin" = "FAIL-CHECKSUM" ]; then
echo "Checksum digit is not valid"
- echo -n "Do you want to use this PIN (y/n)? "
+ printf "Do you want to use this PIN (y/n)? "
read resp
case "$resp" in
y*)
@@ -52,7 +52,7 @@ main_menu()
echo "3: Show current configuration"
echo "0: Exit wps-ap-cli"
- echo -n "Command: "
+ printf "Command: "
read cmd
case "$cmd" in
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 691de0345d13..f4f60c58bee5 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -42,6 +42,7 @@ struct gas_query_pending {
unsigned int wait_comeback:1;
unsigned int offchannel_tx_started:1;
unsigned int retry:1;
+ unsigned int wildcard_bssid:1;
int freq;
u16 status_code;
struct wpabuf *req;
@@ -53,6 +54,7 @@ struct gas_query_pending {
const struct wpabuf *adv_proto,
const struct wpabuf *resp, u16 status_code);
void *ctx;
+ u8 sa[ETH_ALEN];
};
/**
@@ -63,6 +65,9 @@ struct gas_query {
struct dl_list pending; /* struct gas_query_pending */
struct gas_query_pending *current;
struct wpa_radio_work *work;
+ struct os_reltime last_mac_addr_rand;
+ int last_rand_sa_type;
+ u8 rand_addr[ETH_ALEN];
};
@@ -117,6 +122,8 @@ static const char * gas_result_txt(enum gas_query_result result)
return "PEER_ERROR";
case GAS_QUERY_INTERNAL_ERROR:
return "INTERNAL_ERROR";
+ case GAS_QUERY_STOPPED:
+ return "STOPPED";
case GAS_QUERY_DELETED_AT_DEINIT:
return "DELETED_AT_DEINIT";
}
@@ -239,10 +246,17 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
}
os_get_reltime(&query->last_oper);
- if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+ if (result == OFFCHANNEL_SEND_ACTION_SUCCESS ||
+ result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
eloop_cancel_timeout(gas_query_timeout, gas, query);
- eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
- gas_query_timeout, gas, query);
+ if (result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
+ 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);
@@ -278,8 +292,9 @@ static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
};
wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
- "freq=%d prot=%d", MAC2STR(query->addr),
- (unsigned int) wpabuf_len(req), query->freq, prot);
+ "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;
@@ -288,17 +303,20 @@ static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
if (gas->wpa_s->max_remain_on_chan &&
wait_time > gas->wpa_s->max_remain_on_chan)
wait_time = gas->wpa_s->max_remain_on_chan;
- if (!gas->wpa_s->conf->gas_address3 ||
- (gas->wpa_s->current_ssid &&
- gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
- os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0))
+ if (!query->wildcard_bssid &&
+ (!gas->wpa_s->conf->gas_address3 ||
+ (gas->wpa_s->current_ssid &&
+ gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
+ os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0)))
bssid = query->addr;
else
bssid = wildcard_bssid;
+
res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
- gas->wpa_s->own_addr, bssid,
- wpabuf_head(req), wpabuf_len(req),
- wait_time, gas_query_tx_status, 0);
+ query->sa, bssid, wpabuf_head(req),
+ wpabuf_len(req), wait_time,
+ gas_query_tx_status, 0);
+
if (res == 0)
query->offchannel_tx_started = 1;
return res;
@@ -407,6 +425,7 @@ static void gas_query_rx_initial(struct gas_query *gas,
}
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;
@@ -724,6 +743,58 @@ static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst)
}
+static int gas_query_set_sa(struct gas_query *gas,
+ struct gas_query_pending *query)
+{
+ struct wpa_supplicant *wpa_s = gas->wpa_s;
+ struct os_reltime now;
+
+ if (!wpa_s->conf->gas_rand_mac_addr ||
+ !(wpa_s->current_bss ?
+ (wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) :
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA))) {
+ /* Use own MAC address as the transmitter address */
+ os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
+ return 0;
+ }
+
+ os_get_reltime(&now);
+
+ if (wpa_s->conf->gas_rand_mac_addr == gas->last_rand_sa_type &&
+ gas->last_mac_addr_rand.sec != 0 &&
+ !os_reltime_expired(&now, &gas->last_mac_addr_rand,
+ wpa_s->conf->gas_rand_addr_lifetime)) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: Use the previously selected random transmitter address "
+ MACSTR, MAC2STR(gas->rand_addr));
+ os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
+ return 0;
+ }
+
+ if (wpa_s->conf->gas_rand_mac_addr == 1 &&
+ random_mac_addr(gas->rand_addr) < 0) {
+ wpa_printf(MSG_ERROR, "GAS: Failed to get random address");
+ return -1;
+ }
+
+ if (wpa_s->conf->gas_rand_mac_addr == 2 &&
+ random_mac_addr_keep_oui(gas->rand_addr) < 0) {
+ wpa_printf(MSG_ERROR,
+ "GAS: Failed to get random address with same OUI");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "GAS: Use a new random transmitter address "
+ MACSTR, MAC2STR(gas->rand_addr));
+ os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
+ os_get_reltime(&gas->last_mac_addr_rand);
+ gas->last_rand_sa_type = wpa_s->conf->gas_rand_mac_addr;
+
+ return 0;
+}
+
+
/**
* gas_query_req - Request a GAS query
* @gas: GAS query data from gas_query_init()
@@ -736,7 +807,7 @@ static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst)
* Returns: dialog token (>= 0) on success or -1 on failure
*/
int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
- struct wpabuf *req,
+ int wildcard_bssid, struct wpabuf *req,
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
@@ -758,8 +829,13 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
return -1;
query->gas = gas;
+ if (gas_query_set_sa(gas, query)) {
+ os_free(query);
+ return -1;
+ }
os_memcpy(query->addr, dst, ETH_ALEN);
query->dialog_token = dialog_token;
+ query->wildcard_bssid = !!wildcard_bssid;
query->freq = freq;
query->cb = cb;
query->ctx = ctx;
@@ -781,3 +857,27 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
return dialog_token;
}
+
+
+int gas_query_stop(struct gas_query *gas, u8 dialog_token)
+{
+ struct gas_query_pending *query;
+
+ dl_list_for_each(query, &gas->pending, struct gas_query_pending, list) {
+ if (query->dialog_token == dialog_token) {
+ if (!gas->work) {
+ /* The pending radio work has not yet been
+ * started, but the pending entry has a
+ * reference to the soon to be freed query.
+ * Need to remove that radio work now to avoid
+ * leaving behind a reference to freed memory.
+ */
+ radio_remove_pending_work(gas->wpa_s, query);
+ }
+ gas_query_done(gas, query, GAS_QUERY_STOPPED);
+ return 0;
+ }
+ }
+
+ return -1;
+}
diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h
index ef82097e2424..982c0f7ce60e 100644
--- a/wpa_supplicant/gas_query.h
+++ b/wpa_supplicant/gas_query.h
@@ -29,16 +29,18 @@ enum gas_query_result {
GAS_QUERY_TIMEOUT,
GAS_QUERY_PEER_ERROR,
GAS_QUERY_INTERNAL_ERROR,
+ GAS_QUERY_STOPPED,
GAS_QUERY_DELETED_AT_DEINIT
};
int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
- struct wpabuf *req,
+ int wildcard_bssid, struct wpabuf *req,
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
const struct wpabuf *resp, u16 status_code),
void *ctx);
+int gas_query_stop(struct gas_query *gas, u8 dialog_token);
#else /* CONFIG_GAS */
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index e88f147bbd1b..f4187900ed42 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -49,9 +49,12 @@ struct osu_provider {
u8 bssid[ETH_ALEN];
u8 osu_ssid[SSID_MAX_LEN];
u8 osu_ssid_len;
+ u8 osu_ssid2[SSID_MAX_LEN];
+ u8 osu_ssid2_len;
char server_uri[256];
u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
char osu_nai[256];
+ char osu_nai2[256];
struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
size_t friendly_name_count;
struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
@@ -118,6 +121,22 @@ void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id)
}
+void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf,
+ const struct wpa_ssid *ssid)
+{
+ if (!ssid->roaming_consortium_selection ||
+ !ssid->roaming_consortium_selection_len)
+ return;
+
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + ssid->roaming_consortium_selection_len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ROAMING_CONS_SEL_OUI_TYPE);
+ wpabuf_put_data(buf, ssid->roaming_consortium_selection,
+ ssid->roaming_consortium_selection_len);
+}
+
+
int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_bss *bss)
{
@@ -248,7 +267,7 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
if (buf == NULL)
return -1;
- res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+ res = gas_query_req(wpa_s->gas, dst, freq, 0, buf, anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
wpabuf_free(buf);
@@ -429,10 +448,9 @@ static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
if (icon->dialog_token == dialog_token && !icon->image &&
os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) {
- icon->image = os_malloc(slen);
+ icon->image = os_memdup(pos, slen);
if (!icon->image)
return -1;
- os_memcpy(icon->image, pos, slen);
icon->image_len = slen;
hs20_remove_duplicate_icons(wpa_s, icon);
wpa_msg(wpa_s, MSG_INFO,
@@ -646,6 +664,25 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
wpa_s, NULL);
}
break;
+ case HS20_STYPE_OPERATOR_ICON_METADATA:
+ wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
+ " Operator Icon Metadata", MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "Operator Icon Metadata", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->hs20_operator_icon_metadata);
+ anqp->hs20_operator_icon_metadata =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
+ wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
+ " OSU Providers NAI List", MAC2STR(sa));
+ if (anqp) {
+ wpabuf_free(anqp->hs20_osu_providers_nai_list);
+ anqp->hs20_osu_providers_nai_list =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
default:
wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
break;
@@ -725,8 +762,15 @@ static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
wpa_ssid_txt(osu->osu_ssid,
osu->osu_ssid_len));
}
+ if (osu->osu_ssid2_len) {
+ fprintf(f, "osu_ssid2=%s\n",
+ wpa_ssid_txt(osu->osu_ssid2,
+ osu->osu_ssid2_len));
+ }
if (osu->osu_nai[0])
fprintf(f, "osu_nai=%s\n", osu->osu_nai);
+ if (osu->osu_nai2[0])
+ fprintf(f, "osu_nai2=%s\n", osu->osu_nai2);
for (j = 0; j < osu->friendly_name_count; j++) {
fprintf(f, "friendly_name=%s:%s\n",
osu->friendly_name[j].lang,
@@ -790,6 +834,7 @@ void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
const u8 *osu_ssid, u8 osu_ssid_len,
+ const u8 *osu_ssid2, u8 osu_ssid2_len,
const u8 *pos, size_t len)
{
struct osu_provider *prov;
@@ -811,6 +856,9 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
prov->osu_ssid_len = osu_ssid_len;
+ if (osu_ssid2)
+ os_memcpy(prov->osu_ssid2, osu_ssid2, osu_ssid2_len);
+ prov->osu_ssid2_len = osu_ssid2_len;
/* OSU Friendly Name Length */
if (end - pos < 2) {
@@ -992,18 +1040,30 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
struct wpabuf *prov_anqp;
const u8 *pos, *end;
u16 len;
- const u8 *osu_ssid;
- u8 osu_ssid_len;
+ const u8 *osu_ssid, *osu_ssid2;
+ u8 osu_ssid_len, osu_ssid2_len;
u8 num_providers;
hs20_free_osu_prov(wpa_s);
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ struct wpa_ie_data data;
+ const u8 *ie;
+
if (bss->anqp == NULL)
continue;
prov_anqp = bss->anqp->hs20_osu_providers_list;
if (prov_anqp == NULL)
continue;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &data) == 0 &&
+ (data.key_mgmt & WPA_KEY_MGMT_OSEN)) {
+ osu_ssid2 = bss->ssid;
+ osu_ssid2_len = bss->ssid_len;
+ } else {
+ osu_ssid2 = NULL;
+ osu_ssid2_len = 0;
+ }
wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
MACSTR, MAC2STR(bss->bssid));
wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
@@ -1045,7 +1105,8 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
if (len > (unsigned int) (end - pos))
break;
hs20_osu_add_prov(wpa_s, bss, osu_ssid,
- osu_ssid_len, pos, len);
+ osu_ssid_len, osu_ssid2,
+ osu_ssid2_len, pos, len);
pos += len;
}
@@ -1054,6 +1115,35 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
"extra data after OSU Providers",
(int) (end - pos));
}
+
+ prov_anqp = bss->anqp->hs20_osu_providers_nai_list;
+ if (!prov_anqp)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Parsing OSU Providers NAI List from "
+ MACSTR, MAC2STR(bss->bssid));
+ wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers NAI List",
+ prov_anqp);
+ pos = wpabuf_head(prov_anqp);
+ end = pos + wpabuf_len(prov_anqp);
+ num_providers = 0;
+ while (end - pos > 0) {
+ len = *pos++;
+ if (end - pos < len) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Not enough room for OSU_NAI");
+ break;
+ }
+ if (num_providers >= wpa_s->osu_prov_count) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Ignore unexpected OSU Provider NAI List entries");
+ break;
+ }
+ os_memcpy(wpa_s->osu_prov[num_providers].osu_nai2,
+ pos, len);
+ pos += len;
+ num_providers++;
+ }
}
wpa_s->fetch_osu_icon_in_progress = 1;
@@ -1207,6 +1297,18 @@ void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
}
+void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url)
+{
+ if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Ignore Terms and Conditions Acceptance since PMF was not enabled");
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, HS20_T_C_ACCEPTANCE "%s", url);
+}
+
+
void hs20_init(struct wpa_supplicant *wpa_s)
{
dl_list_init(&wpa_s->icon_head);
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index 0dd559fdbf01..66fc540be3e4 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -10,6 +10,8 @@
void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s);
void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id);
+void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf,
+ const struct wpa_ssid *ssid);
int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
const u8 *payload, size_t payload_len, int inmem);
@@ -27,6 +29,7 @@ void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
const char *url, u8 osu_method);
void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
u16 reauth_delay, const char *url);
+void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url);
void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 53d7d57bde35..00919d14a55e 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -259,9 +259,13 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level,
static const u8 * auth_get_psk(void *ctx, const u8 *addr,
- const u8 *p2p_dev_addr, const u8 *prev_psk)
+ const u8 *p2p_dev_addr, const u8 *prev_psk,
+ size_t *psk_len)
{
struct ibss_rsn *ibss_rsn = ctx;
+
+ if (psk_len)
+ *psk_len = PMK_LEN;
wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
__func__, MAC2STR(addr), prev_psk);
if (prev_psk)
@@ -408,7 +412,15 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
const u8 *own_addr, struct wpa_ssid *ssid)
{
struct wpa_auth_config conf;
- struct wpa_auth_callbacks cb;
+ static const struct wpa_auth_callbacks cb = {
+ .logger = auth_logger,
+ .set_eapol = auth_set_eapol,
+ .send_eapol = auth_send_eapol,
+ .get_psk = auth_get_psk,
+ .set_key = auth_set_key,
+ .for_each_sta = auth_for_each_sta,
+ .disconnect = ibss_rsn_disconnect,
+ };
wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
@@ -420,18 +432,10 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
conf.wpa_group = WPA_CIPHER_CCMP;
conf.eapol_version = 2;
conf.wpa_group_rekey = ssid->group_rekey ? ssid->group_rekey : 600;
+ conf.wpa_group_update_count = 4;
+ conf.wpa_pairwise_update_count = 4;
- os_memset(&cb, 0, sizeof(cb));
- cb.ctx = ibss_rsn;
- cb.logger = auth_logger;
- cb.set_eapol = auth_set_eapol;
- cb.send_eapol = auth_send_eapol;
- cb.get_psk = auth_get_psk;
- cb.set_key = auth_set_key;
- cb.for_each_sta = auth_for_each_sta;
- cb.disconnect = ibss_rsn_disconnect;
-
- ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
+ ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb, ibss_rsn);
if (ibss_rsn->auth_group == NULL) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
return -1;
@@ -458,7 +462,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
"\x00\x0f\xac\x04"
"\x01\x00\x00\x0f\xac\x04"
"\x01\x00\x00\x0f\xac\x02"
- "\x00\x00", 22, NULL, 0) !=
+ "\x00\x00", 22, NULL, 0, NULL, 0) !=
WPA_IE_OK) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
return -1;
@@ -760,10 +764,9 @@ static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
if (supp < 0)
return -1;
- tmp = os_malloc(len);
+ tmp = os_memdup(buf, len);
if (tmp == NULL)
return -1;
- os_memcpy(tmp, buf, len);
if (supp) {
peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER;
wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from "
@@ -838,6 +841,18 @@ static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn,
MAC2STR(addr));
if (peer &&
+ peer->authentication_status & (IBSS_RSN_SET_PTK_SUPP |
+ IBSS_RSN_SET_PTK_AUTH)) {
+ /* Clear the TK for this pair to allow recovery from the case
+ * where the peer STA has restarted and lost its key while we
+ * still have a pairwise key configured. */
+ wpa_printf(MSG_DEBUG, "RSN: Clear pairwise key for peer "
+ MACSTR, MAC2STR(addr));
+ wpa_drv_set_key(ibss_rsn->wpa_s, WPA_ALG_NONE, addr, 0, 0,
+ NULL, 0, NULL, 0);
+ }
+
+ if (peer &&
peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) {
if (peer->own_auth_tx.sec) {
struct os_reltime now, diff;
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 1fb40c74e5cf..60c8be9a6c6a 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -106,10 +106,12 @@ static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
if (buf == NULL)
return NULL;
- len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
- for (i = 0; i < num_ids; i++)
- wpabuf_put_le16(buf, info_ids[i]);
- gas_anqp_set_element_len(buf, len_pos);
+ if (num_ids > 0) {
+ len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
+ for (i = 0; i < num_ids; i++)
+ wpabuf_put_le16(buf, info_ids[i]);
+ gas_anqp_set_element_len(buf, len_pos);
+ }
if (extra)
wpabuf_put_buf(buf, extra);
@@ -146,6 +148,8 @@ static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
return 1;
if (cred->required_roaming_consortium_len)
return 1;
+ if (cred->num_roaming_consortiums)
+ return 1;
}
return 0;
}
@@ -299,8 +303,10 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
if (all)
wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
- if (all)
+ if (all) {
wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
+ wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
+ }
gas_anqp_set_element_len(extra, len_pos);
}
#endif /* CONFIG_HS20 */
@@ -310,7 +316,7 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
if (buf == NULL)
return -1;
- res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
+ res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, 0, buf,
interworking_anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
@@ -1143,6 +1149,23 @@ static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
}
+static int cred_roaming_consortiums_match(const u8 *ie,
+ const struct wpabuf *anqp,
+ const struct wpa_cred *cred)
+{
+ unsigned int i;
+
+ for (i = 0; i < cred->num_roaming_consortiums; i++) {
+ if (roaming_consortium_match(ie, anqp,
+ cred->roaming_consortiums[i],
+ cred->roaming_consortiums_len[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
{
const u8 *ie;
@@ -1347,27 +1370,28 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium(
{
struct wpa_cred *cred, *selected = NULL;
const u8 *ie;
+ const struct wpabuf *anqp;
int is_excluded = 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+ anqp = bss->anqp ? bss->anqp->roaming_consortium : NULL;
- if (ie == NULL &&
- (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+ if (!ie && !anqp)
return NULL;
if (wpa_s->conf->cred == NULL)
return NULL;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- if (cred->roaming_consortium_len == 0)
+ if (cred->roaming_consortium_len == 0 &&
+ cred->num_roaming_consortiums == 0)
continue;
- if (!roaming_consortium_match(ie,
- bss->anqp ?
- bss->anqp->roaming_consortium :
- NULL,
- cred->roaming_consortium,
- cred->roaming_consortium_len))
+ if ((cred->roaming_consortium_len == 0 ||
+ !roaming_consortium_match(ie, anqp,
+ cred->roaming_consortium,
+ cred->roaming_consortium_len)) &&
+ !cred_roaming_consortiums_match(ie, anqp, cred))
continue;
if (cred_no_required_oi_match(cred, bss))
@@ -1533,6 +1557,9 @@ static int interworking_connect_roaming_consortium(
struct wpa_bss *bss, int only_add)
{
struct wpa_ssid *ssid;
+ const u8 *ie;
+ const struct wpabuf *anqp;
+ unsigned int i;
wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
" based on roaming consortium match", MAC2STR(bss->bssid));
@@ -1562,6 +1589,26 @@ static int interworking_connect_roaming_consortium(
if (interworking_set_hs20_params(wpa_s, ssid) < 0)
goto fail;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+ anqp = bss->anqp ? bss->anqp->roaming_consortium : NULL;
+ for (i = 0; (ie || anqp) && i < cred->num_roaming_consortiums; i++) {
+ if (!roaming_consortium_match(
+ ie, anqp, cred->roaming_consortiums[i],
+ cred->roaming_consortiums_len[i]))
+ continue;
+
+ ssid->roaming_consortium_selection =
+ os_malloc(cred->roaming_consortiums_len[i]);
+ if (!ssid->roaming_consortium_selection)
+ goto fail;
+ os_memcpy(ssid->roaming_consortium_selection,
+ cred->roaming_consortiums[i],
+ cred->roaming_consortiums_len[i]);
+ ssid->roaming_consortium_selection_len =
+ cred->roaming_consortiums_len[i];
+ break;
+ }
+
if (cred->eap_method == NULL) {
wpa_msg(wpa_s, MSG_DEBUG,
"Interworking: No EAP method set for credential using roaming consortium");
@@ -1769,9 +1816,10 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
switch (eap->method) {
case EAP_TYPE_TTLS:
if (eap->inner_method) {
- os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
- eap_get_name(EAP_VENDOR_IETF,
- eap->inner_method));
+ name = eap_get_name(EAP_VENDOR_IETF, eap->inner_method);
+ if (!name)
+ goto fail;
+ os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", name);
if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
goto fail;
break;
@@ -1894,7 +1942,7 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
size_t len;
wpa_msg(wpa_s, MSG_DEBUG,
"Interworking: IMSI not available - try to read again through eap_proxy");
- wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
wpa_s->imsi,
&len);
if (wpa_s->mnc_len > 0) {
@@ -2530,7 +2578,8 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
MAC2STR(selected->bssid));
interworking_connect(wpa_s, selected, 0);
- }
+ } else if (wpa_s->wpa_state == WPA_SCANNING)
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
}
@@ -2693,7 +2742,7 @@ void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
u16 info_ids[], size_t num_ids, u32 subtypes,
- int get_cell_pref)
+ u32 mbo_subtypes)
{
struct wpabuf *buf;
struct wpabuf *extra_buf = NULL;
@@ -2727,13 +2776,14 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
- if (get_cell_pref) {
+ if (mbo_subtypes) {
struct wpabuf *mbo;
- mbo = mbo_build_anqp_buf(wpa_s, bss);
+ mbo = mbo_build_anqp_buf(wpa_s, bss, mbo_subtypes);
if (mbo) {
if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) {
wpabuf_free(extra_buf);
+ wpabuf_free(mbo);
return -1;
}
wpabuf_put_buf(extra_buf, mbo);
@@ -2747,7 +2797,7 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
if (buf == NULL)
return -1;
- res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+ res = gas_query_req(wpa_s->gas, dst, freq, 0, buf, anqp_resp_cb, wpa_s);
if (res < 0) {
wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
wpabuf_free(buf);
@@ -2796,6 +2846,31 @@ static void anqp_add_extra(struct wpa_supplicant *wpa_s,
}
+static void interworking_parse_venue_url(struct wpa_supplicant *wpa_s,
+ const u8 *data, size_t len)
+{
+ const u8 *pos = data, *end = data + len;
+ char url[255];
+
+ while (end - pos >= 2) {
+ u8 slen, num;
+
+ slen = *pos++;
+ if (slen < 1 || slen > end - pos) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Truncated Venue URL Duple field");
+ return;
+ }
+
+ num = *pos++;
+ os_memcpy(url, pos, slen - 1);
+ url[slen - 1] = '\0';
+ wpa_msg(wpa_s, MSG_INFO, RX_VENUE_URL "%u %s", num, url);
+ pos += slen - 1;
+ }
+}
+
+
static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, const u8 *sa,
u16 info_id,
@@ -2804,9 +2879,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
{
const u8 *pos = data;
struct wpa_bss_anqp *anqp = NULL;
-#ifdef CONFIG_HS20
u8 type;
-#endif /* CONFIG_HS20 */
if (bss)
anqp = bss->anqp;
@@ -2892,12 +2965,35 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
anqp->domain_name = wpabuf_alloc_copy(pos, slen);
}
break;
+#ifdef CONFIG_FILS
+ case ANQP_FILS_REALM_INFO:
+ wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
+ " FILS Realm Information", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: FILS Realm Information",
+ pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->fils_realm_info);
+ anqp->fils_realm_info = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+#endif /* CONFIG_FILS */
+ case ANQP_VENUE_URL:
+ wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Venue URL",
+ MAC2STR(sa));
+ anqp_add_extra(wpa_s, anqp, info_id, pos, slen);
+
+ if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Ignore Venue URL since PMF was not enabled");
+ break;
+ }
+ interworking_parse_venue_url(wpa_s, pos, slen);
+ break;
case ANQP_VENDOR_SPECIFIC:
if (slen < 3)
return;
switch (WPA_GET_BE24(pos)) {
-#ifdef CONFIG_HS20
case OUI_WFA:
pos += 3;
slen -= 3;
@@ -2908,19 +3004,26 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
slen--;
switch (type) {
+#ifdef CONFIG_HS20
case HS20_ANQP_OUI_TYPE:
hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
pos, slen,
dialog_token);
break;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+ case MBO_ANQP_OUI_TYPE:
+ mbo_parse_rx_anqp_resp(wpa_s, bss, sa,
+ pos, slen);
+ break;
+#endif /* CONFIG_MBO */
default:
wpa_msg(wpa_s, MSG_DEBUG,
- "HS20: Unsupported ANQP vendor type %u",
+ "ANQP: Unsupported ANQP vendor type %u",
type);
break;
}
break;
-#endif /* CONFIG_HS20 */
default:
wpa_msg(wpa_s, MSG_DEBUG,
"Interworking: Unsupported vendor-specific ANQP OUI %06x",
@@ -3133,7 +3236,7 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
} else
wpabuf_put_le16(buf, 0);
- res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
+ res = gas_query_req(wpa_s->gas, dst, freq, 0, buf, gas_resp_cb, wpa_s);
if (res < 0) {
wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
wpabuf_free(buf);
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 3d22292618b2..37ee2e904e48 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -13,7 +13,7 @@ enum gas_query_result;
int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
u16 info_ids[], size_t num_ids, u32 subtypes,
- int get_cell_pref);
+ u32 mbo_subtypes);
void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 7e049be3df41..5adf61e58bd0 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -38,6 +38,19 @@ static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason)
}
+const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr)
+{
+ const u8 *mbo;
+ u8 ie_len = mbo_ie[1];
+
+ if (ie_len < MBO_IE_HEADER - 2)
+ return NULL;
+ mbo = mbo_ie + MBO_IE_HEADER;
+
+ return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr);
+}
+
+
const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr)
{
const u8 *mbo, *end;
@@ -149,12 +162,14 @@ static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s,
}
-int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
+ int add_oce_capa)
{
struct wpabuf *mbo;
int res;
- if (len < MBO_IE_HEADER + 3 + 7)
+ if (len < MBO_IE_HEADER + 3 + 7 +
+ ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
return 0;
/* Leave room for the MBO IE header */
@@ -173,9 +188,16 @@ int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
wpabuf_put_u8(mbo, 1);
wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa);
+ /* Add OCE capability indication attribute if OCE is enabled */
+ if ((wpa_s->enable_oce & OCE_STA) && add_oce_capa) {
+ wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND);
+ wpabuf_put_u8(mbo, 1);
+ wpabuf_put_u8(mbo, OCE_RELEASE);
+ }
+
res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
if (!res)
- wpa_printf(MSG_ERROR, "Failed to add MBO IE");
+ wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE");
wpabuf_free(mbo);
return res;
@@ -277,11 +299,10 @@ int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
non_pref_chan ? non_pref_chan : "N/A");
/*
- * The shortest channel configuration is 10 characters - commas, 3
- * colons, and 4 values that one of them (oper_class) is 2 digits or
- * more.
+ * The shortest channel configuration is 7 characters - 3 colons and
+ * 4 values.
*/
- if (!non_pref_chan || os_strlen(non_pref_chan) < 10)
+ if (!non_pref_chan || os_strlen(non_pref_chan) < 7)
goto update;
cmd = os_strdup(non_pref_chan);
@@ -369,315 +390,30 @@ fail:
void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
{
+ u8 *len;
+
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
- wpabuf_put_u8(ie, 7);
+ len = wpabuf_put(ie, 1);
+
wpabuf_put_be24(ie, OUI_WFA);
wpabuf_put_u8(ie, MBO_OUI_TYPE);
wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
wpabuf_put_u8(ie, 1);
wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa);
-}
-
-
-enum chan_allowed {
- NOT_ALLOWED, ALLOWED
-};
-
-static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan,
- unsigned int *flags)
-{
- int i;
-
- for (i = 0; i < mode->num_channels; i++) {
- if (mode->channels[i].chan == chan)
- break;
+ if (wpa_s->enable_oce & OCE_STA) {
+ wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND);
+ wpabuf_put_u8(ie, 1);
+ wpabuf_put_u8(ie, OCE_RELEASE);
}
-
- if (i == mode->num_channels ||
- (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED))
- return NOT_ALLOWED;
-
- if (flags)
- *flags = mode->channels[i].flag;
-
- return ALLOWED;
-}
-
-
-static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel)
-{
- u8 center_channels[] = {42, 58, 106, 122, 138, 155};
- size_t i;
-
- if (mode->mode != HOSTAPD_MODE_IEEE80211A)
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
- /*
- * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
- * so the center channel is 6 channels away from the start/end.
- */
- if (channel >= center_channels[i] - 6 &&
- channel <= center_channels[i] + 6)
- return center_channels[i];
- }
-
- return 0;
-}
-
-
-static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel)
-{
- u8 center_chan;
- unsigned int i;
-
- center_chan = get_center_80mhz(mode, channel);
- if (!center_chan)
- return NOT_ALLOWED;
-
- /* check all the channels are available */
- for (i = 0; i < 4; i++) {
- unsigned int flags;
- u8 adj_chan = center_chan - 6 + i * 4;
-
- if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
- return NOT_ALLOWED;
-
- if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
- (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
- (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
- (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
- return NOT_ALLOWED;
- }
-
- return ALLOWED;
-}
-
-
-static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel)
-{
- u8 center_channels[] = { 50, 114 };
- unsigned int i;
-
- if (mode->mode != HOSTAPD_MODE_IEEE80211A)
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
- /*
- * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
- * so the center channel is 14 channels away from the start/end.
- */
- if (channel >= center_channels[i] - 14 &&
- channel <= center_channels[i] + 14)
- return center_channels[i];
- }
-
- return 0;
-}
-
-
-static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
- u8 channel)
-{
- u8 center_chan;
- unsigned int i;
-
- center_chan = get_center_160mhz(mode, channel);
- if (!center_chan)
- return NOT_ALLOWED;
-
- /* Check all the channels are available */
- for (i = 0; i < 8; i++) {
- unsigned int flags;
- u8 adj_chan = center_chan - 14 + i * 4;
-
- if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
- return NOT_ALLOWED;
-
- if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
- (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
- (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
- (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
- (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
- (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
- (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
- (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
- return NOT_ALLOWED;
- }
-
- return ALLOWED;
-}
-
-
-static enum chan_allowed verify_channel(struct hostapd_hw_modes *mode,
- u8 channel, u8 bw)
-{
- unsigned int flag = 0;
- enum chan_allowed res, res2;
-
- res2 = res = allow_channel(mode, channel, &flag);
- if (bw == BW40MINUS) {
- if (!(flag & HOSTAPD_CHAN_HT40MINUS))
- return NOT_ALLOWED;
- res2 = allow_channel(mode, channel - 4, NULL);
- } else if (bw == BW40PLUS) {
- if (!(flag & HOSTAPD_CHAN_HT40PLUS))
- return NOT_ALLOWED;
- res2 = allow_channel(mode, channel + 4, NULL);
- } else if (bw == BW80) {
- /*
- * channel is a center channel and as such, not necessarily a
- * valid 20 MHz channels. Override earlier allow_channel()
- * result and use only the 80 MHz specific version.
- */
- res2 = res = verify_80mhz(mode, channel);
- } else if (bw == BW160) {
- /*
- * channel is a center channel and as such, not necessarily a
- * valid 20 MHz channels. Override earlier allow_channel()
- * result and use only the 160 MHz specific version.
- */
- res2 = res = verify_160mhz(mode, channel);
- } else if (bw == BW80P80) {
- /*
- * channel is a center channel and as such, not necessarily a
- * valid 20 MHz channels. Override earlier allow_channel()
- * result and use only the 80 MHz specific version.
- */
- res2 = res = verify_80mhz(mode, channel);
- }
-
- if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
- return NOT_ALLOWED;
-
- return ALLOWED;
-}
-
-
-static int wpas_op_class_supported(struct wpa_supplicant *wpa_s,
- const struct oper_class_map *op_class)
-{
- int chan;
- size_t i;
- struct hostapd_hw_modes *mode;
- int found;
-
- mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode);
- if (!mode)
- return 0;
-
- if (op_class->op_class == 128) {
- u8 channels[] = { 42, 58, 106, 122, 138, 155 };
-
- for (i = 0; i < ARRAY_SIZE(channels); i++) {
- if (verify_channel(mode, channels[i], op_class->bw) ==
- ALLOWED)
- return 1;
- }
-
- return 0;
- }
-
- if (op_class->op_class == 129) {
- /* Check if either 160 MHz channels is allowed */
- return verify_channel(mode, 50, op_class->bw) == ALLOWED ||
- verify_channel(mode, 114, op_class->bw) == ALLOWED;
- }
-
- if (op_class->op_class == 130) {
- /* Need at least two non-contiguous 80 MHz segments */
- found = 0;
-
- if (verify_channel(mode, 42, op_class->bw) == ALLOWED ||
- verify_channel(mode, 58, op_class->bw) == ALLOWED)
- found++;
- if (verify_channel(mode, 106, op_class->bw) == ALLOWED ||
- verify_channel(mode, 122, op_class->bw) == ALLOWED ||
- verify_channel(mode, 138, op_class->bw) == ALLOWED)
- found++;
- if (verify_channel(mode, 106, op_class->bw) == ALLOWED &&
- verify_channel(mode, 138, op_class->bw) == ALLOWED)
- found++;
- if (verify_channel(mode, 155, op_class->bw) == ALLOWED)
- found++;
-
- if (found >= 2)
- return 1;
-
- return 0;
- }
-
- found = 0;
- for (chan = op_class->min_chan; chan <= op_class->max_chan;
- chan += op_class->inc) {
- if (verify_channel(mode, chan, op_class->bw) == ALLOWED) {
- found = 1;
- break;
- }
- }
-
- return found;
-}
-
-
-int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
- size_t len)
-{
- struct wpabuf *buf;
- u8 op, current, chan;
- u8 *ie_len;
- int res;
-
- /*
- * Assume 20 MHz channel for now.
- * TODO: Use the secondary channel and VHT channel width that will be
- * used after association.
- */
- if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
- &current, &chan) == NUM_HOSTAPD_MODES)
- return 0;
-
- /*
- * Need 3 bytes for EID, length, and current operating class, plus
- * 1 byte for every other supported operating class.
- */
- buf = wpabuf_alloc(global_op_class_size + 3);
- if (!buf)
- return 0;
-
- wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES);
- /* Will set the length later, putting a placeholder */
- ie_len = wpabuf_put(buf, 1);
- wpabuf_put_u8(buf, current);
-
- for (op = 0; global_op_class[op].op_class; op++) {
- if (wpas_op_class_supported(wpa_s, &global_op_class[op]))
- wpabuf_put_u8(buf, global_op_class[op].op_class);
- }
-
- *ie_len = wpabuf_len(buf) - 2;
- if (*ie_len < 2 || wpabuf_len(buf) > len) {
- wpa_printf(MSG_ERROR,
- "Failed to add supported operating classes IE");
- res = 0;
- } else {
- os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
- res = wpabuf_len(buf);
- wpa_hexdump_buf(MSG_DEBUG,
- "MBO: Added supported operating classes IE",
- buf);
- }
-
- wpabuf_free(buf);
- return res;
+ *len = (u8 *) wpabuf_put(ie, 0) - len - 1;
}
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
size_t len)
{
- const u8 *pos, *cell_pref = NULL, *reason = NULL;
+ const u8 *pos, *cell_pref = NULL;
u8 id, elen;
u16 disallowed_sec = 0;
@@ -712,7 +448,8 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
if (elen != 1)
goto fail;
- reason = pos;
+ wpa_s->wnm_mbo_trans_reason_present = 1;
+ wpa_s->wnm_mbo_transition_reason = *pos;
break;
case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
if (elen != 2)
@@ -726,6 +463,9 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
} else if (wpa_s->wnm_mode &
WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
disallowed_sec = WPA_GET_LE16(pos);
+ wpa_printf(MSG_DEBUG,
+ "MBO: Association retry delay: %u",
+ disallowed_sec);
} else {
wpa_printf(MSG_DEBUG,
"MBO: Association retry delay attribute not in disassoc imminent mode");
@@ -755,9 +495,9 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
*cell_pref);
- if (reason)
+ if (wpa_s->wnm_mbo_trans_reason_present)
wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
- *reason);
+ wpa_s->wnm_mbo_transition_reason);
if (disallowed_sec && wpa_s->current_bss)
wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
@@ -809,10 +549,11 @@ void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa)
struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
- struct wpa_bss *bss)
+ struct wpa_bss *bss, u32 mbo_subtypes)
{
struct wpabuf *anqp_buf;
u8 *len_pos;
+ u8 i;
if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
wpa_printf(MSG_INFO, "MBO: " MACSTR
@@ -821,7 +562,8 @@ struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
return NULL;
}
- anqp_buf = wpabuf_alloc(10);
+ /* Allocate size for the maximum case - all MBO subtypes are set */
+ anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE);
if (!anqp_buf)
return NULL;
@@ -829,8 +571,43 @@ struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
wpabuf_put_be24(anqp_buf, OUI_WFA);
wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
- wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+ wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST);
+
+ /* The first valid MBO subtype is 1 */
+ for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) {
+ if (mbo_subtypes & BIT(i))
+ wpabuf_put_u8(anqp_buf, i);
+ }
+
gas_anqp_set_element_len(anqp_buf, len_pos);
return anqp_buf;
}
+
+
+void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, const u8 *sa,
+ const u8 *data, size_t slen)
+{
+ const u8 *pos = data;
+ u8 subtype;
+
+ if (slen < 1)
+ return;
+
+ subtype = *pos++;
+ slen--;
+
+ switch (subtype) {
+ case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+ if (slen < 1)
+ break;
+ wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR
+ " cell_conn_pref=%u", MAC2STR(sa), *pos);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u",
+ subtype);
+ break;
+ }
+}
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index d67d3b2aa390..38b9fb320ca9 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -84,6 +84,7 @@ static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s,
MESH_CONF_SEC_AMPE;
else
conf->security |= MESH_CONF_SEC_NONE;
+#ifdef CONFIG_IEEE80211W
conf->ieee80211w = ssid->ieee80211w;
if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)
@@ -91,6 +92,7 @@ static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s,
else
conf->ieee80211w = NO_MGMT_FRAME_PROTECTION;
}
+#endif /* CONFIG_IEEE80211W */
cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0);
if (cipher < 0 || cipher == WPA_CIPHER_TKIP) {
@@ -146,7 +148,8 @@ static void wpas_mesh_copy_groups(struct hostapd_data *bss,
static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid)
+ struct wpa_ssid *ssid,
+ struct hostapd_freq_params *freq)
{
struct hostapd_iface *ifmsh;
struct hostapd_data *bss;
@@ -154,8 +157,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
struct mesh_conf *mconf;
int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 };
static int default_groups[] = { 19, 20, 21, 25, 26, -1 };
+ const char *password;
size_t len;
int rate_len;
+ int frequency;
if (!wpa_s->conf->user_mpm) {
/* not much for us to do here */
@@ -164,7 +169,7 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
return 0;
}
- wpa_s->ifmsh = ifmsh = os_zalloc(sizeof(*wpa_s->ifmsh));
+ wpa_s->ifmsh = ifmsh = hostapd_alloc_iface();
if (!ifmsh)
return -ENOMEM;
@@ -175,17 +180,23 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
if (!ifmsh->bss)
goto out_free;
- ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data));
+ ifmsh->bss[0] = bss = hostapd_alloc_bss_data(NULL, NULL, NULL);
if (!bss)
goto out_free;
- dl_list_init(&bss->nr_db);
+ ifmsh->bss[0]->msg_ctx = wpa_s;
os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN);
bss->driver = wpa_s->driver;
bss->drv_priv = wpa_s->drv_priv;
bss->iface = ifmsh;
bss->mesh_sta_free_cb = mesh_mpm_free_sta;
- wpa_s->assoc_freq = ssid->frequency;
+ frequency = ssid->frequency;
+ if (frequency != freq->freq &&
+ frequency == freq->freq + freq->sec_channel_offset * 20) {
+ wpa_printf(MSG_DEBUG, "mesh: pri/sec channels switched");
+ frequency = freq->freq;
+ }
+ wpa_s->assoc_freq = frequency;
wpa_s->current_ssid = ssid;
/* setup an AP config for auth processing */
@@ -211,10 +222,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
ifmsh->mconf = mconf;
/* need conf->hw_mode for supported rates. */
- conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, &conf->channel);
+ conf->hw_mode = ieee80211_freq_to_chan(frequency, &conf->channel);
if (conf->hw_mode == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz",
- ssid->frequency);
+ frequency);
goto out_free;
}
if (ssid->ht40)
@@ -225,13 +236,13 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
case VHT_CHANWIDTH_80MHZ:
case VHT_CHANWIDTH_80P80MHZ:
ieee80211_freq_to_chan(
- ssid->frequency,
+ frequency,
&conf->vht_oper_centr_freq_seg0_idx);
conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
break;
case VHT_CHANWIDTH_160MHZ:
ieee80211_freq_to_chan(
- ssid->frequency,
+ frequency,
&conf->vht_oper_centr_freq_seg0_idx);
conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
conf->vht_oper_centr_freq_seg0_idx += 40 / 5;
@@ -250,11 +261,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
* advertised in beacons match the one in peering frames, sigh.
*/
if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
- conf->basic_rates = os_malloc(sizeof(basic_rates_erp));
+ conf->basic_rates = os_memdup(basic_rates_erp,
+ sizeof(basic_rates_erp));
if (!conf->basic_rates)
goto out_free;
- os_memcpy(conf->basic_rates, basic_rates_erp,
- sizeof(basic_rates_erp));
}
} else {
rate_len = 0;
@@ -283,7 +293,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
}
if (mconf->security != MESH_CONF_SEC_NONE) {
- if (ssid->passphrase == NULL) {
+ password = ssid->sae_password;
+ if (!password)
+ password = ssid->passphrase;
+ if (!password) {
wpa_printf(MSG_ERROR,
"mesh: Passphrase for SAE not configured");
goto out_free;
@@ -297,16 +310,15 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
wpas_mesh_copy_groups(bss, wpa_s);
} else {
bss->conf->sae_groups =
- os_malloc(sizeof(default_groups));
+ os_memdup(default_groups,
+ sizeof(default_groups));
if (!bss->conf->sae_groups)
goto out_free;
- os_memcpy(bss->conf->sae_groups, default_groups,
- sizeof(default_groups));
}
- len = os_strlen(ssid->passphrase);
+ len = os_strlen(password);
bss->conf->ssid.wpa_passphrase =
- dup_binstr(ssid->passphrase, len);
+ dup_binstr(password, len);
wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf);
if (!wpa_s->mesh_rsn)
@@ -406,6 +418,10 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
else if (wpa_s->conf->dtim_period > 0)
params.dtim_period = wpa_s->conf->dtim_period;
params.conf.max_peer_links = wpa_s->conf->max_peer_links;
+ if (ssid->mesh_rssi_threshold < DEFAULT_MESH_RSSI_THRESHOLD) {
+ params.conf.rssi_threshold = ssid->mesh_rssi_threshold;
+ params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD;
+ }
if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH;
@@ -422,7 +438,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
}
params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity;
- if (wpa_supplicant_mesh_init(wpa_s, ssid)) {
+ if (wpa_supplicant_mesh_init(wpa_s, ssid, &params.freq)) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh");
wpa_drv_leave_mesh(wpa_s);
ret = -1;
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index d14c7e3b2045..eafb0af7b82a 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "ap/ieee802_11.h"
@@ -19,6 +20,7 @@
#include "driver_i.h"
#include "mesh_mpm.h"
#include "mesh_rsn.h"
+#include "notify.h"
struct mesh_peer_mgmt_ie {
const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */
@@ -220,13 +222,14 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s,
if (!sta)
return;
- buf_len = 2 + /* capability info */
+ buf_len = 2 + /* Category and Action */
+ 2 + /* capability info */
2 + /* AID */
2 + 8 + /* supported rates */
2 + (32 - 8) +
2 + 32 + /* mesh ID */
2 + 7 + /* mesh config */
- 2 + 23 + /* peering management */
+ 2 + 24 + /* peering management */
2 + 96 + /* AMPE */
2 + 16; /* MIC */
#ifdef CONFIG_IEEE80211N
@@ -435,7 +438,7 @@ static void plink_timer(void *eloop_ctx, void *user_data)
break;
}
reason = WLAN_REASON_MESH_MAX_RETRIES;
- /* fall through on else */
+ /* fall through */
case PLINK_CNF_RCVD:
/* confirm timer */
@@ -646,6 +649,9 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s,
struct mesh_conf *conf = wpa_s->ifmsh->mconf;
struct hostapd_data *data = wpa_s->ifmsh->bss[0];
struct sta_info *sta;
+#ifdef CONFIG_IEEE80211N
+ struct ieee80211_ht_operation *oper;
+#endif /* CONFIG_IEEE80211N */
int ret;
if (elems->mesh_config_len >= 7 &&
@@ -677,6 +683,17 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_IEEE80211N
copy_sta_ht_capab(data, sta, elems->ht_capabilities);
+
+ oper = (struct ieee80211_ht_operation *) elems->ht_operation;
+ if (oper &&
+ !(oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) &&
+ sta->ht_capabilities) {
+ wpa_msg(wpa_s, MSG_DEBUG, MACSTR
+ " does not support 40 MHz bandwidth",
+ MAC2STR(sta->addr));
+ set_disable_ht40(sta->ht_capabilities, 1);
+ }
+
update_ht_state(data, sta);
#endif /* CONFIG_IEEE80211N */
@@ -842,6 +859,9 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
/* Send ctrl event */
wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
MAC2STR(sta->addr));
+
+ /* Send D-Bus event */
+ wpas_notify_mesh_peer_connected(wpa_s, sta->addr);
}
@@ -994,6 +1014,10 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta,
wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR,
MAC2STR(sta->addr));
+ /* Send D-Bus event */
+ wpas_notify_mesh_peer_disconnected(wpa_s, sta->addr,
+ reason);
+
hapd->num_plinks--;
mesh_mpm_send_plink_action(wpa_s, sta,
@@ -1135,7 +1159,7 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
*/
if (!sta && action_field == PLINK_OPEN &&
(!(mconf->security & MESH_CONF_SEC_AMPE) ||
- wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa)))
+ wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa, NULL)))
sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
if (!sta) {
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 27ab8cb36458..e74cb16b0725 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -75,12 +75,15 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level,
static const u8 *auth_get_psk(void *ctx, const u8 *addr,
- const u8 *p2p_dev_addr, const u8 *prev_psk)
+ const u8 *p2p_dev_addr, const u8 *prev_psk,
+ size_t *psk_len)
{
struct mesh_rsn *mesh_rsn = ctx;
struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (psk_len)
+ *psk_len = PMK_LEN;
wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
__func__, MAC2STR(addr), prev_psk);
@@ -140,7 +143,12 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
enum mfp_options ieee80211w)
{
struct wpa_auth_config conf;
- struct wpa_auth_callbacks cb;
+ static const struct wpa_auth_callbacks cb = {
+ .logger = auth_logger,
+ .get_psk = auth_get_psk,
+ .set_key = auth_set_key,
+ .start_ampe = auth_start_ampe,
+ };
u8 seq[6] = {};
wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
@@ -153,20 +161,15 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
conf.wpa_group = rsn->group_cipher;
conf.eapol_version = 0;
conf.wpa_group_rekey = -1;
+ conf.wpa_group_update_count = 4;
+ conf.wpa_pairwise_update_count = 4;
#ifdef CONFIG_IEEE80211W
conf.ieee80211w = ieee80211w;
if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
conf.group_mgmt_cipher = rsn->mgmt_group_cipher;
#endif /* CONFIG_IEEE80211W */
- os_memset(&cb, 0, sizeof(cb));
- cb.ctx = rsn;
- cb.logger = auth_logger;
- cb.get_psk = auth_get_psk;
- cb.set_key = auth_set_key;
- cb.start_ampe = auth_start_ampe;
-
- rsn->auth = wpa_init(addr, &conf, &cb);
+ rsn->auth = wpa_init(addr, &conf, &cb, rsn);
if (rsn->auth == NULL) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
return -1;
@@ -224,6 +227,9 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
struct hostapd_data *bss = wpa_s->ifmsh->bss[0];
const u8 *ie;
size_t ie_len;
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+ struct external_pmksa_cache *entry;
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
mesh_rsn = os_zalloc(sizeof(*mesh_rsn));
if (mesh_rsn == NULL)
@@ -242,6 +248,22 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
bss->wpa_auth = mesh_rsn->auth;
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+ while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache,
+ struct external_pmksa_cache,
+ list)) != NULL) {
+ int ret;
+
+ ret = wpa_auth_pmksa_add_entry(bss->wpa_auth,
+ entry->pmksa_cache);
+ dl_list_del(&entry->list);
+ os_free(entry);
+
+ if (ret < 0)
+ return NULL;
+ }
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len);
conf->rsn_ie = (u8 *) ie;
conf->rsn_ie_len = ie_len;
@@ -295,7 +317,12 @@ static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct sta_info *sta)
{
- if (ssid->passphrase == NULL) {
+ const char *password;
+
+ password = ssid->sae_password;
+ if (!password)
+ password = ssid->passphrase;
+ if (!password) {
wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available");
return -1;
}
@@ -305,9 +332,15 @@ static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
return -1;
}
+ if (sta->sae->tmp && !sta->sae->tmp->pw_id && ssid->sae_password_id) {
+ sta->sae->tmp->pw_id = os_strdup(ssid->sae_password_id);
+ if (!sta->sae->tmp->pw_id)
+ return -1;
+ }
return sae_prepare_commit(wpa_s->own_addr, sta->addr,
- (u8 *) ssid->passphrase,
- os_strlen(ssid->passphrase), sta->sae);
+ (u8 *) password, os_strlen(password),
+ ssid->sae_password_id,
+ sta->sae);
}
@@ -333,7 +366,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
return -1;
}
- pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr);
+ pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL);
if (pmksa) {
if (!sta->wpa_sm)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
@@ -579,7 +612,7 @@ skip_keys:
/* encrypt after MIC */
mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE);
- if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3,
+ if (aes_siv_encrypt(sta->aek, sizeof(sta->aek), ampe_ie, 2 + len, 3,
aad, aad_len, mic_payload)) {
wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
ret = -ENOMEM;
@@ -611,7 +644,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
if (!sta->sae) {
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
- if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) {
+ if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL)) {
wpa_printf(MSG_INFO,
"Mesh RSN: SAE is not prepared yet");
return -1;
@@ -650,7 +683,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
os_memcpy(crypt, elems->mic, crypt_len);
- if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3,
+ if (aes_siv_decrypt(sta->aek, sizeof(sta->aek), crypt, crypt_len, 3,
aad, aad_len, ampe_buf)) {
wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!");
ret = -2;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 67e36ae34cb8..83df04f394c7 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -669,12 +669,12 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int persistent,
- int client)
+ int client, const u8 *ip)
{
/* Notify a group has been started */
wpas_dbus_register_p2p_group(wpa_s, ssid);
- wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent);
+ wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent, ip);
}
@@ -816,6 +816,12 @@ void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
}
+void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code)
+{
+ wpa_msg(wpa_s, MSG_ERROR, WPA_EVENT_EAP_ERROR_CODE "%d", error_code);
+}
+
+
void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
@@ -850,3 +856,49 @@ void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_P2P */
}
+
+
+#ifdef CONFIG_MESH
+
+void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_mesh_group_started(wpa_s, ssid);
+}
+
+
+void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
+ const u8 *meshid, u8 meshid_len,
+ int reason_code)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_mesh_group_removed(wpa_s, meshid, meshid_len,
+ reason_code);
+}
+
+
+void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_mesh_peer_connected(wpa_s, peer_addr);
+}
+
+
+void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int reason_code)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_mesh_peer_disconnected(wpa_s, peer_addr, reason_code);
+}
+
+#endif /* CONFIG_MESH */
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 8cce0f30c2a9..3ca933c7621a 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -114,7 +114,7 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
unsigned int generated_pin);
void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int persistent,
- int client);
+ int client, const u8 *ip);
void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
const char *reason);
void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
@@ -134,6 +134,7 @@ void wpas_notify_preq(struct wpa_supplicant *wpa_s,
const u8 *ie, size_t ie_len, u32 ssi_signal);
void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
const char *parameter);
+void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code);
void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
@@ -141,5 +142,14 @@ void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
const u8 *sa, const u8 *go_dev_addr,
const u8 *bssid, int id, int op_freq);
+void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
+ const u8 *meshid, u8 meshid_len,
+ int reason_code);
+void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr);
+void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int reason_code);
#endif /* NOTIFY_H */
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 26d41a4ad5c6..b74be7dad4ac 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -310,6 +310,8 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
iface = wpas_get_tx_interface(wpa_s, src);
wpa_s->action_tx_wait_time = wait_time;
+ if (wait_time)
+ wpa_s->action_tx_wait_time_used = 1;
ret = wpa_drv_send_action(
iface, wpa_s->pending_action_freq,
@@ -398,13 +400,14 @@ void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
- wpa_s->action_tx_wait_time)
+ (wpa_s->action_tx_wait_time || wpa_s->action_tx_wait_time_used))
wpa_drv_send_action_cancel_wait(wpa_s);
else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = 0;
}
+ wpa_s->action_tx_wait_time_used = 0;
}
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
new file mode 100644
index 000000000000..d23b0094c440
--- /dev/null
+++ b/wpa_supplicant/op_classes.c
@@ -0,0 +1,325 @@
+/*
+ * Operating classes
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+
+
+static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan,
+ unsigned int *flags)
+{
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ if (mode->channels[i].chan == chan)
+ break;
+ }
+
+ if (i == mode->num_channels ||
+ (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED))
+ return NOT_ALLOWED;
+
+ if (flags)
+ *flags = mode->channels[i].flag;
+
+ if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
+ return NO_IR;
+
+ return ALLOWED;
+}
+
+
+static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+ u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
+ size_t i;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
+ /*
+ * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
+ * so the center channel is 6 channels away from the start/end.
+ */
+ if (channel >= center_channels[i] - 6 &&
+ channel <= center_channels[i] + 6)
+ return center_channels[i];
+ }
+
+ return 0;
+}
+
+
+static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+ u8 center_chan;
+ unsigned int i;
+ unsigned int no_ir = 0;
+
+ center_chan = get_center_80mhz(mode, channel);
+ if (!center_chan)
+ return NOT_ALLOWED;
+
+ /* check all the channels are available */
+ for (i = 0; i < 4; i++) {
+ unsigned int flags;
+ u8 adj_chan = center_chan - 6 + i * 4;
+
+ if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
+ return NOT_ALLOWED;
+
+ if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
+ (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
+ (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
+ (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
+ return NOT_ALLOWED;
+
+ if (flags & HOSTAPD_CHAN_NO_IR)
+ no_ir = 1;
+ }
+
+ if (no_ir)
+ return NO_IR;
+
+ return ALLOWED;
+}
+
+
+static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+ u8 center_channels[] = { 50, 114 };
+ unsigned int i;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
+ /*
+ * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
+ * so the center channel is 14 channels away from the start/end.
+ */
+ if (channel >= center_channels[i] - 14 &&
+ channel <= center_channels[i] + 14)
+ return center_channels[i];
+ }
+
+ return 0;
+}
+
+
+static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
+ u8 channel)
+{
+ u8 center_chan;
+ unsigned int i;
+ unsigned int no_ir = 0;
+
+ center_chan = get_center_160mhz(mode, channel);
+ if (!center_chan)
+ return NOT_ALLOWED;
+
+ /* Check all the channels are available */
+ for (i = 0; i < 8; i++) {
+ unsigned int flags;
+ u8 adj_chan = center_chan - 14 + i * 4;
+
+ if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
+ return NOT_ALLOWED;
+
+ if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
+ (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
+ (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
+ (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
+ (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
+ (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
+ (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
+ (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
+ return NOT_ALLOWED;
+
+ if (flags & HOSTAPD_CHAN_NO_IR)
+ no_ir = 1;
+ }
+
+ if (no_ir)
+ return NO_IR;
+
+ return ALLOWED;
+}
+
+
+enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel,
+ u8 bw)
+{
+ unsigned int flag = 0;
+ enum chan_allowed res, res2;
+
+ res2 = res = allow_channel(mode, channel, &flag);
+ if (bw == BW40MINUS) {
+ if (!(flag & HOSTAPD_CHAN_HT40MINUS))
+ return NOT_ALLOWED;
+ res2 = allow_channel(mode, channel - 4, NULL);
+ } else if (bw == BW40PLUS) {
+ if (!(flag & HOSTAPD_CHAN_HT40PLUS))
+ return NOT_ALLOWED;
+ res2 = allow_channel(mode, channel + 4, NULL);
+ } else if (bw == BW80) {
+ /*
+ * channel is a center channel and as such, not necessarily a
+ * valid 20 MHz channels. Override earlier allow_channel()
+ * result and use only the 80 MHz specific version.
+ */
+ res2 = res = verify_80mhz(mode, channel);
+ } else if (bw == BW160) {
+ /*
+ * channel is a center channel and as such, not necessarily a
+ * valid 20 MHz channels. Override earlier allow_channel()
+ * result and use only the 160 MHz specific version.
+ */
+ res2 = res = verify_160mhz(mode, channel);
+ } else if (bw == BW80P80) {
+ /*
+ * channel is a center channel and as such, not necessarily a
+ * valid 20 MHz channels. Override earlier allow_channel()
+ * result and use only the 80 MHz specific version.
+ */
+ res2 = res = verify_80mhz(mode, channel);
+ }
+
+ if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
+ return NOT_ALLOWED;
+
+ if (res == NO_IR || res2 == NO_IR)
+ return NO_IR;
+
+ return ALLOWED;
+}
+
+
+static int wpas_op_class_supported(struct wpa_supplicant *wpa_s,
+ const struct oper_class_map *op_class)
+{
+ int chan;
+ size_t i;
+ struct hostapd_hw_modes *mode;
+ int found;
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode);
+ if (!mode)
+ return 0;
+
+ if (op_class->op_class == 128) {
+ u8 channels[] = { 42, 58, 106, 122, 138, 155 };
+
+ for (i = 0; i < ARRAY_SIZE(channels); i++) {
+ if (verify_channel(mode, channels[i], op_class->bw) !=
+ NOT_ALLOWED)
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if (op_class->op_class == 129) {
+ /* Check if either 160 MHz channels is allowed */
+ return verify_channel(mode, 50, op_class->bw) != NOT_ALLOWED ||
+ verify_channel(mode, 114, op_class->bw) != NOT_ALLOWED;
+ }
+
+ if (op_class->op_class == 130) {
+ /* Need at least two non-contiguous 80 MHz segments */
+ found = 0;
+
+ if (verify_channel(mode, 42, op_class->bw) != NOT_ALLOWED ||
+ verify_channel(mode, 58, op_class->bw) != NOT_ALLOWED)
+ found++;
+ if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED ||
+ verify_channel(mode, 122, op_class->bw) != NOT_ALLOWED ||
+ verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED)
+ found++;
+ if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED &&
+ verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED)
+ found++;
+ if (verify_channel(mode, 155, op_class->bw) != NOT_ALLOWED)
+ found++;
+
+ if (found >= 2)
+ return 1;
+
+ return 0;
+ }
+
+ found = 0;
+ for (chan = op_class->min_chan; chan <= op_class->max_chan;
+ chan += op_class->inc) {
+ if (verify_channel(mode, chan, op_class->bw) != NOT_ALLOWED) {
+ found = 1;
+ break;
+ }
+ }
+
+ return found;
+}
+
+
+size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
+ size_t len)
+{
+ struct wpabuf *buf;
+ u8 op, current, chan;
+ u8 *ie_len;
+ size_t res;
+
+ /*
+ * Assume 20 MHz channel for now.
+ * TODO: Use the secondary channel and VHT channel width that will be
+ * used after association.
+ */
+ if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
+ &current, &chan) == NUM_HOSTAPD_MODES)
+ return 0;
+
+ /*
+ * Need 3 bytes for EID, length, and current operating class, plus
+ * 1 byte for every other supported operating class.
+ */
+ buf = wpabuf_alloc(global_op_class_size + 3);
+ if (!buf)
+ return 0;
+
+ wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES);
+ /* Will set the length later, putting a placeholder */
+ ie_len = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, current);
+
+ for (op = 0; global_op_class[op].op_class; op++) {
+ if (wpas_op_class_supported(wpa_s, &global_op_class[op]))
+ wpabuf_put_u8(buf, global_op_class[op].op_class);
+ }
+
+ *ie_len = wpabuf_len(buf) - 2;
+ if (*ie_len < 2 || wpabuf_len(buf) > len) {
+ wpa_printf(MSG_ERROR,
+ "Failed to add supported operating classes IE");
+ res = 0;
+ } else {
+ os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
+ res = wpabuf_len(buf);
+ wpa_hexdump_buf(MSG_DEBUG,
+ "Added supported operating classes IE", buf);
+ }
+
+ wpabuf_free(buf);
+ return res;
+}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b1fdc2837ff0..c596d5ab6148 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -307,7 +307,14 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
return;
}
+ if (wpa_s->clear_driver_scan_cache) {
+ wpa_printf(MSG_DEBUG,
+ "Request driver to clear scan cache due to local BSS flush");
+ params->only_new_results = 1;
+ }
ret = wpa_drv_scan(wpa_s, params);
+ if (ret == 0)
+ wpa_s->curr_scan_cookie = params->scan_cookie;
wpa_scan_free_params(params);
work->ctx = NULL;
if (ret) {
@@ -320,6 +327,7 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
os_get_reltime(&wpa_s->scan_trigger_time);
wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
wpa_s->own_scan_requested = 1;
+ wpa_s->clear_driver_scan_cache = 0;
wpa_s->p2p_scan_work = work;
}
@@ -740,6 +748,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
conncap = P2PS_SETUP_GROUP_OWNER;
goto grp_owner;
}
+ /* fall through */
default:
return P2PS_SETUP_NONE;
@@ -807,7 +816,7 @@ grp_owner:
wpa_s->own_addr);
} else if (!s && !go_wpa_s) {
if (wpas_p2p_add_group_interface(wpa_s,
- WPA_IF_P2P_GO) < 0) {
+ WPA_IF_P2P_GROUP) < 0) {
wpa_printf(MSG_ERROR,
"P2P: Failed to allocate a new interface for the group");
return P2PS_SETUP_NONE;
@@ -1312,6 +1321,10 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
if (wpa_s->p2p_go_group_formation_completed) {
wpa_s->global->p2p_group_formation = NULL;
wpa_s->p2p_in_provisioning = 0;
+ } else if (wpa_s->p2p_in_provisioning && !success) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "P2P: Stop provisioning state due to failure");
+ wpa_s->p2p_in_provisioning = 0;
}
wpa_s->p2p_in_invitation = 0;
wpa_s->group_formation_reported = 1;
@@ -1383,7 +1396,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
}
if (!client) {
- wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0);
+ wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0, NULL);
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
}
}
@@ -1701,14 +1714,23 @@ static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s,
static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s)
{
+ char buf[20 + P2P_MAX_CHANNELS * 6];
+ char *pos, *end;
unsigned int i;
+ int res;
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies (len=%u):",
- wpa_s->p2p_group_common_freqs_num);
+ pos = buf;
+ end = pos + sizeof(buf);
+ for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+ res = os_snprintf(pos, end - pos, " %d",
+ wpa_s->p2p_group_common_freqs[i]);
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+ *pos = '\0';
- for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++)
- wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d",
- i, wpa_s->p2p_group_common_freqs[i]);
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies:%s", buf);
}
@@ -1801,7 +1823,8 @@ static void p2p_go_configured(void *ctx, void *data)
}
wpas_notify_p2p_group_started(wpa_s, ssid,
- params->persistent_group, 0);
+ params->persistent_group, 0,
+ NULL);
wpas_p2p_cross_connect_setup(wpa_s);
wpas_p2p_set_group_idle_timeout(wpa_s);
@@ -1989,6 +2012,11 @@ do { \
d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
}
d->p2p_cli_probe = s->p2p_cli_probe;
+ d->go_interworking = s->go_interworking;
+ d->go_access_network_type = s->go_access_network_type;
+ d->go_internet = s->go_internet;
+ d->go_venue_group = s->go_venue_group;
+ d->go_venue_type = s->go_venue_type;
}
@@ -3331,10 +3359,6 @@ static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
}
-enum chan_allowed {
- NOT_ALLOWED, NO_IR, ALLOWED
-};
-
static int has_channel(struct wpa_global *global,
struct hostapd_hw_modes *mode, u8 chan, int *flags)
{
@@ -5003,6 +5027,12 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
params.extra_ies = wpabuf_head(ies);
params.extra_ies_len = wpabuf_len(ies);
+ if (wpa_s->clear_driver_scan_cache) {
+ wpa_printf(MSG_DEBUG,
+ "Request driver to clear scan cache due to local BSS flush");
+ params.only_new_results = 1;
+ }
+
/*
* Run a scan to update BSS table and start Provision Discovery once
* the new scan results become available.
@@ -5012,6 +5042,7 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
os_get_reltime(&wpa_s->scan_trigger_time);
wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
wpa_s->own_scan_requested = 1;
+ wpa_s->clear_driver_scan_cache = 0;
}
wpabuf_free(ies);
@@ -5183,7 +5214,8 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
if (!ret) {
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
- ieee80211_is_dfs(freq)) {
+ ieee80211_is_dfs(freq, wpa_s->hw.modes,
+ wpa_s->hw.num_modes)) {
/*
* If freq is a DFS channel and DFS is offloaded
* to the driver, allow P2P GO to use it.
@@ -5236,9 +5268,11 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
if (!res && max_pref_freq > 0) {
*num_pref_freq = max_pref_freq;
i = 0;
- while (wpas_p2p_disallowed_freq(wpa_s->global,
- pref_freq_list[i]) &&
- i < *num_pref_freq) {
+ while (i < *num_pref_freq &&
+ (!p2p_supported_freq(wpa_s->global->p2p,
+ pref_freq_list[i]) ||
+ wpas_p2p_disallowed_freq(wpa_s->global,
+ pref_freq_list[i]))) {
wpa_printf(MSG_DEBUG,
"P2P: preferred_freq_list[%d]=%d is disallowed",
i, pref_freq_list[i]);
@@ -5601,9 +5635,11 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
&size, pref_freq_list);
if (!res && size > 0) {
i = 0;
- while (wpas_p2p_disallowed_freq(wpa_s->global,
- pref_freq_list[i]) &&
- i < size) {
+ while (i < size &&
+ (!p2p_supported_freq(wpa_s->global->p2p,
+ pref_freq_list[i]) ||
+ wpas_p2p_disallowed_freq(wpa_s->global,
+ pref_freq_list[i]))) {
wpa_printf(MSG_DEBUG,
"P2P: preferred_freq_list[%d]=%d is disallowed",
i, pref_freq_list[i]);
@@ -5667,7 +5703,8 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
- ieee80211_is_dfs(freq)) {
+ ieee80211_is_dfs(freq, wpa_s->hw.modes,
+ wpa_s->hw.num_modes)) {
/*
* If freq is a DFS channel and DFS is offloaded to the
* driver, allow P2P GO to use it.
@@ -5705,30 +5742,6 @@ static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s,
{
unsigned int i, r;
- /* first try some random selection of the social channels */
- if (os_get_random((u8 *) &r, sizeof(r)) < 0)
- return;
-
- for (i = 0; i < 3; i++) {
- params->freq = 2412 + ((r + i) % 3) * 25;
- if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
- goto out;
- }
-
- /* try all other channels in operating class 81 */
- for (i = 0; i < 11; i++) {
- params->freq = 2412 + i * 5;
-
- /* skip social channels; covered in the previous loop */
- if (params->freq == 2412 ||
- params->freq == 2437 ||
- params->freq == 2462)
- continue;
-
- if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
- goto out;
- }
-
/* try all channels in operating class 115 */
for (i = 0; i < 4; i++) {
params->freq = 5180 + i * 20;
@@ -5763,6 +5776,30 @@ static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s,
goto out;
}
+ /* try some random selection of the social channels */
+ if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+ return;
+
+ for (i = 0; i < 3; i++) {
+ params->freq = 2412 + ((r + i) % 3) * 25;
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
+ goto out;
+ }
+
+ /* try all other channels in operating class 81 */
+ for (i = 0; i < 11; i++) {
+ params->freq = 2412 + i * 5;
+
+ /* skip social channels; covered in the previous loop */
+ if (params->freq == 2412 ||
+ params->freq == 2437 ||
+ params->freq == 2462)
+ continue;
+
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
+ goto out;
+ }
+
params->freq = 0;
wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
return;
@@ -5772,6 +5809,19 @@ out:
}
+static int wpas_same_band(int freq1, int freq2)
+{
+ enum hostapd_hw_mode mode1, mode2;
+ u8 chan1, chan2;
+
+ mode1 = ieee80211_freq_to_chan(freq1, &chan1);
+ mode2 = ieee80211_freq_to_chan(freq2, &chan2);
+ if (mode1 == NUM_HOSTAPD_MODES)
+ return 0;
+ return mode1 == mode2;
+}
+
+
static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params,
int freq, int vht_center_freq2, int ht40,
@@ -5822,12 +5872,31 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
/* try using the forced freq */
if (freq) {
- if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
+ if (wpas_p2p_disallowed_freq(wpa_s->global, freq) ||
+ !freq_included(wpa_s, channels, freq)) {
wpa_printf(MSG_DEBUG,
- "P2P: Forced GO freq %d MHz not accepted",
+ "P2P: Forced GO freq %d MHz disallowed",
freq);
goto fail;
}
+ if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ ieee80211_is_dfs(freq, wpa_s->hw.modes,
+ wpa_s->hw.num_modes)) {
+ /*
+ * If freq is a DFS channel and DFS is offloaded
+ * to the driver, allow P2P GO to use it.
+ */
+ wpa_printf(MSG_DEBUG,
+ "P2P: %s: The forced channel for GO (%u MHz) requires DFS and DFS is offloaded",
+ __func__, freq);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: The forced channel for GO (%u MHz) is not supported for P2P uses",
+ freq);
+ goto fail;
+ }
+ }
for (i = 0; i < num; i++) {
if (freqs[i].freq == freq) {
@@ -5953,6 +6022,80 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
goto success;
}
+ /* Try using a channel that allows VHT to be used with 80 MHz */
+ if (wpa_s->hw.modes && wpa_s->p2p_group_common_freqs) {
+ for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+ enum hostapd_hw_mode mode;
+ struct hostapd_hw_modes *hwmode;
+ u8 chan;
+
+ cand = wpa_s->p2p_group_common_freqs[i];
+ mode = ieee80211_freq_to_chan(cand, &chan);
+ hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+ mode);
+ if (!hwmode ||
+ wpas_p2p_verify_channel(wpa_s, hwmode, chan,
+ BW80) != ALLOWED)
+ continue;
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ params->freq = cand;
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use freq %d MHz common with the peer and allowing VHT80",
+ params->freq);
+ goto success;
+ }
+ }
+ }
+
+ /* Try using a channel that allows HT to be used with 40 MHz on the same
+ * band so that CSA can be used */
+ if (wpa_s->current_ssid && wpa_s->hw.modes &&
+ wpa_s->p2p_group_common_freqs) {
+ for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+ enum hostapd_hw_mode mode;
+ struct hostapd_hw_modes *hwmode;
+ u8 chan;
+
+ cand = wpa_s->p2p_group_common_freqs[i];
+ mode = ieee80211_freq_to_chan(cand, &chan);
+ hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+ mode);
+ if (!wpas_same_band(wpa_s->current_ssid->frequency,
+ cand) ||
+ !hwmode ||
+ (wpas_p2p_verify_channel(wpa_s, hwmode, chan,
+ BW40MINUS) != ALLOWED &&
+ wpas_p2p_verify_channel(wpa_s, hwmode, chan,
+ BW40PLUS) != ALLOWED))
+ continue;
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ params->freq = cand;
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use freq %d MHz common with the peer, allowing HT40, and maintaining same band",
+ params->freq);
+ goto success;
+ }
+ }
+ }
+
+ /* Try using one of the group common freqs on the same band so that CSA
+ * can be used */
+ if (wpa_s->current_ssid && wpa_s->p2p_group_common_freqs) {
+ for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+ cand = wpa_s->p2p_group_common_freqs[i];
+ if (!wpas_same_band(wpa_s->current_ssid->frequency,
+ cand))
+ continue;
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ params->freq = cand;
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use freq %d MHz common with the peer and maintaining same band",
+ params->freq);
+ goto success;
+ }
+ }
+ }
+
/* Try using one of the group common freqs */
if (wpa_s->p2p_group_common_freqs) {
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
@@ -6022,6 +6165,12 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
return NULL;
}
+ if (go && wpa_s->p2p_go_do_acs) {
+ group_wpa_s->p2p_go_do_acs = wpa_s->p2p_go_do_acs;
+ group_wpa_s->p2p_go_acs_band = wpa_s->p2p_go_acs_band;
+ wpa_s->p2p_go_do_acs = 0;
+ }
+
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
group_wpa_s->ifname);
group_wpa_s->p2p_first_connection_timeout = 0;
@@ -6059,31 +6208,16 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
wpas_p2p_stop_find_oper(wpa_s);
- freq = wpas_p2p_select_go_freq(wpa_s, freq);
- if (freq < 0)
- return -1;
+ if (!wpa_s->p2p_go_do_acs) {
+ freq = wpas_p2p_select_go_freq(wpa_s, freq);
+ if (freq < 0)
+ return -1;
+ }
if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
ht40, vht, max_oper_chwidth, NULL))
return -1;
- if (params.freq &&
- !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
- if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
- ieee80211_is_dfs(params.freq)) {
- /*
- * If freq is a DFS channel and DFS is offloaded to the
- * driver, allow P2P GO to use it.
- */
- wpa_printf(MSG_DEBUG,
- "P2P: %s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to driver",
- __func__, params.freq);
- } else {
- wpa_printf(MSG_DEBUG,
- "P2P: The selected channel for GO (%u MHz) is not supported for P2P uses",
- params.freq);
- return -1;
- }
- }
+
p2p_go_params(wpa_s->global->p2p, &params);
params.persistent_group = persistent_group;
@@ -6559,8 +6693,14 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
wpa_s->p2p_long_listen = 0;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
- wpa_s->p2p_in_provisioning)
+ wpa_s->p2p_in_provisioning) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Reject p2p_find operation%s%s",
+ (wpa_s->global->p2p_disabled || !wpa_s->global->p2p) ?
+ " (P2P disabled)" : "",
+ wpa_s->p2p_in_provisioning ?
+ " (p2p_in_provisioning)" : "");
return -1;
+ }
wpa_supplicant_cancel_sched_scan(wpa_s);
@@ -7005,7 +7145,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
wpas_p2p_store_persistent_group(wpa_s->p2pdev,
ssid, go_dev_addr);
- wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1);
+ wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip);
}
@@ -9041,16 +9181,20 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
unsigned int timeout;
int freq;
+ int dfs_offload;
wpas_p2p_go_update_common_freqs(wpa_s);
freq = wpa_s->current_ssid->frequency;
+ dfs_offload = (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes);
for (i = 0, invalid_freq = 0; i < num; i++) {
if (freqs[i].freq == freq) {
flags = freqs[i].flags;
/* The channel is invalid, must change it */
- if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+ if (!p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
+ !dfs_offload) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Freq=%d MHz no longer valid for GO",
freq);
@@ -9060,7 +9204,7 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
/* Freq is not used by any other station interface */
continue;
} else if (!p2p_supported_freq(wpa_s->global->p2p,
- freqs[i].freq)) {
+ freqs[i].freq) && !dfs_offload) {
/* Freq is not valid for P2P use cases */
continue;
} else if (wpa_s->conf->p2p_go_freq_change_policy ==
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index f4bba98e2a82..f2fff550aa81 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -143,16 +143,19 @@ static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
}
-static int wpa_supplicant_add_pmkid(void *wpa_s,
- const u8 *bssid, const u8 *pmkid)
+static int wpa_supplicant_add_pmkid(void *wpa_s, void *network_ctx,
+ const u8 *bssid, const u8 *pmkid,
+ const u8 *fils_cache_id,
+ const u8 *pmk, size_t pmk_len)
{
printf("%s - not implemented\n", __func__);
return -1;
}
-static int wpa_supplicant_remove_pmkid(void *wpa_s,
- const u8 *bssid, const u8 *pmkid)
+static int wpa_supplicant_remove_pmkid(void *wpa_s, void *network_ctx,
+ const u8 *bssid, const u8 *pmkid,
+ const u8 *fils_cache_id)
{
printf("%s - not implemented\n", __func__);
return -1;
@@ -344,8 +347,8 @@ int main(int argc, char *argv[])
if (preauth_test.auth_timed_out)
ret = -2;
else {
- ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0)
- ? 0 : -3;
+ ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0,
+ NULL, 0) ? 0 : -3;
}
test_eapol_clean(&wpa_s);
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
new file mode 100644
index 000000000000..f4fbfa719352
--- /dev/null
+++ b/wpa_supplicant/rrm.c
@@ -0,0 +1,1460 @@
+/*
+ * wpa_supplicant - Radio Measurements
+ * Copyright (c) 2003-2016, 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 "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+#include "scan.h"
+#include "p2p_supplicant.h"
+
+
+static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+{
+ struct rrm_data *rrm = data;
+
+ if (!rrm->notify_neighbor_rep) {
+ wpa_printf(MSG_ERROR,
+ "RRM: Unexpected neighbor report timeout");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
+ rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
+
+ rrm->notify_neighbor_rep = NULL;
+ rrm->neighbor_rep_cb_ctx = NULL;
+}
+
+
+/*
+ * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
+ * @wpa_s: Pointer to wpa_supplicant
+ */
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->rrm.rrm_used = 0;
+
+ eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+ NULL);
+ if (wpa_s->rrm.notify_neighbor_rep)
+ wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+ wpa_s->rrm.next_neighbor_rep_token = 1;
+ wpas_clear_beacon_rep_data(wpa_s);
+}
+
+
+/*
+ * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
+ * @wpa_s: Pointer to wpa_supplicant
+ * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
+ * @report_len: Length of neighbor report buffer
+ */
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+ const u8 *report, size_t report_len)
+{
+ struct wpabuf *neighbor_rep;
+
+ wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
+ if (report_len < 1)
+ return;
+
+ if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Discarding neighbor report with token %d (expected %d)",
+ report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
+ return;
+ }
+
+ eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+ NULL);
+
+ if (!wpa_s->rrm.notify_neighbor_rep) {
+ wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+ return;
+ }
+
+ /* skipping the first byte, which is only an id (dialog token) */
+ neighbor_rep = wpabuf_alloc(report_len - 1);
+ if (!neighbor_rep) {
+ wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+ return;
+ }
+ wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
+ wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+ report[0]);
+ wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
+ neighbor_rep);
+ wpa_s->rrm.notify_neighbor_rep = NULL;
+ wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+}
+
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+/* Workaround different, undefined for Windows, error codes used here */
+#define ENOTCONN -1
+#define EOPNOTSUPP -1
+#define ECANCELED -1
+#endif
+
+/* Measurement Request element + Location Subject + Maximum Age subelement */
+#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4)
+/* Measurement Request element + Location Civic Request */
+#define MEASURE_REQUEST_CIVIC_LEN (3 + 5)
+
+
+/**
+ * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
+ * is sent in the request.
+ * @lci: if set, neighbor request will include LCI request
+ * @civic: if set, neighbor request will include civic location request
+ * @cb: Callback function to be called once the requested report arrives, or
+ * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
+ * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
+ * the requester's responsibility to free it.
+ * In the latter case NULL will be sent in 'neighbor_rep'.
+ * @cb_ctx: Context value to send the callback function
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ * In case there is a previous request which has not been answered yet, the
+ * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
+ * Request must contain a callback function.
+ */
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid_value *ssid,
+ int lci, int civic,
+ void (*cb)(void *ctx,
+ struct wpabuf *neighbor_rep),
+ void *cb_ctx)
+{
+ struct wpabuf *buf;
+ const u8 *rrm_ie;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+ return -ENOTCONN;
+ }
+
+ if (!wpa_s->rrm.rrm_used) {
+ wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+ return -EOPNOTSUPP;
+ }
+
+ rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
+ WLAN_EID_RRM_ENABLED_CAPABILITIES);
+ if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
+ !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: No network support for Neighbor Report.");
+ return -EOPNOTSUPP;
+ }
+
+ /* Refuse if there's a live request */
+ if (wpa_s->rrm.notify_neighbor_rep) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Currently handling previous Neighbor Report.");
+ return -EBUSY;
+ }
+
+ /* 3 = action category + action code + dialog token */
+ buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) +
+ (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) +
+ (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0));
+ if (buf == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Failed to allocate Neighbor Report Request");
+ return -ENOMEM;
+ }
+
+ wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
+ (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
+ wpa_s->rrm.next_neighbor_rep_token);
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
+ wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
+ if (ssid) {
+ wpabuf_put_u8(buf, WLAN_EID_SSID);
+ wpabuf_put_u8(buf, ssid->ssid_len);
+ wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
+ }
+
+ if (lci) {
+ /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+ wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN);
+
+ /*
+ * Measurement token; nonzero number that is unique among the
+ * Measurement Request elements in a particular frame.
+ */
+ wpabuf_put_u8(buf, 1); /* Measurement Token */
+
+ /*
+ * Parallel, Enable, Request, and Report bits are 0, Duration is
+ * reserved.
+ */
+ wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+ wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */
+
+ /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */
+ /* Location Subject */
+ wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+
+ /* Optional Subelements */
+ /*
+ * IEEE P802.11-REVmc/D5.0 Figure 9-170
+ * The Maximum Age subelement is required, otherwise the AP can
+ * send only data that was determined after receiving the
+ * request. Setting it here to unlimited age.
+ */
+ wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
+ wpabuf_put_u8(buf, 2);
+ wpabuf_put_le16(buf, 0xffff);
+ }
+
+ if (civic) {
+ /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+ wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN);
+
+ /*
+ * Measurement token; nonzero number that is unique among the
+ * Measurement Request elements in a particular frame.
+ */
+ wpabuf_put_u8(buf, 2); /* Measurement Token */
+
+ /*
+ * Parallel, Enable, Request, and Report bits are 0, Duration is
+ * reserved.
+ */
+ wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+ /* Measurement Type */
+ wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC);
+
+ /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14:
+ * Location Civic request */
+ /* Location Subject */
+ wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+ wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */
+ /* Location Service Interval Units: Seconds */
+ wpabuf_put_u8(buf, 0);
+ /* Location Service Interval: 0 - Only one report is requested
+ */
+ wpabuf_put_le16(buf, 0);
+ /* No optional subelements */
+ }
+
+ wpa_s->rrm.next_neighbor_rep_token++;
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Failed to send Neighbor Report Request");
+ wpabuf_free(buf);
+ return -ECANCELED;
+ }
+
+ wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
+ wpa_s->rrm.notify_neighbor_rep = cb;
+ eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
+ wpas_rrm_neighbor_rep_timeout_handler,
+ &wpa_s->rrm, NULL);
+
+ wpabuf_free(buf);
+ return 0;
+}
+
+
+static int wpas_rrm_report_elem(struct wpabuf **buf, u8 token, u8 mode, u8 type,
+ const u8 *data, size_t data_len)
+{
+ if (wpabuf_resize(buf, 5 + data_len))
+ return -1;
+
+ wpabuf_put_u8(*buf, WLAN_EID_MEASURE_REPORT);
+ wpabuf_put_u8(*buf, 3 + data_len);
+ wpabuf_put_u8(*buf, token);
+ wpabuf_put_u8(*buf, mode);
+ wpabuf_put_u8(*buf, type);
+
+ if (data_len)
+ wpabuf_put_data(*buf, data, data_len);
+
+ return 0;
+}
+
+
+static int
+wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s,
+ const struct rrm_measurement_request_element *req,
+ struct wpabuf **buf)
+{
+ u8 subject;
+ u16 max_age = 0;
+ struct os_reltime t, diff;
+ unsigned long diff_l;
+ const u8 *subelem;
+ const u8 *request = req->variable;
+ size_t len = req->len - 3;
+
+ if (len < 1)
+ return -1;
+
+ if (!wpa_s->lci)
+ goto reject;
+
+ subject = *request++;
+ len--;
+
+ wpa_printf(MSG_DEBUG, "Measurement request location subject=%u",
+ subject);
+
+ if (subject != LOCATION_SUBJECT_REMOTE) {
+ wpa_printf(MSG_INFO,
+ "Not building LCI report - bad location subject");
+ return 0;
+ }
+
+ /* Subelements are formatted exactly like elements */
+ wpa_hexdump(MSG_DEBUG, "LCI request subelements", request, len);
+ subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE);
+ if (subelem && subelem[1] == 2)
+ max_age = WPA_GET_LE16(subelem + 2);
+
+ if (os_get_reltime(&t))
+ goto reject;
+
+ os_reltime_sub(&t, &wpa_s->lci_time, &diff);
+ /* LCI age is calculated in 10th of a second units. */
+ diff_l = diff.sec * 10 + diff.usec / 100000;
+
+ if (max_age != 0xffff && max_age < diff_l)
+ goto reject;
+
+ if (wpas_rrm_report_elem(buf, req->token,
+ MEASUREMENT_REPORT_MODE_ACCEPT, req->type,
+ wpabuf_head_u8(wpa_s->lci),
+ wpabuf_len(wpa_s->lci)) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to add LCI report element");
+ return -1;
+ }
+
+ return 0;
+
+reject:
+ if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+ wpas_rrm_report_elem(buf, req->token,
+ MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
+ req->type, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s,
+ const u8 *data, size_t len)
+{
+ struct wpabuf *report = wpabuf_alloc(len + 3);
+
+ if (!report)
+ return;
+
+ wpabuf_put_u8(report, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(report, WLAN_RRM_RADIO_MEASUREMENT_REPORT);
+ wpabuf_put_u8(report, wpa_s->rrm.token);
+
+ wpabuf_put_data(report, data, len);
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(report), wpabuf_len(report), 0)) {
+ wpa_printf(MSG_ERROR,
+ "RRM: Radio measurement report failed: Sending Action frame failed");
+ }
+
+ wpabuf_free(report);
+}
+
+
+static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s,
+ struct wpabuf *buf)
+{
+ int len = wpabuf_len(buf);
+ const u8 *pos = wpabuf_head_u8(buf), *next = pos;
+
+#define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3)
+
+ while (len) {
+ int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len;
+
+ if (send_len == len ||
+ (send_len + next[1] + 2) > MPDU_REPORT_LEN) {
+ wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len);
+ len -= send_len;
+ pos = next;
+ }
+
+ if (len)
+ next += next[1] + 2;
+ }
+#undef MPDU_REPORT_LEN
+}
+
+
+static int wpas_add_channel(u8 op_class, u8 chan, u8 num_primary_channels,
+ int *freqs)
+{
+ size_t i;
+
+ for (i = 0; i < num_primary_channels; i++) {
+ u8 primary_chan = chan - (2 * num_primary_channels - 2) + i * 4;
+
+ freqs[i] = ieee80211_chan_to_freq(NULL, op_class, primary_chan);
+ /* ieee80211_chan_to_freq() is not really meant for this
+ * conversion of 20 MHz primary channel numbers for wider VHT
+ * channels, so handle those as special cases here for now. */
+ if (freqs[i] < 0 &&
+ (op_class == 128 || op_class == 129 || op_class == 130))
+ freqs[i] = 5000 + 5 * primary_chan;
+ if (freqs[i] < 0) {
+ wpa_printf(MSG_DEBUG,
+ "Beacon Report: Invalid channel %u",
+ chan);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int * wpas_add_channels(const struct oper_class_map *op,
+ struct hostapd_hw_modes *mode, int active,
+ const u8 *channels, const u8 size)
+{
+ int *freqs, *next_freq;
+ u8 num_primary_channels, i;
+ u8 num_chans;
+
+ num_chans = channels ? size :
+ (op->max_chan - op->min_chan) / op->inc + 1;
+
+ if (op->bw == BW80 || op->bw == BW80P80)
+ num_primary_channels = 4;
+ else if (op->bw == BW160)
+ num_primary_channels = 8;
+ else
+ num_primary_channels = 1;
+
+ /* one extra place for the zero-terminator */
+ freqs = os_calloc(num_chans * num_primary_channels + 1, sizeof(*freqs));
+ if (!freqs) {
+ wpa_printf(MSG_ERROR,
+ "Beacon Report: Failed to allocate freqs array");
+ return NULL;
+ }
+
+ next_freq = freqs;
+ for (i = 0; i < num_chans; i++) {
+ u8 chan = channels ? channels[i] : op->min_chan + i * op->inc;
+ enum chan_allowed res = verify_channel(mode, chan, op->bw);
+
+ if (res == NOT_ALLOWED || (res == NO_IR && active))
+ continue;
+
+ if (wpas_add_channel(op->op_class, chan, num_primary_channels,
+ next_freq) < 0) {
+ os_free(freqs);
+ return NULL;
+ }
+
+ next_freq += num_primary_channels;
+ }
+
+ if (!freqs[0]) {
+ os_free(freqs);
+ return NULL;
+ }
+
+ return freqs;
+}
+
+
+static int * wpas_op_class_freqs(const struct oper_class_map *op,
+ struct hostapd_hw_modes *mode, int active)
+{
+ u8 channels_80mhz[] = { 42, 58, 106, 122, 138, 155 };
+ u8 channels_160mhz[] = { 50, 114 };
+
+ /*
+ * When adding all channels in the operating class, 80 + 80 MHz
+ * operating classes are like 80 MHz channels because we add all valid
+ * channels anyway.
+ */
+ if (op->bw == BW80 || op->bw == BW80P80)
+ return wpas_add_channels(op, mode, active, channels_80mhz,
+ ARRAY_SIZE(channels_80mhz));
+
+ if (op->bw == BW160)
+ return wpas_add_channels(op, mode, active, channels_160mhz,
+ ARRAY_SIZE(channels_160mhz));
+
+ return wpas_add_channels(op, mode, active, NULL, 0);
+}
+
+
+static int * wpas_channel_report_freqs(struct wpa_supplicant *wpa_s, int active,
+ const char *country, const u8 *subelems,
+ size_t len)
+{
+ int *freqs = NULL, *new_freqs;
+ const u8 *end = subelems + len;
+
+ while (end - subelems > 2) {
+ const struct oper_class_map *op;
+ const u8 *ap_chan_elem, *pos;
+ u8 left;
+ struct hostapd_hw_modes *mode;
+
+ ap_chan_elem = get_ie(subelems, end - subelems,
+ WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL);
+ if (!ap_chan_elem)
+ break;
+ pos = ap_chan_elem + 2;
+ left = ap_chan_elem[1];
+ if (left < 1)
+ break;
+ subelems = ap_chan_elem + 2 + left;
+
+ op = get_oper_class(country, *pos);
+ if (!op) {
+ wpa_printf(MSG_DEBUG,
+ "Beacon request: unknown operating class in AP Channel Report subelement %u",
+ *pos);
+ goto out;
+ }
+ pos++;
+ left--;
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode);
+ if (!mode)
+ continue;
+
+ /*
+ * For 80 + 80 MHz operating classes, this AP Channel Report
+ * element should be followed by another element specifying
+ * the second 80 MHz channel. For now just add this 80 MHz
+ * channel, the second 80 MHz channel will be added when the
+ * next element is parsed.
+ * TODO: Verify that this AP Channel Report element is followed
+ * by a corresponding AP Channel Report element as specified in
+ * IEEE Std 802.11-2016, 11.11.9.1.
+ */
+ new_freqs = wpas_add_channels(op, mode, active, pos, left);
+ if (new_freqs)
+ int_array_concat(&freqs, new_freqs);
+
+ os_free(new_freqs);
+ }
+
+ return freqs;
+out:
+ os_free(freqs);
+ return NULL;
+}
+
+
+static int * wpas_beacon_request_freqs(struct wpa_supplicant *wpa_s,
+ u8 op_class, u8 chan, int active,
+ const u8 *subelems, size_t len)
+{
+ int *freqs = NULL, *ext_freqs = NULL;
+ struct hostapd_hw_modes *mode;
+ const char *country = NULL;
+ const struct oper_class_map *op;
+ const u8 *elem;
+
+ if (!wpa_s->current_bss)
+ return NULL;
+ elem = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
+ if (elem && elem[1] >= 2)
+ country = (const char *) (elem + 2);
+
+ op = get_oper_class(country, op_class);
+ if (!op) {
+ wpa_printf(MSG_DEBUG,
+ "Beacon request: invalid operating class %d",
+ op_class);
+ return NULL;
+ }
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode);
+ if (!mode)
+ return NULL;
+
+ switch (chan) {
+ case 0:
+ freqs = wpas_op_class_freqs(op, mode, active);
+ if (!freqs)
+ return NULL;
+ break;
+ case 255:
+ /* freqs will be added from AP channel subelements */
+ break;
+ default:
+ freqs = wpas_add_channels(op, mode, active, &chan, 1);
+ if (!freqs)
+ return NULL;
+ break;
+ }
+
+ ext_freqs = wpas_channel_report_freqs(wpa_s, active, country, subelems,
+ len);
+ if (ext_freqs) {
+ int_array_concat(&freqs, ext_freqs);
+ os_free(ext_freqs);
+ int_array_sort_unique(freqs);
+ }
+
+ return freqs;
+}
+
+
+static int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len,
+ u8 *op_class, u8 *chan, u8 *phy_type)
+{
+ const u8 *ie;
+ int sec_chan = 0, vht = 0;
+ struct ieee80211_ht_operation *ht_oper = NULL;
+ struct ieee80211_vht_operation *vht_oper = NULL;
+ u8 seg0, seg1;
+
+ ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
+ if (ie && ie[1] >= sizeof(struct ieee80211_ht_operation)) {
+ u8 sec_chan_offset;
+
+ ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
+ sec_chan_offset = ht_oper->ht_param &
+ HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+ if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+ sec_chan = 1;
+ else if (sec_chan_offset ==
+ HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+ sec_chan = -1;
+ }
+
+ ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
+ if (ie && ie[1] >= sizeof(struct ieee80211_vht_operation)) {
+ vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
+
+ switch (vht_oper->vht_op_info_chwidth) {
+ case 1:
+ seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx;
+ seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx;
+ if (seg1 && abs(seg1 - seg0) == 8)
+ vht = VHT_CHANWIDTH_160MHZ;
+ else if (seg1)
+ vht = VHT_CHANWIDTH_80P80MHZ;
+ else
+ vht = VHT_CHANWIDTH_80MHZ;
+ break;
+ case 2:
+ vht = VHT_CHANWIDTH_160MHZ;
+ break;
+ case 3:
+ vht = VHT_CHANWIDTH_80P80MHZ;
+ break;
+ default:
+ vht = VHT_CHANWIDTH_USE_HT;
+ break;
+ }
+ }
+
+ if (ieee80211_freq_to_channel_ext(freq, sec_chan, vht, op_class,
+ chan) == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_DEBUG,
+ "Cannot determine operating class and channel");
+ return -1;
+ }
+
+ *phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL,
+ vht_oper != NULL);
+ if (*phy_type == PHY_TYPE_UNSPECIFIED) {
+ wpa_printf(MSG_DEBUG, "Cannot determine phy type");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpas_beacon_rep_add_frame_body(struct bitfield *eids,
+ enum beacon_report_detail detail,
+ struct wpa_bss *bss, u8 *buf,
+ size_t buf_len)
+{
+ u8 *ies = (u8 *) (bss + 1);
+ size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
+ u8 *pos = buf;
+ int rem_len;
+
+ rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) -
+ sizeof(struct rrm_measurement_report_element) - 2;
+
+ if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
+ wpa_printf(MSG_DEBUG,
+ "Beacon Request: Invalid reporting detail: %d",
+ detail);
+ return -1;
+ }
+
+ if (detail == BEACON_REPORT_DETAIL_NONE)
+ return 0;
+
+ /*
+ * Minimal frame body subelement size: EID(1) + length(1) + TSF(8) +
+ * beacon interval(2) + capabilities(2) = 14 bytes
+ */
+ if (buf_len < 14)
+ return 0;
+
+ *pos++ = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY;
+ /* The length will be filled later */
+ pos++;
+ WPA_PUT_LE64(pos, bss->tsf);
+ pos += sizeof(bss->tsf);
+ WPA_PUT_LE16(pos, bss->beacon_int);
+ pos += 2;
+ WPA_PUT_LE16(pos, bss->caps);
+ pos += 2;
+
+ rem_len -= pos - buf;
+
+ /*
+ * According to IEEE Std 802.11-2016, 9.4.2.22.7, if the reported frame
+ * body subelement causes the element to exceed the maximum element
+ * size, the subelement is truncated so that the last IE is a complete
+ * IE. So even when required to report all IEs, add elements one after
+ * the other and stop once there is no more room in the measurement
+ * element.
+ */
+ while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) {
+ if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS ||
+ (eids && bitfield_is_set(eids, ies[0]))) {
+ u8 eid = ies[0], elen = ies[1];
+
+ if ((eid == WLAN_EID_TIM || eid == WLAN_EID_RSN) &&
+ elen > 4)
+ elen = 4;
+ /*
+ * TODO: Truncate IBSS DFS element as described in
+ * IEEE Std 802.11-2016, 9.4.2.22.7.
+ */
+
+ if (2 + elen > buf + buf_len - pos ||
+ 2 + elen > rem_len)
+ break;
+
+ *pos++ = ies[0];
+ *pos++ = elen;
+ os_memcpy(pos, ies + 2, elen);
+ pos += elen;
+ rem_len -= 2 + elen;
+ }
+
+ ies_len -= 2 + ies[1];
+ ies += 2 + ies[1];
+ }
+
+ /* Now the length is known */
+ buf[1] = pos - buf - 2;
+ return pos - buf;
+}
+
+
+static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s,
+ struct wpabuf **wpa_buf, struct wpa_bss *bss,
+ u64 start, u64 parent_tsf)
+{
+ struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
+ u8 *ie = (u8 *) (bss + 1);
+ size_t ie_len = bss->ie_len + bss->beacon_ie_len;
+ int ret;
+ u8 *buf;
+ struct rrm_measurement_beacon_report *rep;
+
+ if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 &&
+ os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0)
+ return 0;
+
+ if (data->ssid_len &&
+ (data->ssid_len != bss->ssid_len ||
+ os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0))
+ return 0;
+
+ /* Maximum element length: beacon report element + reported frame body
+ * subelement + all IEs of the reported beacon */
+ buf = os_malloc(sizeof(*rep) + 14 + ie_len);
+ if (!buf)
+ return -1;
+
+ rep = (struct rrm_measurement_beacon_report *) buf;
+ if (wpas_get_op_chan_phy(bss->freq, ie, ie_len, &rep->op_class,
+ &rep->channel, &rep->report_info) < 0) {
+ ret = 0;
+ goto out;
+ }
+
+ rep->start_time = host_to_le64(start);
+ rep->duration = host_to_le16(data->scan_params.duration);
+ rep->rcpi = rssi_to_rcpi(bss->level);
+ rep->rsni = 255; /* 255 indicates that RSNI is not available */
+ os_memcpy(rep->bssid, bss->bssid, ETH_ALEN);
+ rep->antenna_id = 0; /* unknown */
+ rep->parent_tsf = host_to_le32(parent_tsf);
+
+ ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail,
+ bss, rep->variable, 14 + ie_len);
+ if (ret < 0)
+ goto out;
+
+ ret = wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token,
+ MEASUREMENT_REPORT_MODE_ACCEPT,
+ MEASURE_TYPE_BEACON, buf,
+ ret + sizeof(*rep));
+out:
+ os_free(buf);
+ return ret;
+}
+
+
+static int wpas_beacon_rep_no_results(struct wpa_supplicant *wpa_s,
+ struct wpabuf **buf)
+{
+ return wpas_rrm_report_elem(buf, wpa_s->beacon_rep_data.token,
+ MEASUREMENT_REPORT_MODE_ACCEPT,
+ MEASURE_TYPE_BEACON, NULL, 0);
+}
+
+
+static void wpas_beacon_rep_table(struct wpa_supplicant *wpa_s,
+ struct wpabuf **buf)
+{
+ size_t i;
+
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ if (wpas_add_beacon_rep(wpa_s, buf, wpa_s->last_scan_res[i],
+ 0, 0) < 0)
+ break;
+ }
+
+ if (!(*buf))
+ wpas_beacon_rep_no_results(wpa_s, buf);
+
+ wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", *buf);
+}
+
+
+void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s)
+{
+ if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr)) {
+ struct wpabuf *buf = NULL;
+
+ if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token,
+ MEASUREMENT_REPORT_MODE_REJECT_REFUSED,
+ MEASURE_TYPE_BEACON, NULL, 0)) {
+ wpa_printf(MSG_ERROR, "RRM: Memory allocation failed");
+ wpabuf_free(buf);
+ return;
+ }
+
+ wpas_rrm_send_msr_report(wpa_s, buf);
+ wpabuf_free(buf);
+ }
+
+ wpas_clear_beacon_rep_data(wpa_s);
+}
+
+
+static void wpas_rrm_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_driver_scan_params *params =
+ &wpa_s->beacon_rep_data.scan_params;
+ u16 prev_duration = params->duration;
+
+ if (!wpa_s->current_bss)
+ return;
+
+ if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) &&
+ params->duration) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Cannot set scan duration due to missing driver support");
+ params->duration = 0;
+ }
+ os_get_reltime(&wpa_s->beacon_rep_scan);
+ if (wpa_s->scanning || wpas_p2p_in_progress(wpa_s) ||
+ wpa_supplicant_trigger_scan(wpa_s, params))
+ wpas_rrm_refuse_request(wpa_s);
+ params->duration = prev_duration;
+}
+
+
+static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s,
+ struct beacon_rep_data *data,
+ u8 sid, u8 slen, const u8 *subelem)
+{
+ u8 report_info, i;
+
+ switch (sid) {
+ case WLAN_BEACON_REQUEST_SUBELEM_SSID:
+ if (!slen) {
+ wpa_printf(MSG_DEBUG,
+ "SSID subelement with zero length - wildcard SSID");
+ break;
+ }
+
+ if (slen > SSID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid SSID subelement length: %u", slen);
+ return -1;
+ }
+
+ data->ssid_len = slen;
+ os_memcpy(data->ssid, subelem, data->ssid_len);
+ break;
+ case WLAN_BEACON_REQUEST_SUBELEM_INFO:
+ if (slen != 2) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid reporting information subelement length: %u",
+ slen);
+ return -1;
+ }
+
+ report_info = subelem[0];
+ if (report_info != 0) {
+ wpa_printf(MSG_DEBUG,
+ "reporting information=%u is not supported",
+ report_info);
+ return 0;
+ }
+ break;
+ case WLAN_BEACON_REQUEST_SUBELEM_DETAIL:
+ if (slen != 1) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid reporting detail subelement length: %u",
+ slen);
+ return -1;
+ }
+
+ data->report_detail = subelem[0];
+ if (data->report_detail >
+ BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
+ wpa_printf(MSG_DEBUG, "Invalid reporting detail: %u",
+ subelem[0]);
+ return -1;
+ }
+
+ break;
+ case WLAN_BEACON_REQUEST_SUBELEM_REQUEST:
+ if (data->report_detail !=
+ BEACON_REPORT_DETAIL_REQUESTED_ONLY) {
+ wpa_printf(MSG_DEBUG,
+ "Beacon request: request subelement is present but report detail is %u",
+ data->report_detail);
+ return -1;
+ }
+
+ if (!slen) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid request subelement length: %u",
+ slen);
+ return -1;
+ }
+
+ if (data->eids) {
+ wpa_printf(MSG_DEBUG,
+ "Beacon Request: Request subelement appears more than once");
+ return -1;
+ }
+
+ data->eids = bitfield_alloc(255);
+ if (!data->eids) {
+ wpa_printf(MSG_DEBUG, "Failed to allocate EIDs bitmap");
+ return -1;
+ }
+
+ for (i = 0; i < slen; i++)
+ bitfield_set(data->eids, subelem[i]);
+ break;
+ case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL:
+ /* Skip - it will be processed when freqs are added */
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Beacon request: Unknown subelement id %u", sid);
+ break;
+ }
+
+ return 1;
+}
+
+
+/**
+ * Returns 0 if the next element can be processed, 1 if some operation was
+ * triggered, and -1 if processing failed (i.e., the element is in invalid
+ * format or an internal error occurred).
+ */
+static int
+wpas_rm_handle_beacon_req(struct wpa_supplicant *wpa_s,
+ u8 elem_token, int duration_mandatory,
+ const struct rrm_measurement_beacon_request *req,
+ size_t len, struct wpabuf **buf)
+{
+ struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
+ struct wpa_driver_scan_params *params = &data->scan_params;
+ const u8 *subelems;
+ size_t elems_len;
+ u16 rand_interval;
+ u32 interval_usec;
+ u32 _rand;
+ int ret = 0, res;
+ u8 reject_mode;
+
+ if (len < sizeof(*req))
+ return -1;
+
+ if (req->mode != BEACON_REPORT_MODE_PASSIVE &&
+ req->mode != BEACON_REPORT_MODE_ACTIVE &&
+ req->mode != BEACON_REPORT_MODE_TABLE)
+ return 0;
+
+ subelems = req->variable;
+ elems_len = len - sizeof(*req);
+ rand_interval = le_to_host16(req->rand_interval);
+
+ os_free(params->freqs);
+ os_memset(params, 0, sizeof(*params));
+
+ data->token = elem_token;
+
+ /* default reporting detail is all fixed length fields and all
+ * elements */
+ data->report_detail = BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS;
+ os_memcpy(data->bssid, req->bssid, ETH_ALEN);
+
+ while (elems_len >= 2) {
+ if (subelems[1] > elems_len - 2) {
+ wpa_printf(MSG_DEBUG,
+ "Beacon Request: Truncated subelement");
+ ret = -1;
+ goto out;
+ }
+
+ res = wpas_rm_handle_beacon_req_subelem(
+ wpa_s, data, subelems[0], subelems[1], &subelems[2]);
+ if (res < 0) {
+ ret = res;
+ goto out;
+ } else if (!res) {
+ reject_mode = MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE;
+ goto out_reject;
+ }
+
+ elems_len -= 2 + subelems[1];
+ subelems += 2 + subelems[1];
+ }
+
+ if (req->mode == BEACON_REPORT_MODE_TABLE) {
+ wpas_beacon_rep_table(wpa_s, buf);
+ goto out;
+ }
+
+ params->freqs = wpas_beacon_request_freqs(
+ wpa_s, req->oper_class, req->channel,
+ req->mode == BEACON_REPORT_MODE_ACTIVE,
+ req->variable, len - sizeof(*req));
+ if (!params->freqs) {
+ wpa_printf(MSG_DEBUG, "Beacon request: No valid channels");
+ reject_mode = MEASUREMENT_REPORT_MODE_REJECT_REFUSED;
+ goto out_reject;
+ }
+
+ params->duration = le_to_host16(req->duration);
+ params->duration_mandatory = duration_mandatory;
+ if (!params->duration) {
+ wpa_printf(MSG_DEBUG, "Beacon request: Duration is 0");
+ ret = -1;
+ goto out;
+ }
+
+ params->only_new_results = 1;
+
+ if (req->mode == BEACON_REPORT_MODE_ACTIVE) {
+ params->ssids[params->num_ssids].ssid = data->ssid;
+ params->ssids[params->num_ssids++].ssid_len = data->ssid_len;
+ }
+
+ if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+ _rand = os_random();
+ interval_usec = (_rand % (rand_interval + 1)) * 1024;
+ eloop_register_timeout(0, interval_usec, wpas_rrm_scan_timeout, wpa_s,
+ NULL);
+ return 1;
+out_reject:
+ if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+ wpas_rrm_report_elem(buf, elem_token, reject_mode,
+ MEASURE_TYPE_BEACON, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
+ ret = -1;
+ }
+out:
+ wpas_clear_beacon_rep_data(wpa_s);
+ return ret;
+}
+
+
+static int
+wpas_rrm_handle_msr_req_element(
+ struct wpa_supplicant *wpa_s,
+ const struct rrm_measurement_request_element *req,
+ struct wpabuf **buf)
+{
+ int duration_mandatory;
+
+ wpa_printf(MSG_DEBUG, "Measurement request type %d token %d",
+ req->type, req->token);
+
+ if (req->mode & MEASUREMENT_REQUEST_MODE_ENABLE) {
+ /* Enable bit is not supported for now */
+ wpa_printf(MSG_DEBUG, "RRM: Enable bit not supported, ignore");
+ return 0;
+ }
+
+ if ((req->mode & MEASUREMENT_REQUEST_MODE_PARALLEL) &&
+ req->type > MEASURE_TYPE_RPI_HIST) {
+ /* Parallel measurements are not supported for now */
+ wpa_printf(MSG_DEBUG,
+ "RRM: Parallel measurements are not supported, reject");
+ goto reject;
+ }
+
+ duration_mandatory =
+ !!(req->mode & MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY);
+
+ switch (req->type) {
+ case MEASURE_TYPE_LCI:
+ return wpas_rrm_build_lci_report(wpa_s, req, buf);
+ case MEASURE_TYPE_BEACON:
+ if (duration_mandatory &&
+ !(wpa_s->drv_rrm_flags &
+ WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL)) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Driver does not support dwell time configuration - reject beacon report with mandatory duration");
+ goto reject;
+ }
+ return wpas_rm_handle_beacon_req(wpa_s, req->token,
+ duration_mandatory,
+ (const void *) req->variable,
+ req->len - 3, buf);
+ default:
+ wpa_printf(MSG_INFO,
+ "RRM: Unsupported radio measurement type %u",
+ req->type);
+ break;
+ }
+
+reject:
+ if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
+ wpas_rrm_report_elem(buf, req->token,
+ MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
+ req->type, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf *
+wpas_rrm_process_msr_req_elems(struct wpa_supplicant *wpa_s, const u8 *pos,
+ size_t len)
+{
+ struct wpabuf *buf = NULL;
+
+ while (len) {
+ const struct rrm_measurement_request_element *req;
+ int res;
+
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "RRM: Truncated element");
+ goto out;
+ }
+
+ req = (const struct rrm_measurement_request_element *) pos;
+ if (req->eid != WLAN_EID_MEASURE_REQUEST) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Expected Measurement Request element, but EID is %u",
+ req->eid);
+ goto out;
+ }
+
+ if (req->len < 3) {
+ wpa_printf(MSG_DEBUG, "RRM: Element length too short");
+ goto out;
+ }
+
+ if (req->len > len - 2) {
+ wpa_printf(MSG_DEBUG, "RRM: Element length too long");
+ goto out;
+ }
+
+ res = wpas_rrm_handle_msr_req_element(wpa_s, req, &buf);
+ if (res < 0)
+ goto out;
+
+ pos += req->len + 2;
+ len -= req->len + 2;
+ }
+
+ return buf;
+
+out:
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
+ const u8 *src, const u8 *dst,
+ const u8 *frame, size_t len)
+{
+ struct wpabuf *report;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED) {
+ wpa_printf(MSG_INFO,
+ "RRM: Ignoring radio measurement request: Not associated");
+ return;
+ }
+
+ if (!wpa_s->rrm.rrm_used) {
+ wpa_printf(MSG_INFO,
+ "RRM: Ignoring radio measurement request: Not RRM network");
+ return;
+ }
+
+ if (len < 3) {
+ wpa_printf(MSG_INFO,
+ "RRM: Ignoring too short radio measurement request");
+ return;
+ }
+
+ wpa_s->rrm.token = *frame;
+ os_memcpy(wpa_s->rrm.dst_addr, dst, ETH_ALEN);
+
+ /* Number of repetitions is not supported */
+
+ report = wpas_rrm_process_msr_req_elems(wpa_s, frame + 3, len - 3);
+ if (!report)
+ return;
+
+ wpas_rrm_send_msr_report(wpa_s, report);
+ wpabuf_free(report);
+}
+
+
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ const u8 *frame, size_t len,
+ int rssi)
+{
+ struct wpabuf *buf;
+ const struct rrm_link_measurement_request *req;
+ struct rrm_link_measurement_report report;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED) {
+ wpa_printf(MSG_INFO,
+ "RRM: Ignoring link measurement request. Not associated");
+ return;
+ }
+
+ if (!wpa_s->rrm.rrm_used) {
+ wpa_printf(MSG_INFO,
+ "RRM: Ignoring link measurement request. Not RRM network");
+ return;
+ }
+
+ if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+ wpa_printf(MSG_INFO,
+ "RRM: Measurement report failed. TX power insertion not supported");
+ return;
+ }
+
+ req = (const struct rrm_link_measurement_request *) frame;
+ if (len < sizeof(*req)) {
+ wpa_printf(MSG_INFO,
+ "RRM: Link measurement report failed. Request too short");
+ return;
+ }
+
+ os_memset(&report, 0, sizeof(report));
+ report.dialog_token = req->dialog_token;
+ report.tpc.eid = WLAN_EID_TPC_REPORT;
+ report.tpc.len = 2;
+ /* Note: The driver is expected to update report.tpc.tx_power and
+ * report.tpc.link_margin subfields when sending out this frame.
+ * Similarly, the driver would need to update report.rx_ant_id and
+ * report.tx_ant_id subfields. */
+ report.rsni = 255; /* 255 indicates that RSNI is not available */
+ report.rcpi = rssi_to_rcpi(rssi);
+
+ /* action_category + action_code */
+ buf = wpabuf_alloc(2 + sizeof(report));
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "RRM: Link measurement report failed. Buffer allocation failed");
+ return;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
+ wpabuf_put_data(buf, &report, sizeof(report));
+ wpa_hexdump_buf(MSG_DEBUG, "RRM: Link measurement report", buf);
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0)) {
+ wpa_printf(MSG_ERROR,
+ "RRM: Link measurement report failed. Send action failed");
+ }
+ wpabuf_free(buf);
+}
+
+
+int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res,
+ struct scan_info *info)
+{
+ size_t i = 0;
+ struct wpabuf *buf = NULL;
+
+ if (!wpa_s->beacon_rep_data.token)
+ return 0;
+
+ if (!wpa_s->current_bss)
+ goto out;
+
+ /* If the measurement was aborted, don't report partial results */
+ if (info->aborted)
+ goto out;
+
+ wpa_printf(MSG_DEBUG, "RRM: TSF BSSID: " MACSTR " current BSS: " MACSTR,
+ MAC2STR(info->scan_start_tsf_bssid),
+ MAC2STR(wpa_s->current_bss->bssid));
+ if ((wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) &&
+ os_memcmp(info->scan_start_tsf_bssid, wpa_s->current_bss->bssid,
+ ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Ignore scan results due to mismatching TSF BSSID");
+ goto out;
+ }
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_bss *bss =
+ wpa_bss_get_bssid(wpa_s, scan_res->res[i]->bssid);
+
+ if (!bss)
+ continue;
+
+ if ((wpa_s->drv_rrm_flags &
+ WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) &&
+ os_memcmp(scan_res->res[i]->tsf_bssid,
+ wpa_s->current_bss->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Ignore scan result for " MACSTR
+ " due to mismatching TSF BSSID" MACSTR,
+ MAC2STR(scan_res->res[i]->bssid),
+ MAC2STR(scan_res->res[i]->tsf_bssid));
+ continue;
+ }
+
+ /*
+ * Don't report results that were not received during the
+ * current measurement.
+ */
+ if (!(wpa_s->drv_rrm_flags &
+ WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT)) {
+ struct os_reltime update_time, diff;
+
+ /* For now, allow 8 ms older results due to some
+ * unknown issue with cfg80211 BSS table updates during
+ * a scan with the current BSS.
+ * TODO: Fix this more properly to avoid having to have
+ * this type of hacks in place. */
+ calculate_update_time(&scan_res->fetch_time,
+ scan_res->res[i]->age,
+ &update_time);
+ os_reltime_sub(&wpa_s->beacon_rep_scan,
+ &update_time, &diff);
+ if (os_reltime_before(&update_time,
+ &wpa_s->beacon_rep_scan) &&
+ (diff.sec || diff.usec >= 8000)) {
+ wpa_printf(MSG_DEBUG,
+ "RRM: Ignore scan result for " MACSTR
+ " due to old update (age(ms) %u, calculated age %u.%06u seconds)",
+ MAC2STR(scan_res->res[i]->bssid),
+ scan_res->res[i]->age,
+ (unsigned int) diff.sec,
+ (unsigned int) diff.usec);
+ continue;
+ }
+ } else if (info->scan_start_tsf >
+ scan_res->res[i]->parent_tsf) {
+ continue;
+ }
+
+ if (wpas_add_beacon_rep(wpa_s, &buf, bss, info->scan_start_tsf,
+ scan_res->res[i]->parent_tsf) < 0)
+ break;
+ }
+
+ if (!buf && wpas_beacon_rep_no_results(wpa_s, &buf))
+ goto out;
+
+ wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", buf);
+
+ wpas_rrm_send_msr_report(wpa_s, buf);
+ wpabuf_free(buf);
+
+out:
+ wpas_clear_beacon_rep_data(wpa_s);
+ return 1;
+}
+
+
+void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s)
+{
+ struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
+
+ eloop_cancel_timeout(wpas_rrm_scan_timeout, wpa_s, NULL);
+ bitfield_free(data->eids);
+ os_free(data->scan_params.freqs);
+ os_memset(data, 0, sizeof(*data));
+}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index fb8ebdf2ecc1..ee39e0c9228d 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -117,9 +117,19 @@ int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
+ int min_temp_disabled = 0;
+
while (ssid) {
- if (!wpas_network_disabled(wpa_s, ssid))
- break;
+ if (!wpas_network_disabled(wpa_s, ssid)) {
+ int temp_disabled = wpas_temp_disabled(wpa_s, ssid);
+
+ if (temp_disabled <= 0)
+ break;
+
+ if (!min_temp_disabled ||
+ temp_disabled < min_temp_disabled)
+ min_temp_disabled = temp_disabled;
+ }
ssid = ssid->next;
}
@@ -128,7 +138,7 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
"end of scan list - go back to beginning");
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ wpa_supplicant_req_scan(wpa_s, min_temp_disabled, 0);
return;
}
if (ssid->next) {
@@ -176,10 +186,22 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
params->only_new_results = 1;
}
ret = wpa_drv_scan(wpa_s, params);
+ /*
+ * Store the obtained vendor scan cookie (if any) in wpa_s context.
+ * The current design is to allow only one scan request on each
+ * interface, hence having this scan cookie stored in wpa_s context is
+ * fine for now.
+ *
+ * Revisit this logic if concurrent scan operations per interface
+ * is supported.
+ */
+ if (ret == 0)
+ wpa_s->curr_scan_cookie = params->scan_cookie;
wpa_scan_free_params(params);
work->ctx = NULL;
if (ret) {
- int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ;
+ int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+ !wpa_s->beacon_rep_data.token;
if (wpa_s->disconnected)
retry = 0;
@@ -197,7 +219,14 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
/* Restore scan_req since we will try to scan again */
wpa_s->scan_req = wpa_s->last_scan_req;
wpa_supplicant_req_scan(wpa_s, 1, 0);
+ } else if (wpa_s->scan_res_handler) {
+ /* Clear the scan_res_handler */
+ wpa_s->scan_res_handler = NULL;
}
+
+ if (wpa_s->beacon_rep_data.token)
+ wpas_rrm_refuse_request(wpa_s);
+
return;
}
@@ -426,6 +455,33 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_MBO
+static void wpas_fils_req_param_add_max_channel(struct wpa_supplicant *wpa_s,
+ struct wpabuf **ie)
+{
+ if (wpabuf_resize(ie, 5)) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to allocate space for FILS Request Parameters element");
+ return;
+ }
+
+ /* FILS Request Parameters element */
+ wpabuf_put_u8(*ie, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(*ie, 3); /* FILS Request attribute length */
+ wpabuf_put_u8(*ie, WLAN_EID_EXT_FILS_REQ_PARAMS);
+ /* Parameter control bitmap */
+ wpabuf_put_u8(*ie, 0);
+ /* Max Channel Time field - contains the value of MaxChannelTime
+ * parameter of the MLME-SCAN.request primitive represented in units of
+ * TUs, as an unsigned integer. A Max Channel Time field value of 255
+ * is used to indicate any duration of more than 254 TUs, or an
+ * unspecified or unknown duration. (IEEE Std 802.11ai-2016, 9.4.2.178)
+ */
+ wpabuf_put_u8(*ie, 255);
+}
+#endif /* CONFIG_MBO */
+
+
void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s)
{
struct wpabuf *default_ies = NULL;
@@ -447,8 +503,10 @@ void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s)
wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
#ifdef CONFIG_MBO
- /* Send cellular capabilities for potential MBO STAs */
- if (wpabuf_resize(&default_ies, 9) == 0)
+ if (wpa_s->enable_oce & OCE_STA)
+ wpas_fils_req_param_add_max_channel(wpa_s, &default_ies);
+ /* Send MBO and OCE capabilities */
+ if (wpabuf_resize(&default_ies, 12) == 0)
wpas_mbo_scan_ie(wpa_s, default_ies);
#endif /* CONFIG_MBO */
@@ -488,6 +546,11 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
wpas_add_interworking_elements(wpa_s, extra_ie);
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_MBO
+ if (wpa_s->enable_oce & OCE_STA)
+ wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie);
+#endif /* CONFIG_MBO */
+
#ifdef CONFIG_WPS
wps = wpas_wps_in_use(wpa_s, &req_type);
@@ -529,8 +592,8 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
#endif /* CONFIG_FST */
#ifdef CONFIG_MBO
- /* Send cellular capabilities for potential MBO STAs */
- if (wpabuf_resize(&extra_ie, 9) == 0)
+ /* Send MBO and OCE capabilities */
+ if (wpabuf_resize(&extra_ie, 12) == 0)
wpas_mbo_scan_ie(wpa_s, extra_ie);
#endif /* CONFIG_MBO */
@@ -614,6 +677,87 @@ static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s,
}
+static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ size_t max_ssids, const u8 *ssid, size_t ssid_len)
+{
+ unsigned int j;
+
+ for (j = 0; j < params->num_ssids; j++) {
+ if (params->ssids[j].ssid_len == ssid_len &&
+ params->ssids[j].ssid &&
+ os_memcmp(params->ssids[j].ssid, ssid, ssid_len) == 0)
+ return; /* already in the list */
+ }
+
+ if (params->num_ssids + 1 > max_ssids) {
+ wpa_printf(MSG_DEBUG, "Over max scan SSIDs for manual request");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
+ wpa_ssid_txt(ssid, ssid_len));
+
+ params->ssids[params->num_ssids].ssid = ssid;
+ params->ssids[params->num_ssids].ssid_len = ssid_len;
+ params->num_ssids++;
+}
+
+
+static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ struct wpa_ssid *ssid, size_t max_ssids)
+{
+#ifdef CONFIG_OWE
+ struct wpa_bss *bss;
+
+ if (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE))
+ return;
+
+ wpa_printf(MSG_DEBUG, "OWE: Look for transition mode AP. ssid=%s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ const u8 *owe, *pos, *end;
+ const u8 *owe_ssid;
+ size_t owe_ssid_len;
+
+ if (bss->ssid_len != ssid->ssid_len ||
+ os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) != 0)
+ continue;
+
+ owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+ if (!owe || owe[1] < 4)
+ continue;
+
+ pos = owe + 6;
+ end = owe + 2 + owe[1];
+
+ /* Must include BSSID and ssid_len */
+ if (end - pos < ETH_ALEN + 1)
+ return;
+
+ /* Skip BSSID */
+ pos += ETH_ALEN;
+ owe_ssid_len = *pos++;
+ owe_ssid = pos;
+
+ if ((size_t) (end - pos) < owe_ssid_len ||
+ owe_ssid_len > SSID_MAX_LEN)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "OWE: scan_ssids: transition mode OWE ssid=%s",
+ wpa_ssid_txt(owe_ssid, owe_ssid_len));
+
+ wpa_add_scan_ssid(wpa_s, params, max_ssids,
+ owe_ssid, owe_ssid_len);
+ return;
+ }
+#endif /* CONFIG_OWE */
+}
+
+
static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params,
size_t max_ssids)
@@ -628,33 +772,17 @@ static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids;
for (i = 0; i < wpa_s->scan_id_count; i++) {
- unsigned int j;
-
ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
- if (!ssid || !ssid->scan_ssid)
+ if (!ssid)
continue;
-
- for (j = 0; j < params->num_ssids; j++) {
- if (params->ssids[j].ssid_len == ssid->ssid_len &&
- params->ssids[j].ssid &&
- os_memcmp(params->ssids[j].ssid, ssid->ssid,
- ssid->ssid_len) == 0)
- break;
- }
- if (j < params->num_ssids)
- continue; /* already in the list */
-
- if (params->num_ssids + 1 > max_ssids) {
- wpa_printf(MSG_DEBUG,
- "Over max scan SSIDs for manual request");
- break;
- }
-
- wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
- wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
- params->ssids[params->num_ssids].ssid = ssid->ssid;
- params->ssids[params->num_ssids].ssid_len = ssid->ssid_len;
- params->num_ssids++;
+ if (ssid->scan_ssid)
+ wpa_add_scan_ssid(wpa_s, params, max_ssids,
+ ssid->ssid, ssid->ssid_len);
+ /*
+ * Also add the SSID of the OWE BSS, to allow discovery of
+ * transition mode APs more quickly.
+ */
+ wpa_add_owe_scan_ssid(wpa_s, params, ssid, max_ssids);
}
wpa_s->scan_id_count = 0;
@@ -703,10 +831,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
size_t max_ssids;
int connect_without_scan = 0;
- if (wpa_s->pno || wpa_s->pno_sched_pending) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress");
- return;
- }
+ wpa_s->ignore_post_flush_scan_res = 0;
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
@@ -768,6 +893,21 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
return;
}
+ /*
+ * Don't cancel the scan based on ongoing PNO; defer it. Some scans are
+ * used for changing modes inside wpa_supplicant (roaming,
+ * auto-reconnect, etc). Discarding the scan might hurt these processes.
+ * The normal use case for PNO is to suspend the host immediately after
+ * starting PNO, so the periodic 100 ms attempts to run the scan do not
+ * normally happen in practice multiple times, i.e., this is simply
+ * restarting scanning once the host is woken up and PNO stopped.
+ */
+ if (wpa_s->pno || wpa_s->pno_sched_pending) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress");
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ return;
+ }
+
if (wpa_s->conf->ap_scan == 2)
max_ssids = 1;
else {
@@ -909,6 +1049,17 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
if (params.num_ssids + 1 >= max_ssids)
break;
}
+
+ if (!wpas_network_disabled(wpa_s, ssid)) {
+ /*
+ * Also add the SSID of the OWE BSS, to allow
+ * discovery of transition mode APs more
+ * quickly.
+ */
+ wpa_add_owe_scan_ssid(wpa_s, &params, ssid,
+ max_ssids);
+ }
+
ssid = ssid->next;
if (ssid == start)
break;
@@ -995,6 +1146,13 @@ ssid_list_set:
wpa_s->manual_scan_freqs = NULL;
}
+ if (params.freqs == NULL && wpa_s->select_network_scan_freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Limit select_network scan to specified channels");
+ params.freqs = wpa_s->select_network_scan_freqs;
+ wpa_s->select_network_scan_freqs = NULL;
+ }
+
if (params.freqs == NULL && wpa_s->next_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
"generated frequency list");
@@ -1029,6 +1187,11 @@ ssid_list_set:
}
}
+#ifdef CONFIG_MBO
+ if (wpa_s->enable_oce & OCE_STA)
+ params.oce_scan = 1;
+#endif /* CONFIG_MBO */
+
params.filter_ssids = wpa_supplicant_build_filter_ssids(
wpa_s->conf, &params.num_filter_ssids);
if (extra_ie) {
@@ -1047,7 +1210,8 @@ ssid_list_set:
}
#endif /* CONFIG_P2P */
- if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) {
+ if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
+ wpa_s->wpa_state <= WPA_SCANNING) {
params.mac_addr_rand = 1;
if (wpa_s->mac_addr_scan) {
params.mac_addr = wpa_s->mac_addr_scan;
@@ -1225,6 +1389,26 @@ int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
}
+static void
+wpa_scan_set_relative_rssi_params(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params)
+{
+ if (wpa_s->wpa_state != WPA_COMPLETED ||
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI) ||
+ wpa_s->srp.relative_rssi_set == 0)
+ return;
+
+ params->relative_rssi_set = 1;
+ params->relative_rssi = wpa_s->srp.relative_rssi;
+
+ if (wpa_s->srp.relative_adjust_rssi == 0)
+ return;
+
+ params->relative_adjust_band = wpa_s->srp.relative_adjust_band;
+ params->relative_adjust_rssi = wpa_s->srp.relative_adjust_rssi;
+}
+
+
/**
* wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
* @wpa_s: Pointer to wpa_supplicant data
@@ -1417,6 +1601,11 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
int_array_concat(&params.freqs, wpa_s->conf->freq_list);
}
+#ifdef CONFIG_MBO
+ if (wpa_s->enable_oce & OCE_STA)
+ params.oce_scan = 1;
+#endif /* CONFIG_MBO */
+
scan_params = &params;
scan:
@@ -1458,18 +1647,24 @@ scan:
params.sched_scan_plans_num = 1;
}
+ params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
+
if (ssid || !wpa_s->first_sched_scan) {
wpa_dbg(wpa_s, MSG_DEBUG,
- "Starting sched scan: interval %u timeout %d",
+ "Starting sched scan after %u seconds: interval %u timeout %d",
+ params.sched_scan_start_delay,
params.sched_scan_plans[0].interval,
wpa_s->sched_scan_timeout);
} else {
- wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting sched scan after %u seconds (no timeout)",
+ params.sched_scan_start_delay);
}
wpa_setband_scan_freqs(wpa_s, scan_params);
- if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) {
+ if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) &&
+ wpa_s->wpa_state <= WPA_SCANNING) {
params.mac_addr_rand = 1;
if (wpa_s->mac_addr_sched_scan) {
params.mac_addr = wpa_s->mac_addr_sched_scan;
@@ -1478,6 +1673,8 @@ scan:
}
}
+ wpa_scan_set_relative_rssi_params(wpa_s, scan_params);
+
ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
wpabuf_free(extra_ie);
os_free(params.filter_ssids);
@@ -1618,7 +1815,13 @@ static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
*/
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
{
- return get_ie((const u8 *) (res + 1), res->ie_len, ie);
+ size_t ie_len = res->ie_len;
+
+ /* Use the Beacon frame IEs if res->ie_len is not available */
+ if (!ie_len)
+ ie_len = res->beacon_ie_len;
+
+ return get_ie((const u8 *) (res + 1), ie_len, ie);
}
@@ -1735,10 +1938,12 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
* This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
* rule of thumb is that any SNR above 20 is good." This one
* http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
- * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a
- * conservative value.
+ * recommends 25 as a minimum SNR for 54 Mbps data rate. The estimates used in
+ * scan_est_throughput() allow even smaller SNR values for the maximum rates
+ * (21 for 54 Mbps, 22 for VHT80 MCS9, 24 for HT40 and HT20 MCS7). Use 25 as a
+ * somewhat conservative value here.
*/
-#define GREAT_SNR 30
+#define GREAT_SNR 25
#define IS_5GHZ(n) (n > 4000)
@@ -1786,10 +1991,12 @@ static int wpa_scan_result_compar(const void *a, const void *b)
}
/* if SNR is close, decide by max rate or frequency band */
- if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
- (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
+ if (snr_a && snr_b && abs(snr_b - snr_a) < 7) {
if (wa->est_throughput != wb->est_throughput)
return wb->est_throughput - wa->est_throughput;
+ }
+ if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
+ (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
return IS_5GHZ(wa->freq) ? -1 : 1;
}
@@ -2177,10 +2384,22 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_WPS */
- qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
- compar);
+ if (scan_res->res) {
+ qsort(scan_res->res, scan_res->num,
+ sizeof(struct wpa_scan_res *), compar);
+ }
dump_scan_res(scan_res);
+ if (wpa_s->ignore_post_flush_scan_res) {
+ /* FLUSH command aborted an ongoing scan and these are the
+ * results from the aborted scan. Do not process the results to
+ * maintain flushed state. */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Do not update BSS table based on pending post-FLUSH scan results");
+ wpa_s->ignore_post_flush_scan_res = 0;
+ return scan_res;
+ }
+
wpa_bss_update_start(wpa_s);
for (i = 0; i < scan_res->num; i++)
wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
@@ -2262,11 +2481,10 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
for (i = 0; i < src->num_ssids; i++) {
if (src->ssids[i].ssid) {
- n = os_malloc(src->ssids[i].ssid_len);
+ n = os_memdup(src->ssids[i].ssid,
+ src->ssids[i].ssid_len);
if (n == NULL)
goto failed;
- os_memcpy(n, src->ssids[i].ssid,
- src->ssids[i].ssid_len);
params->ssids[i].ssid = n;
params->ssids[i].ssid_len = src->ssids[i].ssid_len;
}
@@ -2274,30 +2492,26 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
params->num_ssids = src->num_ssids;
if (src->extra_ies) {
- n = os_malloc(src->extra_ies_len);
+ n = os_memdup(src->extra_ies, src->extra_ies_len);
if (n == NULL)
goto failed;
- os_memcpy(n, src->extra_ies, src->extra_ies_len);
params->extra_ies = n;
params->extra_ies_len = src->extra_ies_len;
}
if (src->freqs) {
int len = int_array_len(src->freqs);
- params->freqs = os_malloc((len + 1) * sizeof(int));
+ params->freqs = os_memdup(src->freqs, (len + 1) * sizeof(int));
if (params->freqs == NULL)
goto failed;
- os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int));
}
if (src->filter_ssids) {
- params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) *
+ params->filter_ssids = os_memdup(src->filter_ssids,
+ sizeof(*params->filter_ssids) *
src->num_filter_ssids);
if (params->filter_ssids == NULL)
goto failed;
- os_memcpy(params->filter_ssids, src->filter_ssids,
- sizeof(*params->filter_ssids) *
- src->num_filter_ssids);
params->num_filter_ssids = src->num_filter_ssids;
}
@@ -2305,17 +2519,18 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
params->p2p_probe = src->p2p_probe;
params->only_new_results = src->only_new_results;
params->low_priority = src->low_priority;
+ params->duration = src->duration;
+ params->duration_mandatory = src->duration_mandatory;
+ params->oce_scan = src->oce_scan;
if (src->sched_scan_plans_num > 0) {
params->sched_scan_plans =
- os_malloc(sizeof(*src->sched_scan_plans) *
+ os_memdup(src->sched_scan_plans,
+ sizeof(*src->sched_scan_plans) *
src->sched_scan_plans_num);
if (!params->sched_scan_plans)
goto failed;
- os_memcpy(params->sched_scan_plans, src->sched_scan_plans,
- sizeof(*src->sched_scan_plans) *
- src->sched_scan_plans_num);
params->sched_scan_plans_num = src->sched_scan_plans_num;
}
@@ -2340,13 +2555,16 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
if (src->bssid) {
u8 *bssid;
- bssid = os_malloc(ETH_ALEN);
+ bssid = os_memdup(src->bssid, ETH_ALEN);
if (!bssid)
goto failed;
- os_memcpy(bssid, src->bssid, ETH_ALEN);
params->bssid = bssid;
}
+ params->relative_rssi_set = src->relative_rssi_set;
+ params->relative_rssi = src->relative_rssi;
+ params->relative_adjust_band = src->relative_adjust_band;
+ params->relative_adjust_rssi = src->relative_adjust_rssi;
return params;
failed:
@@ -2404,7 +2622,7 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
return 0;
if ((wpa_s->wpa_state > WPA_SCANNING) &&
- (wpa_s->wpa_state <= WPA_COMPLETED)) {
+ (wpa_s->wpa_state < WPA_COMPLETED)) {
wpa_printf(MSG_ERROR, "PNO: In assoc process");
return -EAGAIN;
}
@@ -2511,12 +2729,15 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
params.sched_scan_plans_num = 1;
}
+ params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
+
if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
params.freqs = wpa_s->manual_sched_scan_freqs;
}
- if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) {
+ if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) &&
+ wpa_s->wpa_state <= WPA_SCANNING) {
params.mac_addr_rand = 1;
if (wpa_s->mac_addr_pno) {
params.mac_addr = wpa_s->mac_addr_pno;
@@ -2524,6 +2745,8 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
}
}
+ wpa_scan_set_relative_rssi_params(wpa_s, &params);
+
ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
os_free(params.filter_ssids);
if (ret == 0)
@@ -2614,18 +2837,20 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
{
- int scan_work = !!wpa_s->scan_work;
-
-#ifdef CONFIG_P2P
- scan_work |= !!wpa_s->p2p_scan_work;
-#endif /* CONFIG_P2P */
+ struct wpa_radio_work *work;
+ struct wpa_radio *radio = wpa_s->radio;
- if (scan_work && wpa_s->own_scan_running) {
+ dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+ if (work->wpa_s != wpa_s || !work->started ||
+ (os_strcmp(work->type, "scan") != 0 &&
+ os_strcmp(work->type, "p2p-scan") != 0))
+ continue;
wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
- return wpa_drv_abort_scan(wpa_s);
+ return wpa_drv_abort_scan(wpa_s, wpa_s->curr_scan_cookie);
}
- return 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "No ongoing scan/p2p-scan found to abort");
+ return -1;
}
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 61fd3b24549c..39c80696a94c 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -72,7 +72,7 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
wpa_s->sme.sae.group);
- return 0;
+ return 0;
}
wpa_s->sme.sae_group_index++;
}
@@ -83,12 +83,29 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- const u8 *bssid)
+ const u8 *bssid, int external)
{
struct wpabuf *buf;
size_t len;
-
- if (ssid->passphrase == NULL) {
+ const char *password;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->sae_commit_override) {
+ wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+ buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
+ if (!buf)
+ return NULL;
+ wpabuf_put_le16(buf, 1); /* Transaction seq# */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ wpabuf_put_buf(buf, wpa_s->sae_commit_override);
+ return buf;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ password = ssid->sae_password;
+ if (!password)
+ password = ssid->passphrase;
+ if (!password) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
@@ -99,27 +116,32 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
}
if (sae_prepare_commit(wpa_s->own_addr, bssid,
- (u8 *) ssid->passphrase,
- os_strlen(ssid->passphrase),
+ (u8 *) password, os_strlen(password),
+ ssid->sae_password_id,
&wpa_s->sme.sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0;
+ if (ssid->sae_password_id)
+ len += 4 + os_strlen(ssid->sae_password_id);
buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
if (buf == NULL)
return NULL;
-
- wpabuf_put_le16(buf, 1); /* Transaction seq# */
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
- sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
+ if (!external) {
+ wpabuf_put_le16(buf, 1); /* Transaction seq# */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ }
+ sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
+ ssid->sae_password_id);
return buf;
}
-static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
+static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s,
+ int external)
{
struct wpabuf *buf;
@@ -127,8 +149,10 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
if (buf == NULL)
return NULL;
- wpabuf_put_le16(buf, 2); /* Transaction seq# */
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ if (!external) {
+ wpabuf_put_le16(buf, 2); /* Transaction seq# */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ }
sae_write_confirm(&wpa_s->sme.sae, buf);
return buf;
@@ -187,6 +211,10 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s,
if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)
*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
+ *pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+ WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+
if (wpa_s->lci)
pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT;
@@ -204,16 +232,18 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_IEEE80211R
const u8 *ie;
#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
const u8 *md = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
int i, bssid_changed;
struct wpabuf *resp = NULL;
u8 ext_capab[18];
int ext_capab_len;
int skip_auth;
+ u8 *wpa_ie;
+ size_t wpa_ie_len;
#ifdef CONFIG_MBO
- const u8 *mbo;
+ const u8 *mbo_ie;
#endif /* CONFIG_MBO */
if (bss == NULL) {
@@ -300,13 +330,20 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
wpa_key_mgmt_wpa(ssid->key_mgmt)) {
int try_opportunistic;
+ const u8 *cache_id = NULL;
+
try_opportunistic = (ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc :
ssid->proactive_key_caching) &&
(ssid->proto & WPA_PROTO_RSN);
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(ssid->key_mgmt))
+ cache_id = wpa_bss_get_fils_cache_id(bss);
+#endif /* CONFIG_FILS */
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
wpa_s->current_ssid,
- try_opportunistic) == 0)
+ try_opportunistic, cache_id,
+ 0) == 0)
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
@@ -317,6 +354,20 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
wpas_connect_work_done(wpa_s);
return;
}
+#ifdef CONFIG_HS20
+ } else if (wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) &&
+ (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) {
+ /* No PMKSA caching, but otherwise similar to RSN/WPA */
+ wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+ if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+ wpa_s->sme.assoc_req_ie,
+ &wpa_s->sme.assoc_req_ie_len)) {
+ wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
+ "key management and encryption suites");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+#endif /* CONFIG_HS20 */
} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
/*
@@ -356,6 +407,28 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
wpa_s->sme.assoc_req_ie_len = 0;
}
+ /* In case the WPA vendor IE is used, it should be placed after all the
+ * non-vendor IEs, as the lower layer expects the IEs to be ordered as
+ * defined in the standard. Store the WPA IE so it can later be
+ * inserted at the correct location.
+ */
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ if (wpa_s->wpa_proto == WPA_PROTO_WPA) {
+ wpa_ie = os_memdup(wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len);
+ if (wpa_ie) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Storing WPA IE");
+
+ wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+ wpa_s->sme.assoc_req_ie_len = 0;
+ } else {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed copy WPA IE");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+ }
+
#ifdef CONFIG_IEEE80211R
ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
@@ -366,7 +439,12 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
}
- if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) {
+ if (md && !wpa_key_mgmt_ft(ssid->key_mgmt))
+ md = NULL;
+ if (md) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
+ md[0], md[1]);
+
if (wpa_s->sme.assoc_req_ie_len + 5 <
sizeof(wpa_s->sme.assoc_req_ie)) {
struct rsn_mdie *mdie;
@@ -440,20 +518,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
sme_auth_handle_rrm(wpa_s, bss);
-#ifdef CONFIG_MBO
- mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
- if (mbo) {
- int len;
-
- len = wpas_mbo_supp_op_class_ie(
- wpa_s, bss->freq,
- wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
- sizeof(wpa_s->sme.assoc_req_ie) -
- wpa_s->sme.assoc_req_ie_len);
- if (len > 0)
- wpa_s->sme.assoc_req_ie_len += len;
- }
-#endif /* CONFIG_MBO */
+ wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie(
+ wpa_s, bss->freq,
+ wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+ sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len);
if (params.p2p)
wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
@@ -477,12 +545,13 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
if (is_hs20_network(wpa_s, ssid, bss)) {
struct wpabuf *hs20;
- hs20 = wpabuf_alloc(20);
+ hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN);
if (hs20) {
int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
size_t len;
wpas_hs20_add_indication(hs20, pps_mo_id);
+ wpas_hs20_add_roam_cons_sel(hs20, ssid);
len = sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len;
if (wpabuf_len(hs20) <= len) {
@@ -496,6 +565,26 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_HS20 */
+ if (wpa_ie) {
+ size_t len;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Reinsert WPA IE");
+
+ len = sizeof(wpa_s->sme.assoc_req_ie) -
+ wpa_s->sme.assoc_req_ie_len;
+
+ if (len > wpa_ie_len) {
+ os_memcpy(wpa_s->sme.assoc_req_ie +
+ wpa_s->sme.assoc_req_ie_len,
+ wpa_ie, wpa_ie_len);
+ wpa_s->sme.assoc_req_ie_len += wpa_ie_len;
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Failed to add WPA IE");
+ }
+
+ os_free(wpa_ie);
+ }
+
if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
size_t len;
@@ -511,13 +600,16 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
}
#ifdef CONFIG_MBO
- if (mbo) {
+ mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+ if (mbo_ie) {
int len;
len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie +
wpa_s->sme.assoc_req_ie_len,
sizeof(wpa_s->sme.assoc_req_ie) -
- wpa_s->sme.assoc_req_ie_len);
+ wpa_s->sme.assoc_req_ie_len,
+ !!mbo_attr_from_mbo_ie(mbo_ie,
+ OCE_ATTR_ID_CAPA_IND));
if (len >= 0)
wpa_s->sme.assoc_req_ie_len += len;
}
@@ -525,10 +617,11 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_SAE
if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
- pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
- {
+ pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
+ NULL, WPA_KEY_MGMT_SAE) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
+ wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
params.auth_alg = WPA_AUTH_ALG_OPEN;
wpa_s->sme.sae_pmksa_caching = 1;
}
@@ -536,19 +629,105 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
if (start)
resp = sme_auth_build_sae_commit(wpa_s, ssid,
- bss->bssid);
+ bss->bssid, 0);
else
- resp = sme_auth_build_sae_confirm(wpa_s);
+ resp = sme_auth_build_sae_confirm(wpa_s, 0);
if (resp == NULL) {
wpas_connection_failed(wpa_s, bss->bssid);
return;
}
- params.sae_data = wpabuf_head(resp);
- params.sae_data_len = wpabuf_len(resp);
+ params.auth_data = wpabuf_head(resp);
+ params.auth_data_len = wpabuf_len(resp);
wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
}
#endif /* CONFIG_SAE */
+ old_ssid = wpa_s->current_ssid;
+ wpa_s->current_ssid = ssid;
+ wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+ wpa_supplicant_initiate_eapol(wpa_s);
+
+#ifdef CONFIG_FILS
+ /* TODO: FILS operations can in some cases be done between different
+ * network_ctx (i.e., same credentials can be used with multiple
+ * networks). */
+ if (params.auth_alg == WPA_AUTH_ALG_OPEN &&
+ wpa_key_mgmt_fils(ssid->key_mgmt)) {
+ const u8 *indic;
+ u16 fils_info;
+ const u8 *realm, *username, *rrk;
+ size_t realm_len, username_len, rrk_len;
+ u16 next_seq_num;
+
+ /*
+ * Check FILS Indication element (FILS Information field) bits
+ * indicating supported authentication algorithms against local
+ * configuration (ssid->fils_dh_group). Try to use FILS
+ * authentication only if the AP supports the combination in the
+ * network profile. */
+ indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+ if (!indic || indic[1] < 2) {
+ wpa_printf(MSG_DEBUG, "SME: " MACSTR
+ " does not include FILS Indication element - cannot use FILS authentication with it",
+ MAC2STR(bss->bssid));
+ goto no_fils;
+ }
+
+ fils_info = WPA_GET_LE16(indic + 2);
+ if (ssid->fils_dh_group == 0 && !(fils_info & BIT(9))) {
+ wpa_printf(MSG_DEBUG, "SME: " MACSTR
+ " does not support FILS SK without PFS - cannot use FILS authentication with it",
+ MAC2STR(bss->bssid));
+ goto no_fils;
+ }
+ if (ssid->fils_dh_group != 0 && !(fils_info & BIT(10))) {
+ wpa_printf(MSG_DEBUG, "SME: " MACSTR
+ " does not support FILS SK with PFS - cannot use FILS authentication with it",
+ MAC2STR(bss->bssid));
+ goto no_fils;
+ }
+
+ if (wpa_s->last_con_fail_realm &&
+ eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap,
+ &username, &username_len,
+ &realm, &realm_len, &next_seq_num,
+ &rrk, &rrk_len) == 0 &&
+ realm && realm_len == wpa_s->last_con_fail_realm_len &&
+ os_memcmp(realm, wpa_s->last_con_fail_realm,
+ realm_len) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "SME: FILS authentication for this realm failed last time - try to regenerate ERP key hierarchy");
+ goto no_fils;
+ }
+
+ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+ ssid, 0,
+ wpa_bss_get_fils_cache_id(bss),
+ 0) == 0)
+ wpa_printf(MSG_DEBUG,
+ "SME: Try to use FILS with PMKSA caching");
+ resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md);
+ if (resp) {
+ int auth_alg;
+
+ if (ssid->fils_dh_group)
+ wpa_printf(MSG_DEBUG,
+ "SME: Try to use FILS SK authentication with PFS (DH Group %u)",
+ ssid->fils_dh_group);
+ else
+ wpa_printf(MSG_DEBUG,
+ "SME: Try to use FILS SK authentication without PFS");
+ auth_alg = ssid->fils_dh_group ?
+ WPA_AUTH_ALG_FILS_SK_PFS : WPA_AUTH_ALG_FILS;
+ params.auth_alg = auth_alg;
+ params.auth_data = wpabuf_head(resp);
+ params.auth_data_len = wpabuf_len(resp);
+ wpa_s->sme.auth_alg = auth_alg;
+ }
+ }
+no_fils:
+#endif /* CONFIG_FILS */
+
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
@@ -556,12 +735,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
wpa_clear_keys(wpa_s, bss->bssid);
wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
- old_ssid = wpa_s->current_ssid;
- wpa_s->current_ssid = ssid;
- wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
- wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
@@ -650,6 +826,10 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
return;
}
+ /* Starting new connection, so clear the possibly used WPA IE from the
+ * previous association. */
+ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
}
@@ -700,8 +880,151 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_SAE
+static int sme_external_auth_build_buf(struct wpabuf *buf,
+ struct wpabuf *params,
+ const u8 *sa, const u8 *da,
+ u16 auth_transaction, u16 seq_num)
+{
+ struct ieee80211_mgmt *resp;
+
+ resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+ u.auth.variable));
+
+ resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_AUTH << 4));
+ os_memcpy(resp->da, da, ETH_ALEN);
+ os_memcpy(resp->sa, sa, ETH_ALEN);
+ os_memcpy(resp->bssid, da, ETH_ALEN);
+ resp->u.auth.auth_alg = host_to_le16(WLAN_AUTH_SAE);
+ resp->seq_ctrl = host_to_le16(seq_num << 4);
+ resp->u.auth.auth_transaction = host_to_le16(auth_transaction);
+ resp->u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+ if (params)
+ wpabuf_put_buf(buf, params);
+
+ return 0;
+}
+
+
+static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
+ const u8 *bssid,
+ struct wpa_ssid *ssid)
+{
+ struct wpabuf *resp, *buf;
+
+ resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1);
+ if (!resp)
+ return;
+
+ wpa_s->sme.sae.state = SAE_COMMITTED;
+ buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
+ if (!buf) {
+ wpabuf_free(resp);
+ return;
+ }
+
+ wpa_s->sme.seq_num++;
+ sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
+ bssid, 1, wpa_s->sme.seq_num);
+ wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
+ wpabuf_free(resp);
+ wpabuf_free(buf);
+}
+
+
+static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s,
+ u16 status)
+{
+ struct external_auth params;
+
+ os_memset(&params, 0, sizeof(params));
+ params.status = status;
+ os_memcpy(params.ssid, wpa_s->sme.ext_auth.ssid,
+ wpa_s->sme.ext_auth.ssid_len);
+ params.ssid_len = wpa_s->sme.ext_auth.ssid_len;
+ os_memcpy(params.bssid, wpa_s->sme.ext_auth.bssid, ETH_ALEN);
+ wpa_drv_send_external_auth_status(wpa_s, &params);
+}
+
+
+static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ struct wpa_ssid *ssid;
+ size_t ssid_str_len = data->external_auth.ssid_len;
+ u8 *ssid_str = data->external_auth.ssid;
+
+ /* Get the SSID conf from the ssid string obtained */
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (!wpas_network_disabled(wpa_s, ssid) &&
+ ssid_str_len == ssid->ssid_len &&
+ os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 &&
+ (ssid->key_mgmt & WPA_KEY_MGMT_SAE))
+ break;
+ }
+ if (ssid)
+ sme_external_auth_send_sae_commit(wpa_s,
+ data->external_auth.bssid,
+ ssid);
+ else
+ sme_send_external_auth_status(wpa_s,
+ WLAN_STATUS_UNSPECIFIED_FAILURE);
+}
+
+
+static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
+ const u8 *da)
+{
+ struct wpabuf *resp, *buf;
+
+ resp = sme_auth_build_sae_confirm(wpa_s, 1);
+ if (!resp) {
+ wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure");
+ return;
+ }
+
+ wpa_s->sme.sae.state = SAE_CONFIRMED;
+ buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp));
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
+ wpabuf_free(resp);
+ return;
+ }
+ wpa_s->sme.seq_num++;
+ sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
+ da, 2, wpa_s->sme.seq_num);
+ wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
+ wpabuf_free(resp);
+ wpabuf_free(buf);
+}
+
+
+void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) !=
+ RSN_AUTH_KEY_MGMT_SAE)
+ return;
+
+ if (data->external_auth.action == EXT_AUTH_START) {
+ os_memcpy(&wpa_s->sme.ext_auth, data,
+ sizeof(struct external_auth));
+ wpa_s->sme.seq_num = 0;
+ wpa_s->sme.sae.state = SAE_NOTHING;
+ wpa_s->sme.sae.send_confirm = 0;
+ wpa_s->sme.sae_group_index = 0;
+ sme_handle_external_auth_start(wpa_s, data);
+ } else if (data->external_auth.action == EXT_AUTH_ABORT) {
+ /* Report failure to driver for the wrong trigger */
+ sme_send_external_auth_status(wpa_s,
+ WLAN_STATUS_UNSPECIFIED_FAILURE);
+ }
+}
+
+
static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
- u16 status_code, const u8 *data, size_t len)
+ u16 status_code, const u8 *data, size_t len,
+ int external, const u8 *sa)
{
int *groups;
@@ -711,7 +1034,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
if (auth_transaction == 1 &&
status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
wpa_s->sme.sae.state == SAE_COMMITTED &&
- wpa_s->current_bss && wpa_s->current_ssid) {
+ (external || wpa_s->current_bss) && wpa_s->current_ssid) {
int default_groups[] = { 19, 20, 21, 25, 26, 0 };
u16 group;
@@ -738,25 +1061,45 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
wpabuf_free(wpa_s->sme.sae_token);
wpa_s->sme.sae_token = wpabuf_alloc_copy(data + sizeof(le16),
len - sizeof(le16));
- sme_send_authentication(wpa_s, wpa_s->current_bss,
- wpa_s->current_ssid, 1);
+ if (!external)
+ sme_send_authentication(wpa_s, wpa_s->current_bss,
+ wpa_s->current_ssid, 1);
+ else
+ sme_external_auth_send_sae_commit(
+ wpa_s, wpa_s->sme.ext_auth.bssid,
+ wpa_s->current_ssid);
return 0;
}
if (auth_transaction == 1 &&
status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
wpa_s->sme.sae.state == SAE_COMMITTED &&
- wpa_s->current_bss && wpa_s->current_ssid) {
+ (external || wpa_s->current_bss) && wpa_s->current_ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
wpa_s->sme.sae_group_index++;
if (sme_set_sae_group(wpa_s) < 0)
return -1; /* no other groups enabled */
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
- sme_send_authentication(wpa_s, wpa_s->current_bss,
- wpa_s->current_ssid, 1);
+ if (!external)
+ sme_send_authentication(wpa_s, wpa_s->current_bss,
+ wpa_s->current_ssid, 1);
+ else
+ sme_external_auth_send_sae_commit(
+ wpa_s, wpa_s->sme.ext_auth.bssid,
+ wpa_s->current_ssid);
return 0;
}
+ if (auth_transaction == 1 &&
+ status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+ const u8 *bssid = sa ? sa : wpa_s->pending_bssid;
+
+ wpa_msg(wpa_s, MSG_INFO,
+ WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR,
+ MAC2STR(bssid));
+ return -1;
+ }
+
if (status_code != WLAN_STATUS_SUCCESS)
return -1;
@@ -766,7 +1109,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
groups = wpa_s->conf->sae_groups;
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
- if (wpa_s->current_bss == NULL ||
+ if ((!external && wpa_s->current_bss == NULL) ||
wpa_s->current_ssid == NULL)
return -1;
if (wpa_s->sme.sae.state != SAE_COMMITTED)
@@ -791,8 +1134,11 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
wpabuf_free(wpa_s->sme.sae_token);
wpa_s->sme.sae_token = NULL;
- sme_send_authentication(wpa_s, wpa_s->current_bss,
- wpa_s->current_ssid, 0);
+ if (!external)
+ sme_send_authentication(wpa_s, wpa_s->current_bss,
+ wpa_s->current_ssid, 0);
+ else
+ sme_external_auth_send_sae_confirm(wpa_s, sa);
return 0;
} else if (auth_transaction == 2) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
@@ -802,11 +1148,60 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
return -1;
wpa_s->sme.sae.state = SAE_ACCEPTED;
sae_clear_temp_data(&wpa_s->sme.sae);
+
+ if (external) {
+ /* Report success to driver */
+ sme_send_external_auth_status(wpa_s,
+ WLAN_STATUS_SUCCESS);
+ }
+
return 1;
}
return -1;
}
+
+
+void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+ const u8 *auth_frame, size_t len)
+{
+ const struct ieee80211_mgmt *header;
+ size_t auth_length;
+
+ header = (const struct ieee80211_mgmt *) auth_frame;
+ auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
+
+ if (len < auth_length) {
+ /* Notify failure to the driver */
+ sme_send_external_auth_status(wpa_s,
+ WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return;
+ }
+
+ if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) {
+ int res;
+
+ res = sme_sae_auth(
+ wpa_s, le_to_host16(header->u.auth.auth_transaction),
+ le_to_host16(header->u.auth.status_code),
+ header->u.auth.variable,
+ len - auth_length, 1, header->sa);
+ if (res < 0) {
+ /* Notify failure to the driver */
+ sme_send_external_auth_status(
+ wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return;
+ }
+ if (res != 1)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "SME: SAE completed - setting PMK for 4-way handshake");
+ wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+ wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
+ }
+}
+
#endif /* CONFIG_SAE */
@@ -847,7 +1242,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
int res;
res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
data->auth.status_code, data->auth.ies,
- data->auth.ies_len);
+ data->auth.ies_len, 0, NULL);
if (res < 0) {
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
@@ -875,12 +1270,19 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
}
}
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR
- " auth_type=%u auth_transaction=%u status_code=%u ie=%s",
+ " auth_type=%u auth_transaction=%u status_code=%u%s%s",
MAC2STR(data->auth.peer), data->auth.auth_type,
data->auth.auth_transaction, data->auth.status_code,
- ie_txt);
+ ie_txt ? " ie=" : "",
+ ie_txt ? ie_txt : "");
os_free(ie_txt);
+#ifdef CONFIG_FILS
+ if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
+ wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS)
+ fils_connection_failure(wpa_s);
+#endif /* CONFIG_FILS */
+
if (data->auth.status_code !=
WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
wpa_s->sme.auth_alg == data->auth.auth_type ||
@@ -916,9 +1318,17 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
#ifdef CONFIG_IEEE80211R
if (data->auth.auth_type == WLAN_AUTH_FT) {
+ const u8 *ric_ies = NULL;
+ size_t ric_ies_len = 0;
+
+ if (wpa_s->ric_ies) {
+ ric_ies = wpabuf_head(wpa_s->ric_ies);
+ ric_ies_len = wpabuf_len(wpa_s->ric_ies);
+ }
if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies,
data->auth.ies_len, 0,
- data->auth.peer, NULL, 0) < 0) {
+ data->auth.peer,
+ ric_ies, ric_ies_len) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: FT Authentication response processing failed");
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
@@ -933,16 +1343,75 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_FILS
+ if (data->auth.auth_type == WLAN_AUTH_FILS_SK ||
+ data->auth.auth_type == WLAN_AUTH_FILS_SK_PFS) {
+ u16 expect_auth_type;
+
+ expect_auth_type = wpa_s->sme.auth_alg ==
+ WPA_AUTH_ALG_FILS_SK_PFS ? WLAN_AUTH_FILS_SK_PFS :
+ WLAN_AUTH_FILS_SK;
+ if (data->auth.auth_type != expect_auth_type) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SME: FILS Authentication response used different auth alg (%u; expected %u)",
+ data->auth.auth_type, expect_auth_type);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+ MACSTR
+ " reason=%d locally_generated=1",
+ MAC2STR(wpa_s->pending_bssid),
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ return;
+ }
+
+ if (fils_process_auth(wpa_s->wpa, wpa_s->pending_bssid,
+ data->auth.ies, data->auth.ies_len) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SME: FILS Authentication response processing failed");
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+ MACSTR
+ " reason=%d locally_generated=1",
+ MAC2STR(wpa_s->pending_bssid),
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ return;
+ }
+ }
+#endif /* CONFIG_FILS */
+
sme_associate(wpa_s, ssid->mode, data->auth.peer,
data->auth.auth_type);
}
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R
+static void remove_ie(u8 *buf, size_t *len, u8 eid)
+{
+ u8 *pos, *next, *end;
+
+ pos = (u8 *) get_ie(buf, *len, eid);
+ if (pos) {
+ next = pos + 2 + pos[1];
+ end = buf + *len;
+ *len -= 2 + pos[1];
+ os_memmove(pos, next, end - next);
+ }
+}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
+
void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
const u8 *bssid, u16 auth_type)
{
struct wpa_driver_associate_params params;
struct ieee802_11_elems elems;
+#ifdef CONFIG_FILS
+ u8 nonces[2 * FILS_NONCE_LEN];
+#endif /* CONFIG_FILS */
#ifdef CONFIG_HT_OVERRIDES
struct ieee80211_ht_capabilities htcaps;
struct ieee80211_ht_capabilities htcaps_mask;
@@ -953,6 +1422,138 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
#endif /* CONFIG_VHT_OVERRIDES */
os_memset(&params, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+ if (auth_type == WLAN_AUTH_FILS_SK ||
+ auth_type == WLAN_AUTH_FILS_SK_PFS) {
+ struct wpabuf *buf;
+ const u8 *snonce, *anonce;
+ const unsigned int max_hlp = 20;
+ struct wpabuf *hlp[max_hlp];
+ unsigned int i, num_hlp = 0;
+ struct fils_hlp_req *req;
+
+ dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+ list) {
+ hlp[num_hlp] = wpabuf_alloc(2 * ETH_ALEN + 6 +
+ wpabuf_len(req->pkt));
+ if (!hlp[num_hlp])
+ break;
+ wpabuf_put_data(hlp[num_hlp], req->dst, ETH_ALEN);
+ wpabuf_put_data(hlp[num_hlp], wpa_s->own_addr,
+ ETH_ALEN);
+ wpabuf_put_data(hlp[num_hlp],
+ "\xaa\xaa\x03\x00\x00\x00", 6);
+ wpabuf_put_buf(hlp[num_hlp], req->pkt);
+ num_hlp++;
+ if (num_hlp >= max_hlp)
+ break;
+ }
+
+ buf = fils_build_assoc_req(wpa_s->wpa, &params.fils_kek,
+ &params.fils_kek_len, &snonce,
+ &anonce,
+ (const struct wpabuf **) hlp,
+ num_hlp);
+ for (i = 0; i < num_hlp; i++)
+ wpabuf_free(hlp[i]);
+ if (!buf)
+ return;
+ wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements",
+ wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len);
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+ /* Remove RSNE and MDE to allow them to be overridden
+ * with FILS+FT specific values from
+ * fils_build_assoc_req(). */
+ remove_ie(wpa_s->sme.assoc_req_ie,
+ &wpa_s->sme.assoc_req_ie_len,
+ WLAN_EID_RSN);
+ wpa_hexdump(MSG_DEBUG,
+ "FILS: assoc_req after RSNE removal",
+ wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len);
+ remove_ie(wpa_s->sme.assoc_req_ie,
+ &wpa_s->sme.assoc_req_ie_len,
+ WLAN_EID_MOBILITY_DOMAIN);
+ wpa_hexdump(MSG_DEBUG,
+ "FILS: assoc_req after MDE removal",
+ wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len);
+ }
+#endif /* CONFIG_IEEE80211R */
+ /* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
+ if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
+ sizeof(wpa_s->sme.assoc_req_ie)) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Not enough buffer room for own AssocReq elements");
+ wpabuf_free(buf);
+ return;
+ }
+ os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
+ wpabuf_free(buf);
+ wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements",
+ wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len);
+
+ os_memcpy(nonces, snonce, FILS_NONCE_LEN);
+ os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
+ params.fils_nonces = nonces;
+ params.fils_nonces_len = sizeof(nonces);
+ }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+#ifdef CONFIG_TESTING_OPTIONS
+ if (get_ie_ext(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len,
+ WLAN_EID_EXT_OWE_DH_PARAM)) {
+ wpa_printf(MSG_INFO, "TESTING: Override OWE DH element");
+ } else
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (auth_type == WLAN_AUTH_OPEN &&
+ wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
+ struct wpabuf *owe_ie;
+ u16 group;
+
+ if (wpa_s->current_ssid && wpa_s->current_ssid->owe_group) {
+ group = wpa_s->current_ssid->owe_group;
+ } else if (wpa_s->assoc_status_code ==
+ WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
+ if (wpa_s->last_owe_group == 19)
+ group = 20;
+ else if (wpa_s->last_owe_group == 20)
+ group = 21;
+ else
+ group = OWE_DH_GROUP;
+ } else {
+ group = OWE_DH_GROUP;
+ }
+
+ wpa_s->last_owe_group = group;
+ wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group);
+ owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
+ if (!owe_ie) {
+ wpa_printf(MSG_ERROR,
+ "OWE: Failed to build IE for Association Request frame");
+ return;
+ }
+ if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(owe_ie) >
+ sizeof(wpa_s->sme.assoc_req_ie)) {
+ wpa_printf(MSG_ERROR,
+ "OWE: Not enough buffer room for own Association Request frame elements");
+ wpabuf_free(owe_ie);
+ return;
+ }
+ os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+ wpabuf_head(owe_ie), wpabuf_len(owe_ie));
+ wpa_s->sme.assoc_req_ie_len += wpabuf_len(owe_ie);
+ wpabuf_free(owe_ie);
+ }
+#endif /* CONFIG_OWE */
+
params.bssid = bssid;
params.ssid = wpa_s->sme.ssid;
params.ssid_len = wpa_s->sme.ssid_len;
@@ -962,8 +1563,11 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
wpa_s->sme.assoc_req_ie : NULL;
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+ wpa_hexdump(MSG_DEBUG, "SME: Association Request IEs",
+ params.wpa_ie, params.wpa_ie_len);
params.pairwise_suite = wpa_s->pairwise_cipher;
params.group_suite = wpa_s->group_cipher;
+ params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
params.key_mgmt_suite = wpa_s->key_mgmt;
params.wpa_proto = wpa_s->wpa_proto;
#ifdef CONFIG_HT_OVERRIDES
@@ -981,9 +1585,85 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, &params);
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_IEEE80211R
- if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
+ if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
+ get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len,
+ WLAN_EID_RIC_DATA)) {
+ /* There seems to be a pretty inconvenient bug in the Linux
+ * kernel IE splitting functionality when RIC is used. For now,
+ * skip correct behavior in IE construction here (i.e., drop the
+ * additional non-FT-specific IEs) to avoid kernel issues. This
+ * is fine since RIC is used only for testing purposes in the
+ * current implementation. */
+ wpa_printf(MSG_INFO,
+ "SME: Linux kernel workaround - do not try to include additional IEs with RIC");
params.wpa_ie = wpa_s->sme.ft_ies;
params.wpa_ie_len = wpa_s->sme.ft_ies_len;
+ } else if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
+ const u8 *rm_en, *pos, *end;
+ size_t rm_en_len = 0;
+ u8 *rm_en_dup = NULL, *wpos;
+
+ /* Remove RSNE, MDE, FTE to allow them to be overridden with
+ * FT specific values */
+ remove_ie(wpa_s->sme.assoc_req_ie,
+ &wpa_s->sme.assoc_req_ie_len,
+ WLAN_EID_RSN);
+ remove_ie(wpa_s->sme.assoc_req_ie,
+ &wpa_s->sme.assoc_req_ie_len,
+ WLAN_EID_MOBILITY_DOMAIN);
+ remove_ie(wpa_s->sme.assoc_req_ie,
+ &wpa_s->sme.assoc_req_ie_len,
+ WLAN_EID_FAST_BSS_TRANSITION);
+ rm_en = get_ie(wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len,
+ WLAN_EID_RRM_ENABLED_CAPABILITIES);
+ if (rm_en) {
+ /* Need to remove RM Enabled Capabilities element as
+ * well temporarily, so that it can be placed between
+ * RSNE and MDE. */
+ rm_en_len = 2 + rm_en[1];
+ rm_en_dup = os_memdup(rm_en, rm_en_len);
+ remove_ie(wpa_s->sme.assoc_req_ie,
+ &wpa_s->sme.assoc_req_ie_len,
+ WLAN_EID_RRM_ENABLED_CAPABILITIES);
+ }
+ wpa_hexdump(MSG_DEBUG,
+ "SME: Association Request IEs after FT IE removal",
+ wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len);
+ if (wpa_s->sme.assoc_req_ie_len + wpa_s->sme.ft_ies_len +
+ rm_en_len > sizeof(wpa_s->sme.assoc_req_ie)) {
+ wpa_printf(MSG_ERROR,
+ "SME: Not enough buffer room for FT IEs in Association Request frame");
+ os_free(rm_en_dup);
+ return;
+ }
+
+ os_memmove(wpa_s->sme.assoc_req_ie + wpa_s->sme.ft_ies_len +
+ rm_en_len,
+ wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len);
+ pos = wpa_s->sme.ft_ies;
+ end = pos + wpa_s->sme.ft_ies_len;
+ wpos = wpa_s->sme.assoc_req_ie;
+ if (*pos == WLAN_EID_RSN) {
+ os_memcpy(wpos, pos, 2 + pos[1]);
+ wpos += 2 + pos[1];
+ pos += 2 + pos[1];
+ }
+ if (rm_en_dup) {
+ os_memcpy(wpos, rm_en_dup, rm_en_len);
+ wpos += rm_en_len;
+ os_free(rm_en_dup);
+ }
+ os_memcpy(wpos, pos, end - pos);
+ wpa_s->sme.assoc_req_ie_len += wpa_s->sme.ft_ies_len +
+ rm_en_len;
+ params.wpa_ie = wpa_s->sme.assoc_req_ie;
+ params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+ wpa_hexdump(MSG_DEBUG,
+ "SME: Association Request IEs after FT override",
+ params.wpa_ie, params.wpa_ie_len);
}
#endif /* CONFIG_IEEE80211R */
params.mode = mode;
@@ -1038,6 +1718,14 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
NULL);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
+ wpa_s->last_assoc_req_wpa_ie = NULL;
+ if (params.wpa_ie)
+ wpa_s->last_assoc_req_wpa_ie =
+ wpabuf_alloc_copy(params.wpa_ie, params.wpa_ie_len);
+#endif /* CONFIG_TESTING_OPTIONS */
}
@@ -1056,10 +1744,9 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
os_free(wpa_s->sme.ft_ies);
- wpa_s->sme.ft_ies = os_malloc(ies_len);
+ wpa_s->sme.ft_ies = os_memdup(ies, ies_len);
if (wpa_s->sme.ft_ies == NULL)
return -1;
- os_memcpy(wpa_s->sme.ft_ies, ies, ies_len);
wpa_s->sme.ft_ies_len = ies_len;
return 0;
}
@@ -1226,7 +1913,7 @@ void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
sae_clear_data(&wpa_s->sme.sae);
#endif /* CONFIG_SAE */
#ifdef CONFIG_IEEE80211R
- if (wpa_s->sme.ft_ies)
+ if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used)
sme_update_ft_ies(wpa_s, NULL, NULL, 0);
#endif /* CONFIG_IEEE80211R */
}
diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h
index fd5c3b4e1ed8..f3c822025574 100644
--- a/wpa_supplicant/sme.h
+++ b/wpa_supplicant/sme.h
@@ -38,6 +38,10 @@ void sme_deinit(struct wpa_supplicant *wpa_s);
int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
+void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data);
+void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+ const u8 *auth_frame, size_t len);
#else /* CONFIG_SME */
@@ -113,6 +117,16 @@ static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s,
{
}
+static inline void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+}
+
+static inline void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+ const u8 *auth_frame, size_t len)
+{
+}
+
#endif /* CONFIG_SME */
#endif /* SME_H */
diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c
index c363b21b92b1..c94e4610893a 100644
--- a/wpa_supplicant/wifi_display.c
+++ b/wpa_supplicant/wifi_display.c
@@ -86,6 +86,7 @@ static int wifi_display_update_wfd_ie(struct wpa_global *global)
p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
p2p_set_wfd_ie_go_neg(global->p2p, NULL);
p2p_set_wfd_dev_info(global->p2p, NULL);
+ p2p_set_wfd_r2_dev_info(global->p2p, NULL);
p2p_set_wfd_assoc_bssid(global->p2p, NULL);
p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
return 0;
@@ -93,6 +94,8 @@ static int wifi_display_update_wfd_ie(struct wpa_global *global)
p2p_set_wfd_dev_info(global->p2p,
global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+ p2p_set_wfd_r2_dev_info(
+ global->p2p, global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
p2p_set_wfd_assoc_bssid(
global->p2p,
global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
@@ -133,6 +136,11 @@ static int wifi_display_update_wfd_ie(struct wpa_global *global)
if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
len += wpabuf_len(global->wfd_subelem[
WFD_SUBELEM_DEVICE_INFO]);
+
+ if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
+ len += wpabuf_len(global->wfd_subelem[
+ WFD_SUBELEM_R2_DEVICE_INFO]);
+
if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
len += wpabuf_len(global->wfd_subelem[
WFD_SUBELEM_ASSOCIATED_BSSID]);
@@ -151,6 +159,11 @@ static int wifi_display_update_wfd_ie(struct wpa_global *global)
if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
wpabuf_put_buf(buf,
global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+
+ if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
+ wpabuf_put_buf(buf,
+ global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
+
if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
wpabuf_put_buf(buf, global->wfd_subelem[
WFD_SUBELEM_ASSOCIATED_BSSID]);
diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c
index 5625d36638b5..a88cc46f3956 100644
--- a/wpa_supplicant/wmm_ac.c
+++ b/wpa_supplicant/wmm_ac.c
@@ -87,13 +87,10 @@ static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr,
}
/* copy tspec */
- _tspec = os_malloc(sizeof(*_tspec));
+ _tspec = os_memdup(tspec, sizeof(*_tspec));
if (!_tspec)
return -1;
- /* store the admitted TSPEC */
- os_memcpy(_tspec, tspec, sizeof(*_tspec));
-
if (dir != WMM_AC_DIR_DOWNLINK) {
ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time);
wpa_printf(MSG_DEBUG,
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 67a07ff7b1e7..6b68fc9e3772 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -13,6 +13,7 @@
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "rsn_supp/wpa.h"
+#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "scan.h"
@@ -84,12 +85,11 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
/* TFS IE(s) */
if (tfs_req) {
wnmtfs_ie_len = wpabuf_len(tfs_req);
- wnmtfs_ie = os_malloc(wnmtfs_ie_len);
+ wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len);
if (wnmtfs_ie == NULL) {
os_free(wnmsleep_ie);
return -1;
}
- os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
} else {
wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
if (wnmtfs_ie == NULL) {
@@ -338,6 +338,9 @@ void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
wpa_s->wnm_num_neighbor_report = 0;
os_free(wpa_s->wnm_neighbor_report_elements);
wpa_s->wnm_neighbor_report_elements = NULL;
+
+ wpabuf_free(wpa_s->coloc_intf_elems);
+ wpa_s->coloc_intf_elems = NULL;
}
@@ -501,10 +504,128 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
}
+static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
+{
+ unsigned int i;
+
+ for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
+ wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
+}
+
+
+static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
+{
+ unsigned int i;
+ struct neighbor_report *nei;
+
+ for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+ nei = &wpa_s->wnm_neighbor_report_elements[i];
+ if (nei->acceptable)
+ return wpa_bss_get_bssid(wpa_s, nei->bssid);
+ }
+
+ return NULL;
+}
+
+
+#ifdef CONFIG_MBO
static struct wpa_bss *
-compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
+get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
+ enum mbo_transition_reject_reason *reason)
{
+ struct wpa_bss *target = NULL;
+ struct wpa_bss_trans_info params;
+ struct wpa_bss_candidate_info *info = NULL;
+ struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
+ u8 *first_candidate_bssid = NULL, *pos;
+ unsigned int i;
+
+ params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
+ params.n_candidates = 0;
+ params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
+ if (!params.bssid)
+ return NULL;
+
+ pos = params.bssid;
+ for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
+ if (nei->is_first)
+ first_candidate_bssid = nei->bssid;
+ if (!nei->acceptable)
+ continue;
+ os_memcpy(pos, nei->bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ params.n_candidates++;
+ }
+
+ if (!params.n_candidates)
+ goto end;
+
+ info = wpa_drv_get_bss_trans_status(wpa_s, &params);
+ if (!info) {
+ /* If failed to get candidate BSS transition status from driver,
+ * get the first acceptable candidate from wpa_supplicant.
+ */
+ target = wpa_bss_get_bssid(wpa_s, params.bssid);
+ goto end;
+ }
+
+ /* Get the first acceptable candidate from driver */
+ for (i = 0; i < info->num; i++) {
+ if (info->candidates[i].is_accept) {
+ target = wpa_bss_get_bssid(wpa_s,
+ info->candidates[i].bssid);
+ goto end;
+ }
+ }
+
+ /* If Disassociation Imminent is set and driver rejects all the
+ * candidate select first acceptable candidate which has
+ * rssi > disassoc_imminent_rssi_threshold
+ */
+ if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+ for (i = 0; i < info->num; i++) {
+ target = wpa_bss_get_bssid(wpa_s,
+ info->candidates[i].bssid);
+ if (target &&
+ (target->level <
+ wpa_s->conf->disassoc_imminent_rssi_threshold))
+ continue;
+ goto end;
+ }
+ }
+
+ /* While sending BTM reject use reason code of the first candidate
+ * received in BTM request frame
+ */
+ if (reason) {
+ for (i = 0; i < info->num; i++) {
+ if (first_candidate_bssid &&
+ os_memcmp(first_candidate_bssid,
+ info->candidates[i].bssid, ETH_ALEN) == 0)
+ {
+ *reason = info->candidates[i].reject_reason;
+ break;
+ }
+ }
+ }
+
+ target = NULL;
+
+end:
+ os_free(params.bssid);
+ if (info) {
+ os_free(info->candidates);
+ os_free(info);
+ }
+ return target;
+}
+#endif /* CONFIG_MBO */
+
+static struct wpa_bss *
+compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
+ enum mbo_transition_reject_reason *reason)
+{
u8 i;
struct wpa_bss *bss = wpa_s->current_bss;
struct wpa_bss *target;
@@ -515,6 +636,8 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
MAC2STR(wpa_s->bssid), bss->level);
+ wnm_clear_acceptable(wpa_s);
+
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
struct neighbor_report *nei;
@@ -564,7 +687,7 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
if (wpa_s->current_ssid &&
!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
- 1)) {
+ 1, 0)) {
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
" (pref %d) does not match the current network profile",
MAC2STR(nei->bssid),
@@ -591,14 +714,26 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
continue;
}
+ nei->acceptable = 1;
+ }
+
+#ifdef CONFIG_MBO
+ if (wpa_s->wnm_mbo_trans_reason_present)
+ target = get_mbo_transition_candidate(wpa_s, reason);
+ else
+ target = get_first_acceptable(wpa_s);
+#else /* CONFIG_MBO */
+ target = get_first_acceptable(wpa_s);
+#endif /* CONFIG_MBO */
+
+ if (target) {
wpa_printf(MSG_DEBUG,
"WNM: Found an acceptable preferred transition candidate BSS "
MACSTR " (RSSI %d)",
- MAC2STR(nei->bssid), target->level);
- return target;
+ MAC2STR(target->bssid), target->level);
}
- return NULL;
+ return target;
}
@@ -651,36 +786,40 @@ static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
}
-static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info,
- u8 op_class, u8 chan, u8 phy_type, u8 pref)
+static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
+ u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
+ u8 pref)
{
- u8 *pos = buf;
+ if (wpabuf_len(*buf) + 18 >
+ IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: No room in frame for Neighbor Report element");
+ return -1;
+ }
- if (len < 18) {
+ if (wpabuf_resize(buf, 18) < 0) {
wpa_printf(MSG_DEBUG,
- "WNM: Not enough room for Neighbor Report element");
+ "WNM: Failed to allocate memory for Neighbor Report element");
return -1;
}
- *pos++ = WLAN_EID_NEIGHBOR_REPORT;
+ wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
/* length: 13 for basic neighbor report + 3 for preference subelement */
- *pos++ = 16;
- os_memcpy(pos, bssid, ETH_ALEN);
- pos += ETH_ALEN;
- WPA_PUT_LE32(pos, bss_info);
- pos += 4;
- *pos++ = op_class;
- *pos++ = chan;
- *pos++ = phy_type;
- *pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE;
- *pos++ = 1;
- *pos++ = pref;
- return pos - buf;
+ wpabuf_put_u8(*buf, 16);
+ wpabuf_put_data(*buf, bssid, ETH_ALEN);
+ wpabuf_put_le32(*buf, bss_info);
+ wpabuf_put_u8(*buf, op_class);
+ wpabuf_put_u8(*buf, chan);
+ wpabuf_put_u8(*buf, phy_type);
+ wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
+ wpabuf_put_u8(*buf, 1);
+ wpabuf_put_u8(*buf, pref);
+ return 0;
}
static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
- struct wpa_bss *bss, u8 *buf, size_t len,
+ struct wpa_bss *bss, struct wpabuf **buf,
u8 pref)
{
const u8 *ie;
@@ -729,20 +868,19 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
info = wnm_get_bss_info(wpa_s, bss);
- return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan,
- phy_type, pref);
+ return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type,
+ pref);
}
-static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
{
- u8 *pos = buf;
unsigned int i, pref = 255;
struct os_reltime now;
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (!ssid)
- return 0;
+ return;
/*
* TODO: Define when scan results are no longer valid for the candidate
@@ -750,7 +888,7 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
*/
os_get_reltime(&now);
if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
- return 0;
+ return;
wpa_printf(MSG_DEBUG,
"WNM: Add candidate list to BSS Transition Management Response frame");
@@ -758,93 +896,100 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
struct wpa_bss *bss = wpa_s->last_scan_res[i];
int res;
- if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) {
- res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--);
+ if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
+ res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
if (res == -2)
continue; /* could not build entry for BSS */
if (res < 0)
break; /* no more room for candidates */
if (pref == 1)
break;
-
- pos += res;
- len -= res;
}
}
- wpa_hexdump(MSG_DEBUG,
- "WNM: BSS Transition Management Response candidate list",
- buf, pos - buf);
-
- return pos - buf;
+ wpa_hexdump_buf(MSG_DEBUG,
+ "WNM: BSS Transition Management Response candidate list",
+ *buf);
}
+#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
+
static void wnm_send_bss_transition_mgmt_resp(
struct wpa_supplicant *wpa_s, u8 dialog_token,
- enum bss_trans_mgmt_status_code status, u8 delay,
- const u8 *target_bssid)
+ enum bss_trans_mgmt_status_code status,
+ enum mbo_transition_reject_reason reason,
+ u8 delay, const u8 *target_bssid)
{
- u8 buf[2000], *pos;
- struct ieee80211_mgmt *mgmt;
- size_t len;
+ struct wpabuf *buf;
int res;
- wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
- "to " MACSTR " dialog_token=%u status=%u delay=%d",
- MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+ wpa_printf(MSG_DEBUG,
+ "WNM: Send BSS Transition Management Response to " MACSTR
+ " dialog_token=%u status=%u reason=%u delay=%d",
+ MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
if (!wpa_s->current_bss) {
wpa_printf(MSG_DEBUG,
"WNM: Current BSS not known - drop response");
return;
}
- mgmt = (struct ieee80211_mgmt *) buf;
- os_memset(&buf, 0, sizeof(buf));
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- mgmt->u.action.category = WLAN_ACTION_WNM;
- mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
- mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
- mgmt->u.action.u.bss_tm_resp.status_code = status;
- mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
- pos = mgmt->u.action.u.bss_tm_resp.variable;
+ buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Failed to allocate memory for BTM response");
+ return;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_u8(buf, status);
+ wpabuf_put_u8(buf, delay);
if (target_bssid) {
- os_memcpy(pos, target_bssid, ETH_ALEN);
- pos += ETH_ALEN;
+ wpabuf_put_data(buf, target_bssid, ETH_ALEN);
} else if (status == WNM_BSS_TM_ACCEPT) {
/*
* P802.11-REVmc clarifies that the Target BSSID field is always
* present when status code is zero, so use a fake value here if
* no BSSID is yet known.
*/
- os_memset(pos, 0, ETH_ALEN);
- pos += ETH_ALEN;
+ wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
}
if (status == WNM_BSS_TM_ACCEPT)
- pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+ wnm_add_cand_list(wpa_s, &buf);
#ifdef CONFIG_MBO
- if (status != WNM_BSS_TM_ACCEPT) {
- pos += wpas_mbo_ie_bss_trans_reject(
- wpa_s, pos, buf + sizeof(buf) - pos,
- MBO_TRANSITION_REJECT_REASON_UNSPECIFIED);
+ if (status != WNM_BSS_TM_ACCEPT &&
+ wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
+ u8 mbo[10];
+ size_t ret;
+
+ ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
+ reason);
+ if (ret) {
+ if (wpabuf_resize(&buf, ret) < 0) {
+ wpabuf_free(buf);
+ wpa_printf(MSG_DEBUG,
+ "WNM: Failed to allocate memory for MBO IE");
+ return;
+ }
+
+ wpabuf_put_data(buf, mbo, ret);
+ }
}
#endif /* CONFIG_MBO */
- len = pos - (u8 *) &mgmt->u.action.category;
-
res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid,
- &mgmt->u.action.category, len, 0);
+ wpabuf_head_u8(buf), wpabuf_len(buf), 0);
if (res < 0) {
wpa_printf(MSG_DEBUG,
"WNM: Failed to send BSS Transition Management Response");
}
+
+ wpabuf_free(buf);
}
@@ -863,10 +1008,10 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
wpa_s->wnm_reply = 0;
wpa_printf(MSG_DEBUG,
"WNM: Sending successful BSS Transition Management Response");
- wnm_send_bss_transition_mgmt_resp(wpa_s,
- wpa_s->wnm_dialog_token,
- WNM_BSS_TM_ACCEPT,
- 0, bss->bssid);
+ wnm_send_bss_transition_mgmt_resp(
+ wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
+ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+ bss->bssid);
}
if (bss == wpa_s->current_bss) {
@@ -888,6 +1033,8 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
+ enum mbo_transition_reject_reason reason =
+ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
if (!wpa_s->wnm_neighbor_report_elements)
return 0;
@@ -909,7 +1056,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
}
/* Compare the Neighbor Report and scan results */
- bss = compare_scan_neighbor_results(wpa_s, 0);
+ bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
if (!bss) {
wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
@@ -930,7 +1077,7 @@ send_bss_resp_fail:
wpa_s->wnm_reply = 0;
wnm_send_bss_transition_mgmt_resp(wpa_s,
wpa_s->wnm_dialog_token,
- status, 0, NULL);
+ status, reason, 0, NULL);
}
wnm_deallocate_memory(wpa_s);
@@ -1118,7 +1265,7 @@ static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
return 0;
}
- bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE);
+ bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
if (!bss) {
wpa_dbg(wpa_s, MSG_DEBUG,
"WNM: Comparison of scan results against transition candidates did not find matches");
@@ -1144,6 +1291,11 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
if (end - pos < 5)
return;
+#ifdef CONFIG_MBO
+ wpa_s->wnm_mbo_trans_reason_present = 0;
+ wpa_s->wnm_mbo_transition_reason = 0;
+#endif /* CONFIG_MBO */
+
if (wpa_s->current_bss)
beacon_int = wpa_s->current_bss->beacon_int;
else
@@ -1166,10 +1318,10 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wpa_printf(MSG_INFO,
"WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
wpa_s->reject_btm_req_reason);
- wnm_send_bss_transition_mgmt_resp(wpa_s,
- wpa_s->wnm_dialog_token,
- wpa_s->reject_btm_req_reason,
- 0, NULL);
+ wnm_send_bss_transition_mgmt_resp(
+ wpa_s, wpa_s->wnm_dialog_token,
+ wpa_s->reject_btm_req_reason,
+ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
return;
}
#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
@@ -1248,6 +1400,15 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wpa_s->wnm_num_neighbor_report];
wnm_parse_neighbor_report(wpa_s, pos, len, rep);
wpa_s->wnm_num_neighbor_report++;
+#ifdef CONFIG_MBO
+ if (wpa_s->wnm_mbo_trans_reason_present &&
+ wpa_s->wnm_num_neighbor_report == 1) {
+ rep->is_first = 1;
+ wpa_printf(MSG_DEBUG,
+ "WNM: First transition candidate is "
+ MACSTR, MAC2STR(rep->bssid));
+ }
+#endif /* CONFIG_MBO */
}
pos += len;
@@ -1259,7 +1420,8 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wnm_send_bss_transition_mgmt_resp(
wpa_s, wpa_s->wnm_dialog_token,
WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
- 0, NULL);
+ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+ NULL);
return;
}
@@ -1322,19 +1484,21 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
status = WNM_BSS_TM_REJECT_UNSPECIFIED;
}
- wnm_send_bss_transition_mgmt_resp(wpa_s,
- wpa_s->wnm_dialog_token,
- status, 0, NULL);
+ wnm_send_bss_transition_mgmt_resp(
+ wpa_s, wpa_s->wnm_dialog_token, status,
+ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
}
}
+#define BTM_QUERY_MIN_SIZE 4
+
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
- u8 query_reason, int cand_list)
+ u8 query_reason,
+ const char *btm_candidates,
+ int cand_list)
{
- u8 buf[2000], *pos;
- struct ieee80211_mgmt *mgmt;
- size_t len;
+ struct wpabuf *buf;
int ret;
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
@@ -1342,28 +1506,43 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
MAC2STR(wpa_s->bssid), query_reason,
cand_list ? " candidate list" : "");
- mgmt = (struct ieee80211_mgmt *) buf;
- os_memset(&buf, 0, sizeof(buf));
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- mgmt->u.action.category = WLAN_ACTION_WNM;
- mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
- mgmt->u.action.u.bss_tm_query.dialog_token = 1;
- mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
- pos = mgmt->u.action.u.bss_tm_query.variable;
+ buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
+ if (!buf)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_u8(buf, query_reason);
if (cand_list)
- pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+ wnm_add_cand_list(wpa_s, &buf);
+
+ if (btm_candidates) {
+ const size_t max_len = 1000;
+
+ ret = wpabuf_resize(&buf, max_len);
+ if (ret < 0) {
+ wpabuf_free(buf);
+ return ret;
+ }
+
+ ret = ieee802_11_parse_candidate_list(btm_candidates,
+ wpabuf_put(buf, 0),
+ max_len);
+ if (ret < 0) {
+ wpabuf_free(buf);
+ return ret;
+ }
- len = pos - (u8 *) &mgmt->u.action.category;
+ wpabuf_put(buf, ret);
+ }
ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid,
- &mgmt->u.action.category, len, 0);
+ wpabuf_head_u8(buf), wpabuf_len(buf), 0);
+ wpabuf_free(buf);
return ret;
}
@@ -1468,6 +1647,32 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
pos = next;
continue;
}
+
+ if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
+ WPA_GET_BE24(pos) == OUI_WFA &&
+ pos[3] == HS20_WNM_T_C_ACCEPTANCE) {
+ const u8 *ie_end;
+ u8 url_len;
+ char *url;
+
+ ie_end = pos + ie_len;
+ pos += 4;
+ url_len = *pos++;
+ wpa_printf(MSG_DEBUG,
+ "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)",
+ url_len);
+ if (url_len > ie_end - pos)
+ break;
+ url = os_malloc(url_len + 1);
+ if (!url)
+ break;
+ os_memcpy(url, pos, url_len);
+ url[url_len] = '\0';
+ hs20_rx_t_c_acceptance(wpa_s, url);
+ os_free(url);
+ pos = next;
+ continue;
+ }
#endif /* CONFIG_HS20 */
pos = next;
@@ -1515,6 +1720,46 @@ static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
}
+static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *frm,
+ int len)
+{
+ u8 dialog_token, req_info, auto_report, timeout;
+
+ if (!wpa_s->conf->coloc_intf_reporting)
+ return;
+
+ /* Dialog Token [1] | Request Info [1] */
+
+ if (len < 2)
+ return;
+ dialog_token = frm[0];
+ req_info = frm[1];
+ auto_report = req_info & 0x03;
+ timeout = req_info >> 2;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")",
+ dialog_token, auto_report, timeout, MAC2STR(sa));
+
+ if (dialog_token == 0)
+ return; /* only nonzero values are used for request */
+
+ if (wpa_s->wpa_state != WPA_COMPLETED ||
+ os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WNM: Collocated Interference Request frame not from current AP - ignore it");
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u",
+ dialog_token, auto_report, timeout);
+ wpa_s->coloc_intf_dialog_token = dialog_token;
+ wpa_s->coloc_intf_auto_report = auto_report;
+ wpa_s->coloc_intf_timeout = timeout;
+}
+
+
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -1548,8 +1793,75 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
case WNM_NOTIFICATION_REQ:
ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
break;
+ case WNM_COLLOCATED_INTERFERENCE_REQ:
+ ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos,
+ end - pos);
+ break;
default:
wpa_printf(MSG_ERROR, "WNM: Unknown request");
break;
}
}
+
+
+int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
+ const struct wpabuf *elems)
+{
+ struct wpabuf *buf;
+ int ret;
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to "
+ MACSTR " (dialog token %u)",
+ MAC2STR(wpa_s->bssid), dialog_token);
+
+ buf = wpabuf_alloc(3 + wpabuf_len(elems));
+ if (!buf)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_buf(buf, elems);
+
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head_u8(buf), wpabuf_len(buf), 0);
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
+ struct wpabuf *elems)
+{
+ wpabuf_free(wpa_s->coloc_intf_elems);
+ if (elems && wpabuf_len(elems) == 0) {
+ wpabuf_free(elems);
+ elems = NULL;
+ }
+ wpa_s->coloc_intf_elems = elems;
+
+ if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems &&
+ wpa_s->coloc_intf_dialog_token &&
+ (wpa_s->coloc_intf_auto_report == 1 ||
+ wpa_s->coloc_intf_auto_report == 3)) {
+ /* TODO: Check that there has not been less than
+ * wpa_s->coloc_intf_timeout * 200 TU from the last report.
+ */
+ wnm_send_coloc_intf_report(wpa_s,
+ wpa_s->coloc_intf_dialog_token,
+ wpa_s->coloc_intf_elems);
+ }
+}
+
+
+void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WNM
+ wpa_s->coloc_intf_dialog_token = 0;
+ wpa_s->coloc_intf_auto_report = 0;
+#endif /* CONFIG_WNM */
+}
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 81d815359634..29625f8ca943 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -43,6 +43,10 @@ struct neighbor_report {
unsigned int rm_capab_present:1;
unsigned int bearing_present:1;
unsigned int bss_term_present:1;
+ unsigned int acceptable:1;
+#ifdef CONFIG_MBO
+ unsigned int is_first:1;
+#endif /* CONFIG_MBO */
struct measurement_pilot *meas_pilot;
struct multiple_bssid *mul_bssid;
int freq;
@@ -56,13 +60,21 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len);
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
- u8 query_reason, int cand_list);
+ u8 query_reason,
+ const char *btm_candidates,
+ int cand_list);
+
void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
+int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
+ const struct wpabuf *elems);
+void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
+ struct wpabuf *elems);
#ifdef CONFIG_WNM
int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s);
#else /* CONFIG_WNM */
@@ -72,6 +84,10 @@ static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
return 0;
}
+static inline void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
+{
+}
+
#endif /* CONFIG_WNM */
#endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index a848b7737db5..779355440a01 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-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.
@@ -29,7 +29,7 @@
static const char *const wpa_cli_version =
"wpa_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";
#define VENDOR_ELEM_FRAME_ID \
" 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \
@@ -60,6 +60,10 @@ static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */
static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */
static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(creds); /* struct cli_txt_entry */
+#ifdef CONFIG_AP
+static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
+#endif /* CONFIG_AP */
static void print_help(const char *cmd);
@@ -67,7 +71,9 @@ static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx);
static void wpa_cli_close_connection(void);
static char * wpa_cli_get_default_ifname(void);
static char ** wpa_list_cmd_list(void);
+static void update_creds(struct wpa_ctrl *ctrl);
static void update_networks(struct wpa_ctrl *ctrl);
+static void update_stations(struct wpa_ctrl *ctrl);
static void usage(void)
@@ -214,7 +220,7 @@ static void wpa_cli_msg_cb(char *msg, size_t len)
}
-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;
@@ -250,7 +256,7 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
}
-static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+static int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
{
return _wpa_ctrl_command(ctrl, cmd, 1);
}
@@ -331,6 +337,39 @@ static int wpa_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
}
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+
+static int wpa_cli_cmd_pmksa_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "PMKSA_GET", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_pmksa_add(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "PMKSA_ADD", 8, argc, argv);
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_cli_mesh_cmd_pmksa_get(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "MESH_PMKSA_GET", 1, argc, argv);
+}
+
+
+static int wpa_cli_mesh_cmd_pmksa_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "MESH_PMKSA_ADD", 4, argc, argv);
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
print_help(argc > 0 ? argv[0] : NULL);
@@ -437,11 +476,13 @@ static char ** wpa_cli_complete_set(const char *str, int pos)
#endif /* CONFIG_P2P */
"country", "bss_max_count", "bss_expiration_age",
"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
- "max_num_sta", "disassoc_low_ack",
+ "max_num_sta", "disassoc_low_ack", "ap_isolate",
#ifdef CONFIG_HS20
"hs20",
#endif /* CONFIG_HS20 */
"interworking", "hessid", "access_network_type", "pbc_in_m1",
+ "go_interworking", "go_access_network_type", "go_internet",
+ "go_venue_group", "go_venue_type",
"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
@@ -455,6 +496,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos)
#ifdef CONFIG_TESTING_OPTIONS
"ignore_auth_resp",
#endif /* CONFIG_TESTING_OPTIONS */
+ "relative_rssi", "relative_band_adjust",
};
int i, num_fields = ARRAY_SIZE(fields);
@@ -531,15 +573,18 @@ static char ** wpa_cli_complete_get(const char *str, int pos)
#endif /* CONFIG_P2P */
"bss_max_count", "bss_expiration_age",
"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
- "max_num_sta", "disassoc_low_ack",
+ "max_num_sta", "disassoc_low_ack", "ap_isolate",
#ifdef CONFIG_HS20
"hs20",
#endif /* CONFIG_HS20 */
"interworking", "access_network_type", "pbc_in_m1", "autoscan",
+ "go_interworking", "go_access_network_type", "go_internet",
+ "go_venue_group", "go_venue_type",
"wps_nfc_dev_pw_id", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
"dtim_period", "beacon_int", "ignore_old_scan_res",
"scan_cur_freq", "sched_scan_interval",
+ "sched_scan_start_delay",
"tdls_external_control", "osu_dir", "wowlan_triggers",
"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
@@ -639,13 +684,6 @@ static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
-static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
-{
- return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv);
-}
-
-
static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv);
@@ -1332,7 +1370,8 @@ static const char *network_fields[] = {
"ssid", "scan_ssid", "bssid", "bssid_blacklist",
"bssid_whitelist", "psk", "proto", "key_mgmt",
"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
- "freq_list", "max_oper_chwidth",
+ "freq_list", "max_oper_chwidth", "ht40", "vht", "vht_center_freq1",
+ "vht_center_freq2", "ht",
#ifdef IEEE8021X_EAPOL
"eap", "identity", "anonymous_identity", "password", "ca_cert",
"ca_path", "client_cert", "private_key", "private_key_passwd",
@@ -1352,7 +1391,7 @@ static const char *network_fields[] = {
"eap_workaround", "pac_file", "fragment_size", "ocsp",
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_MESH
- "mode", "no_auto_peer",
+ "mode", "no_auto_peer", "mesh_rssi_threshold",
#else /* CONFIG_MESH */
"mode",
#endif /* CONFIG_MESH */
@@ -1360,7 +1399,7 @@ static const char *network_fields[] = {
#ifdef CONFIG_IEEE80211W
"ieee80211w",
#endif /* CONFIG_IEEE80211W */
- "peerkey", "mixed_cell", "frequency", "fixed_freq",
+ "mixed_cell", "frequency", "fixed_freq",
#ifdef CONFIG_MESH
"mesh_basic_rates", "dot11MeshMaxRetries",
"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
@@ -1386,6 +1425,9 @@ static const char *network_fields[] = {
"ap_max_inactivity", "dtim_period", "beacon_int",
#ifdef CONFIG_MACSEC
"macsec_policy",
+ "macsec_integ_only",
+ "macsec_port",
+ "mka_priority",
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_HS20
"update_identifier",
@@ -1479,14 +1521,56 @@ static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- return wpa_ctrl_command(ctrl, "ADD_CRED");
+ int res = wpa_ctrl_command(ctrl, "ADD_CRED");
+ if (interactive)
+ update_creds(ctrl);
+ return res;
}
static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv);
+ int res = wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv);
+ if (interactive)
+ update_creds(ctrl);
+ return res;
+}
+
+
+static const char * const cred_fields[] = {
+ "temporary", "priority", "sp_priority", "pcsc", "eap",
+ "update_identifier", "min_dl_bandwidth_home", "min_ul_bandwidth_home",
+ "min_dl_bandwidth_roaming", "min_ul_bandwidth_roaming", "max_bss_load",
+ "req_conn_capab", "ocsp", "sim_num", "realm", "username", "password",
+ "ca_cert", "client_cert", "private_key", "private_key_passwd", "imsi",
+ "milenage", "domain_suffix_match", "domain", "phase1", "phase2",
+ "roaming_consortium", "required_roaming_consortium", "excluded_ssid",
+ "roaming_partner", "provisioning_sp"
+};
+
+
+static char ** wpa_cli_complete_cred(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ int i, num_fields = ARRAY_SIZE(cred_fields);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&creds);
+ break;
+ case 2:
+ res = os_calloc(num_fields + 1, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; i < num_fields; i++) {
+ res[i] = os_strdup(cred_fields[i]);
+ if (res[i] == NULL)
+ break;
+ }
+ }
+ return res;
}
@@ -1736,8 +1820,23 @@ static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
-static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
- char *addr, size_t addr_len)
+static char ** wpa_cli_complete_sta(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 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;
@@ -1765,9 +1864,11 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
}
buf[len] = '\0';
- if (os_memcmp(buf, "FAIL", 4) == 0)
+ if (os_memcmp(buf, "FAIL", 4) == 0 ||
+ os_memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
return -1;
- printf("%s", buf);
+ if (print)
+ printf("%s", buf);
pos = buf;
while (*pos != '\0' && *pos != '\n')
@@ -1782,16 +1883,33 @@ static int wpa_cli_cmd_all_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)))
+ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
return 0;
do {
os_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 wpa_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 wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1799,12 +1917,43 @@ static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
}
+static char ** wpa_cli_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 wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
}
+
+static char ** wpa_cli_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;
+}
+
+
static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -2176,7 +2325,7 @@ static char ** wpa_cli_complete_p2p_peer(const char *str, int pos)
}
-static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd,
+static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, const char *cmd,
char *addr, size_t addr_len,
int discovered)
{
@@ -2338,6 +2487,8 @@ static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc,
return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv);
}
+#endif /* CONFIG_P2P */
+
static int wpa_cli_cmd_vendor_elem_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
@@ -2359,7 +2510,6 @@ static int wpa_cli_cmd_vendor_elem_remove(struct wpa_ctrl *ctrl, int argc,
return wpa_cli_cmd(ctrl, "VENDOR_ELEM_REMOVE", 2, argc, argv);
}
-#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
@@ -2726,6 +2876,101 @@ static int wpa_cli_cmd_p2p_lo_stop(struct wpa_ctrl *ctrl, int argc,
}
+#ifdef CONFIG_DPP
+
+static int wpa_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN");
+}
+
+
+static int wpa_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_DPP */
+
+
enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01
@@ -2798,6 +3043,22 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "pmksa_flush", wpa_cli_cmd_pmksa_flush, NULL,
cli_cmd_flag_none,
"= flush PMKSA cache entries" },
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+ { "pmksa_get", wpa_cli_cmd_pmksa_get, NULL,
+ cli_cmd_flag_none,
+ "<network_id> = fetch all stored PMKSA cache entries" },
+ { "pmksa_add", wpa_cli_cmd_pmksa_add, NULL,
+ cli_cmd_flag_sensitive,
+ "<network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> = store PMKSA cache entry from external storage" },
+#ifdef CONFIG_MESH
+ { "mesh_pmksa_get", wpa_cli_mesh_cmd_pmksa_get, NULL,
+ cli_cmd_flag_none,
+ "<peer MAC address | any> = fetch all stored mesh PMKSA cache entries" },
+ { "mesh_pmksa_add", wpa_cli_mesh_cmd_pmksa_add, NULL,
+ cli_cmd_flag_sensitive,
+ "<BSSID> <PMKID> <PMK> <expiration in seconds> = store mesh PMKSA cache entry from external storage" },
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
{ "reassociate", wpa_cli_cmd_reassociate, NULL,
cli_cmd_flag_none,
"= force reassociation" },
@@ -2807,30 +3068,30 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<BSSID> = force preauthentication" },
- { "identity", wpa_cli_cmd_identity, NULL,
+ { "identity", wpa_cli_cmd_identity, wpa_cli_complete_network_id,
cli_cmd_flag_none,
"<network id> <identity> = configure identity for an SSID" },
- { "password", wpa_cli_cmd_password, NULL,
+ { "password", wpa_cli_cmd_password, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <password> = configure password for an SSID" },
- { "new_password", wpa_cli_cmd_new_password, NULL,
- cli_cmd_flag_sensitive,
+ { "new_password", wpa_cli_cmd_new_password,
+ wpa_cli_complete_network_id, cli_cmd_flag_sensitive,
"<network id> <password> = change password for an SSID" },
- { "pin", wpa_cli_cmd_pin, NULL,
+ { "pin", wpa_cli_cmd_pin, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <pin> = configure pin for an SSID" },
- { "otp", wpa_cli_cmd_otp, NULL,
+ { "otp", wpa_cli_cmd_otp, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <password> = configure one-time-password for an SSID"
},
- { "passphrase", wpa_cli_cmd_passphrase, NULL,
+ { "passphrase", wpa_cli_cmd_passphrase, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <passphrase> = configure private key passphrase\n"
" for an SSID" },
- { "sim", wpa_cli_cmd_sim, NULL,
+ { "sim", wpa_cli_cmd_sim, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <pin> = report SIM operation result" },
- { "bssid", wpa_cli_cmd_bssid, NULL,
+ { "bssid", wpa_cli_cmd_bssid, wpa_cli_complete_network_id,
cli_cmd_flag_none,
"<network id> <BSSID> = set preferred BSSID for an SSID" },
{ "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss,
@@ -2884,10 +3145,10 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "remove_cred", wpa_cli_cmd_remove_cred, NULL,
cli_cmd_flag_none,
"<cred id> = remove a credential" },
- { "set_cred", wpa_cli_cmd_set_cred, NULL,
+ { "set_cred", wpa_cli_cmd_set_cred, wpa_cli_complete_cred,
cli_cmd_flag_sensitive,
"<cred id> <variable> <value> = set credential variables" },
- { "get_cred", wpa_cli_cmd_get_cred, NULL,
+ { "get_cred", wpa_cli_cmd_get_cred, wpa_cli_complete_cred,
cli_cmd_flag_none,
"<cred id> <variable> = get credential variables" },
{ "save_config", wpa_cli_cmd_save_config, NULL,
@@ -2951,9 +3212,6 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "bss_flush", wpa_cli_cmd_bss_flush, NULL,
cli_cmd_flag_none,
"<value> = set BSS flush age (0 by default)" },
- { "stkstart", wpa_cli_cmd_stkstart, NULL,
- cli_cmd_flag_none,
- "<addr> = request STK negotiation with <addr>" },
{ "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> = request over-the-DS FT with <addr>" },
@@ -3029,17 +3287,20 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
cli_cmd_flag_none,
"<addr> = request RSN authentication with <addr> in IBSS" },
#ifdef CONFIG_AP
- { "sta", wpa_cli_cmd_sta, NULL,
+ { "sta", wpa_cli_cmd_sta, wpa_cli_complete_sta,
cli_cmd_flag_none,
"<addr> = get information about an associated station (AP)" },
{ "all_sta", wpa_cli_cmd_all_sta, NULL,
cli_cmd_flag_none,
"= get information about all associated stations (AP)" },
- { "deauthenticate", wpa_cli_cmd_deauthenticate, NULL,
+ { "list_sta", wpa_cli_cmd_list_sta, NULL,
cli_cmd_flag_none,
+ "= list all stations (AP)" },
+ { "deauthenticate", wpa_cli_cmd_deauthenticate,
+ wpa_cli_complete_deauthenticate, cli_cmd_flag_none,
"<addr> = deauthenticate a station" },
- { "disassociate", wpa_cli_cmd_disassociate, NULL,
- cli_cmd_flag_none,
+ { "disassociate", wpa_cli_cmd_disassociate,
+ wpa_cli_complete_disassociate, cli_cmd_flag_none,
"<addr> = disassociate a station" },
{ "chan_switch", wpa_cli_cmd_chanswitch, NULL,
cli_cmd_flag_none,
@@ -3168,6 +3429,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "p2p_remove_client", wpa_cli_cmd_p2p_remove_client,
wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
"<address|iface=address> = remove a peer from all groups" },
+#endif /* CONFIG_P2P */
{ "vendor_elem_add", wpa_cli_cmd_vendor_elem_add, NULL,
cli_cmd_flag_none,
"<frame id> <hexdump of elem(s)> = add vendor specific IEs to frame(s)\n"
@@ -3180,7 +3442,6 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
cli_cmd_flag_none,
"<frame id> <hexdump of elem(s)> = remove vendor specific IE(s) in frame(s)\n"
VENDOR_ELEM_FRAME_ID },
-#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
{ "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
cli_cmd_flag_none,
@@ -3285,7 +3546,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
"<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
{ "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none,
- "<query reason> [list] = Send BSS Transition Management Query" },
+ "<query reason> [list]"
+ " [neighbor=<BSSID>,<BSSID information>,<operating class>,<channel number>,<PHY type>[,<hexdump of optional subelements>]"
+ " = Send BSS Transition Management Query" },
#endif /* CONFIG_WNM */
{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
"<params..> = Sent unprocessed command" },
@@ -3320,6 +3583,44 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL,
cli_cmd_flag_none,
"= stop P2P listen offload" },
+#ifdef CONFIG_DPP
+ { "dpp_qr_code", wpa_cli_cmd_dpp_qr_code, NULL, cli_cmd_flag_none,
+ "report a scanned DPP URI from a QR Code" },
+ { "dpp_bootstrap_gen", wpa_cli_cmd_dpp_bootstrap_gen, NULL,
+ cli_cmd_flag_sensitive,
+ "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+ { "dpp_bootstrap_remove", wpa_cli_cmd_dpp_bootstrap_remove, NULL,
+ cli_cmd_flag_none,
+ "*|<id> = remove DPP bootstrap information" },
+ { "dpp_bootstrap_get_uri", wpa_cli_cmd_dpp_bootstrap_get_uri, NULL,
+ cli_cmd_flag_none,
+ "<id> = get DPP bootstrap URI" },
+ { "dpp_bootstrap_info", wpa_cli_cmd_dpp_bootstrap_info, NULL,
+ cli_cmd_flag_none,
+ "<id> = show DPP bootstrap information" },
+ { "dpp_auth_init", wpa_cli_cmd_dpp_auth_init, NULL, cli_cmd_flag_none,
+ "peer=<id> [own=<id>] = initiate DPP bootstrapping" },
+ { "dpp_listen", wpa_cli_cmd_dpp_listen, NULL, cli_cmd_flag_none,
+ "<freq in MHz> = start DPP listen" },
+ { "dpp_stop_listen", wpa_cli_cmd_dpp_stop_listen, NULL,
+ cli_cmd_flag_none,
+ "= stop DPP listen" },
+ { "dpp_configurator_add", wpa_cli_cmd_dpp_configurator_add, NULL,
+ cli_cmd_flag_sensitive,
+ "[curve=..] [key=..] = add DPP configurator" },
+ { "dpp_configurator_remove", wpa_cli_cmd_dpp_configurator_remove, NULL,
+ cli_cmd_flag_none,
+ "*|<id> = remove DPP configurator" },
+ { "dpp_configurator_get_key", wpa_cli_cmd_dpp_configurator_get_key,
+ NULL, cli_cmd_flag_none,
+ "<id> = Get DPP configurator's private key" },
+ { "dpp_pkex_add", wpa_cli_cmd_dpp_pkex_add, NULL,
+ cli_cmd_flag_sensitive,
+ "add PKEX code" },
+ { "dpp_pkex_remove", wpa_cli_cmd_dpp_pkex_remove, NULL,
+ cli_cmd_flag_none,
+ "*|<id> = remove DPP pkex information" },
+#endif /* CONFIG_DPP */
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
};
@@ -3638,6 +3939,10 @@ static void wpa_cli_action_process(const char *msg)
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_SUCCESS)) {
wpa_cli_exec(action_file, ifname, pos);
+ } else if (str_starts(pos, WPS_EVENT_ACTIVE)) {
+ wpa_cli_exec(action_file, ifname, pos);
+ } else if (str_starts(pos, WPS_EVENT_TIMEOUT)) {
+ wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_FAIL)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, AP_STA_CONNECTED)) {
@@ -3650,6 +3955,8 @@ static void wpa_cli_action_process(const char *msg)
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
wpa_cli_exec(action_file, ifname, pos);
+ } else if (str_starts(pos, HS20_T_C_ACCEPTANCE)) {
+ wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPA_EVENT_TERMINATING)) {
printf("wpa_supplicant is terminating - stop monitoring\n");
wpa_cli_quit = 1;
@@ -3675,6 +3982,7 @@ static void wpa_cli_reconnect(void)
edit_clear_line();
printf("\rConnection to wpa_supplicant re-established\n");
edit_redraw();
+ update_stations(ctrl_conn);
}
}
@@ -3897,7 +4205,7 @@ static void update_bssid_list(struct wpa_ctrl *ctrl)
char buf[4096];
size_t len = sizeof(buf);
int ret;
- char *cmd = "BSS RANGE=ALL MASK=0x2";
+ const char *cmd = "BSS RANGE=ALL MASK=0x2";
char *pos, *end;
if (ctrl == NULL)
@@ -3928,7 +4236,7 @@ static void update_ifnames(struct wpa_ctrl *ctrl)
char buf[4096];
size_t len = sizeof(buf);
int ret;
- char *cmd = "INTERFACES";
+ const char *cmd = "INTERFACES";
char *pos, *end;
char txt[200];
@@ -3955,12 +4263,44 @@ static void update_ifnames(struct wpa_ctrl *ctrl)
}
+static void update_creds(struct wpa_ctrl *ctrl)
+{
+ char buf[4096];
+ size_t len = sizeof(buf);
+ int ret;
+ const char *cmd = "LIST_CREDS";
+ char *pos, *end;
+ int header = 1;
+
+ cli_txt_list_flush(&creds);
+
+ if (ctrl == NULL)
+ return;
+ ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
+ if (ret < 0)
+ return;
+ buf[len] = '\0';
+
+ pos = buf;
+ while (pos) {
+ end = os_strchr(pos, '\n');
+ if (end == NULL)
+ break;
+ *end = '\0';
+ if (!header)
+ cli_txt_list_add_word(&creds, pos, '\t');
+ header = 0;
+ pos = end + 1;
+ }
+}
+
+
static void update_networks(struct wpa_ctrl *ctrl)
{
char buf[4096];
size_t len = sizeof(buf);
int ret;
- char *cmd = "LIST_NETWORKS";
+ const char *cmd = "LIST_NETWORKS";
char *pos, *end;
int header = 1;
@@ -3987,6 +4327,27 @@ static void update_networks(struct wpa_ctrl *ctrl)
}
+static void update_stations(struct wpa_ctrl *ctrl)
+{
+#ifdef CONFIG_AP
+ 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);
+#endif /* CONFIG_AP */
+}
+
+
static void try_connection(void *eloop_ctx, void *timeout_ctx)
{
if (ctrl_conn)
@@ -4007,7 +4368,9 @@ static void try_connection(void *eloop_ctx, void *timeout_ctx)
}
update_bssid_list(ctrl_conn);
+ update_creds(ctrl_conn);
update_networks(ctrl_conn);
+ update_stations(ctrl_conn);
if (warning_displayed)
printf("Connection established.\n");
@@ -4029,6 +4392,7 @@ static void wpa_cli_interactive(void)
cli_txt_list_flush(&p2p_groups);
cli_txt_list_flush(&bsses);
cli_txt_list_flush(&ifnames);
+ cli_txt_list_flush(&creds);
cli_txt_list_flush(&networks);
if (edit_started)
edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
@@ -4254,6 +4618,7 @@ int main(int argc, char *argv[])
"control interface\n");
}
}
+ update_stations(ctrl_conn);
}
}
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
index 9b568f0f7c67..adca1cce13ee 100644
--- a/wpa_supplicant/wpa_passphrase.c
+++ b/wpa_supplicant/wpa_passphrase.c
@@ -17,6 +17,7 @@ int main(int argc, char *argv[])
unsigned char psk[32];
int i;
char *ssid, *passphrase, buf[64], *pos;
+ size_t len;
if (argc < 2) {
printf("usage: wpa_passphrase <ssid> [passphrase]\n"
@@ -47,10 +48,15 @@ int main(int argc, char *argv[])
passphrase = buf;
}
- if (os_strlen(passphrase) < 8 || os_strlen(passphrase) > 63) {
+ len = os_strlen(passphrase);
+ if (len < 8 || len > 63) {
printf("Passphrase must be 8..63 characters\n");
return 1;
}
+ if (has_ctrl_char((u8 *) passphrase, len)) {
+ printf("Invalid passphrase character\n");
+ return 1;
+ }
pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32);
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 511df4f18148..b3ad45eca516 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -21,6 +21,7 @@
#include "common/privsep_commands.h"
#include "common/ieee802_11_defs.h"
+#define WPA_PRIV_MAX_L2 3
struct wpa_priv_interface {
struct wpa_priv_interface *next;
@@ -35,11 +36,16 @@ struct wpa_priv_interface {
void *drv_priv;
void *drv_global_priv;
struct sockaddr_un drv_addr;
+ socklen_t drv_addr_len;
int wpas_registered;
- /* TODO: add support for multiple l2 connections */
- struct l2_packet_data *l2;
- struct sockaddr_un l2_addr;
+ struct l2_packet_data *l2[WPA_PRIV_MAX_L2];
+ struct sockaddr_un l2_addr[WPA_PRIV_MAX_L2];
+ socklen_t l2_addr_len[WPA_PRIV_MAX_L2];
+ struct wpa_priv_l2 {
+ struct wpa_priv_interface *parent;
+ int idx;
+ } l2_ctx[WPA_PRIV_MAX_L2];
};
struct wpa_priv_global {
@@ -48,8 +54,10 @@ struct wpa_priv_global {
static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
- struct sockaddr_un *from)
+ struct sockaddr_un *from, socklen_t fromlen)
{
+ int i;
+
if (iface->drv_priv) {
wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance");
if (iface->driver->deinit)
@@ -62,11 +70,13 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
iface->wpas_registered = 0;
}
- if (iface->l2) {
- wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
- "instance");
- l2_packet_deinit(iface->l2);
- iface->l2 = NULL;
+ for (i = 0; i < WPA_PRIV_MAX_L2; i++) {
+ if (iface->l2[i]) {
+ wpa_printf(MSG_DEBUG,
+ "Cleaning up forgotten l2_packet instance");
+ l2_packet_deinit(iface->l2[i]);
+ iface->l2[i] = NULL;
+ }
}
if (iface->driver->init2) {
@@ -96,7 +106,8 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface "
"'%s'", iface->driver_name, iface->ifname);
- os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr));
+ os_memcpy(&iface->drv_addr, from, fromlen);
+ iface->drv_addr_len = fromlen;
iface->wpas_registered = 1;
if (iface->driver->set_param &&
@@ -123,18 +134,43 @@ static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface,
static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface,
- char *buf, size_t len)
+ void *buf, size_t len)
{
struct wpa_driver_scan_params params;
+ struct privsep_cmd_scan *scan;
+ unsigned int i;
+ int freqs[PRIVSEP_MAX_SCAN_FREQS + 1];
if (iface->drv_priv == NULL)
return;
+ if (len < sizeof(*scan)) {
+ wpa_printf(MSG_DEBUG, "Invalid scan request");
+ return;
+ }
+
+ scan = buf;
+
os_memset(&params, 0, sizeof(params));
- if (len) {
- params.ssids[0].ssid = (u8 *) buf;
- params.ssids[0].ssid_len = len;
- params.num_ssids = 1;
+ if (scan->num_ssids > WPAS_MAX_SCAN_SSIDS) {
+ wpa_printf(MSG_DEBUG, "Invalid scan request (num_ssids)");
+ return;
+ }
+ params.num_ssids = scan->num_ssids;
+ for (i = 0; i < scan->num_ssids; i++) {
+ params.ssids[i].ssid = scan->ssids[i];
+ params.ssids[i].ssid_len = scan->ssid_lens[i];
+ }
+
+ if (scan->num_freqs > PRIVSEP_MAX_SCAN_FREQS) {
+ wpa_printf(MSG_DEBUG, "Invalid scan request (num_freqs)");
+ return;
+ }
+ if (scan->num_freqs) {
+ for (i = 0; i < scan->num_freqs; i++)
+ freqs[i] = scan->freqs[i];
+ freqs[i] = 0;
+ params.freqs = freqs;
}
if (iface->driver->scan2)
@@ -143,7 +179,8 @@ static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface,
static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
- struct sockaddr_un *from)
+ struct sockaddr_un *from,
+ socklen_t fromlen)
{
struct wpa_scan_results *res;
u8 *buf = NULL, *pos, *end;
@@ -165,7 +202,7 @@ static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
for (i = 0; i < res->num; i++) {
struct wpa_scan_res *r = res->res[i];
- val = sizeof(*r) + r->ie_len;
+ val = sizeof(*r) + r->ie_len + r->beacon_ie_len;
if (end - pos < (int) sizeof(int) + val)
break;
os_memcpy(pos, &val, sizeof(int));
@@ -174,8 +211,7 @@ static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
pos += val;
}
- sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from,
- sizeof(*from));
+ sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from, fromlen);
os_free(buf);
wpa_scan_results_free(res);
@@ -184,21 +220,21 @@ static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
fail:
os_free(buf);
wpa_scan_results_free(res);
- sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+ sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
}
static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface,
- struct sockaddr_un *from)
+ struct sockaddr_un *from,
+ socklen_t fromlen)
{
if (iface->drv_priv == NULL)
return;
if (iface->driver->get_scan_results2)
- wpa_priv_get_scan_results2(iface, from);
+ wpa_priv_get_scan_results2(iface, from, fromlen);
else
- sendto(iface->fd, "", 0, 0, (struct sockaddr *) from,
- sizeof(*from));
+ sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
}
@@ -218,7 +254,7 @@ static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface,
}
auth = buf;
- if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) {
+ if (sizeof(*auth) + auth->ie_len + auth->auth_data_len > len) {
wpa_printf(MSG_DEBUG, "Authentication request overflow");
return;
}
@@ -244,9 +280,9 @@ static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface,
params.ie = (u8 *) (auth + 1);
params.ie_len = auth->ie_len;
}
- if (auth->sae_data_len) {
- params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len;
- params.sae_data_len = auth->sae_data_len;
+ if (auth->auth_data_len) {
+ params.auth_data = ((u8 *) (auth + 1)) + auth->ie_len;
+ params.auth_data_len = auth->auth_data_len;
}
res = iface->driver->authenticate(iface->drv_priv, &params);
@@ -303,7 +339,7 @@ static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface,
- struct sockaddr_un *from)
+ struct sockaddr_un *from, socklen_t fromlen)
{
u8 bssid[ETH_ALEN];
@@ -315,16 +351,16 @@ static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface,
goto fail;
sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from,
- sizeof(*from));
+ fromlen);
return;
fail:
- sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+ sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
}
static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
- struct sockaddr_un *from)
+ struct sockaddr_un *from, socklen_t fromlen)
{
u8 ssid[sizeof(int) + SSID_MAX_LEN];
int res;
@@ -335,17 +371,18 @@ static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
if (iface->driver->get_ssid == NULL)
goto fail;
+ os_memset(ssid, 0, sizeof(ssid));
res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]);
if (res < 0 || res > SSID_MAX_LEN)
goto fail;
os_memcpy(ssid, &res, sizeof(int));
sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from,
- sizeof(*from));
+ fromlen);
return;
fail:
- sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+ sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
}
@@ -378,7 +415,7 @@ static void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface,
static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface,
- struct sockaddr_un *from)
+ struct sockaddr_un *from, socklen_t fromlen)
{
struct wpa_driver_capa capa;
@@ -394,18 +431,19 @@ static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface,
capa.extended_capa_mask = NULL;
capa.extended_capa_len = 0;
sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
- sizeof(*from));
+ fromlen);
return;
fail:
- sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+ sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
}
static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
- struct wpa_priv_interface *iface = ctx;
+ struct wpa_priv_l2 *l2_ctx = ctx;
+ struct wpa_priv_interface *iface = l2_ctx->parent;
struct msghdr msg;
struct iovec io[2];
@@ -417,8 +455,8 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
os_memset(&msg, 0, sizeof(msg));
msg.msg_iov = io;
msg.msg_iovlen = 2;
- msg.msg_name = &iface->l2_addr;
- msg.msg_namelen = sizeof(iface->l2_addr);
+ msg.msg_name = &iface->l2_addr[l2_ctx->idx];
+ msg.msg_namelen = iface->l2_addr_len[l2_ctx->idx];
if (sendmsg(iface->fd, &msg, 0) < 0) {
wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno));
@@ -426,14 +464,23 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
}
+static int wpa_priv_allowed_l2_proto(u16 proto)
+{
+ return proto == ETH_P_EAPOL || proto == ETH_P_RSN_PREAUTH ||
+ proto == ETH_P_80211_ENCAP;
+}
+
+
static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
struct sockaddr_un *from,
+ socklen_t fromlen,
void *buf, size_t len)
{
int *reg_cmd = buf;
u8 own_addr[ETH_ALEN];
int res;
u16 proto;
+ int idx;
if (len != 2 * sizeof(int)) {
wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu",
@@ -442,50 +489,69 @@ static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
}
proto = reg_cmd[0];
- if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH &&
- proto != ETH_P_80211_ENCAP) {
+ if (!wpa_priv_allowed_l2_proto(proto)) {
wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
"ethertype 0x%x", proto);
return;
}
- if (iface->l2) {
- wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
- "instance");
- l2_packet_deinit(iface->l2);
- iface->l2 = NULL;
+ for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
+ if (!iface->l2[idx])
+ break;
+ }
+ if (idx == WPA_PRIV_MAX_L2) {
+ wpa_printf(MSG_DEBUG, "No free l2_packet connection found");
+ return;
}
- os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr));
+ os_memcpy(&iface->l2_addr[idx], from, fromlen);
+ iface->l2_addr_len[idx] = fromlen;
- iface->l2 = l2_packet_init(iface->ifname, NULL, proto,
- wpa_priv_l2_rx, iface, reg_cmd[1]);
- if (iface->l2 == NULL) {
+ iface->l2_ctx[idx].idx = idx;
+ iface->l2_ctx[idx].parent = iface;
+ iface->l2[idx] = l2_packet_init(iface->ifname, NULL, proto,
+ wpa_priv_l2_rx, &iface->l2_ctx[idx],
+ reg_cmd[1]);
+ if (!iface->l2[idx]) {
wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet "
"instance for protocol %d", proto);
return;
}
- if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) {
+ if (l2_packet_get_own_addr(iface->l2[idx], own_addr) < 0) {
wpa_printf(MSG_DEBUG, "Failed to get own address from "
"l2_packet");
- l2_packet_deinit(iface->l2);
- iface->l2 = NULL;
+ l2_packet_deinit(iface->l2[idx]);
+ iface->l2[idx] = NULL;
return;
}
res = sendto(iface->fd, own_addr, ETH_ALEN, 0,
- (struct sockaddr *) from, sizeof(*from));
- wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res);
+ (struct sockaddr *) from, fromlen);
+ wpa_printf(MSG_DEBUG, "L2 registration[idx=%d]: res=%d", idx, res);
}
static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
- struct sockaddr_un *from)
+ struct sockaddr_un *from,
+ socklen_t fromlen)
{
- if (iface->l2) {
- l2_packet_deinit(iface->l2);
- iface->l2 = NULL;
+ int idx;
+
+ for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
+ if (iface->l2_addr_len[idx] == fromlen &&
+ os_memcmp(&iface->l2_addr[idx], from, fromlen) == 0)
+ break;
+ }
+ if (idx == WPA_PRIV_MAX_L2) {
+ wpa_printf(MSG_DEBUG,
+ "No registered l2_packet socket found for unregister request");
+ return;
+ }
+
+ if (iface->l2[idx]) {
+ l2_packet_deinit(iface->l2[idx]);
+ iface->l2[idx] = NULL;
}
}
@@ -493,20 +559,36 @@ static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface,
struct sockaddr_un *from)
{
- if (iface->l2)
- l2_packet_notify_auth_start(iface->l2);
+ int idx;
+
+ for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
+ if (iface->l2[idx])
+ l2_packet_notify_auth_start(iface->l2[idx]);
+ }
}
static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
- struct sockaddr_un *from,
+ struct sockaddr_un *from, socklen_t fromlen,
void *buf, size_t len)
{
u8 *dst_addr;
u16 proto;
int res;
+ int idx;
- if (iface->l2 == NULL)
+ for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
+ if (iface->l2_addr_len[idx] == fromlen &&
+ os_memcmp(&iface->l2_addr[idx], from, fromlen) == 0)
+ break;
+ }
+ if (idx == WPA_PRIV_MAX_L2) {
+ wpa_printf(MSG_DEBUG,
+ "No registered l2_packet socket found for send request");
+ return;
+ }
+
+ if (iface->l2[idx] == NULL)
return;
if (len < ETH_ALEN + 2) {
@@ -518,15 +600,15 @@ static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
dst_addr = buf;
os_memcpy(&proto, buf + ETH_ALEN, 2);
- if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
+ if (!wpa_priv_allowed_l2_proto(proto)) {
wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
"0x%x", proto);
return;
}
- res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2,
- len - ETH_ALEN - 2);
- wpa_printf(MSG_DEBUG, "L2 send: res=%d", res);
+ res = l2_packet_send(iface->l2[idx], dst_addr, proto,
+ buf + ETH_ALEN + 2, len - ETH_ALEN - 2);
+ wpa_printf(MSG_DEBUG, "L2 send[idx=%d]: res=%d", idx, res);
}
@@ -571,7 +653,7 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
switch (cmd) {
case PRIVSEP_CMD_REGISTER:
- wpa_priv_cmd_register(iface, &from);
+ wpa_priv_cmd_register(iface, &from, fromlen);
break;
case PRIVSEP_CMD_UNREGISTER:
wpa_priv_cmd_unregister(iface, &from);
@@ -580,34 +662,35 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
wpa_priv_cmd_scan(iface, cmd_buf, cmd_len);
break;
case PRIVSEP_CMD_GET_SCAN_RESULTS:
- wpa_priv_cmd_get_scan_results(iface, &from);
+ wpa_priv_cmd_get_scan_results(iface, &from, fromlen);
break;
case PRIVSEP_CMD_ASSOCIATE:
wpa_priv_cmd_associate(iface, cmd_buf, cmd_len);
break;
case PRIVSEP_CMD_GET_BSSID:
- wpa_priv_cmd_get_bssid(iface, &from);
+ wpa_priv_cmd_get_bssid(iface, &from, fromlen);
break;
case PRIVSEP_CMD_GET_SSID:
- wpa_priv_cmd_get_ssid(iface, &from);
+ wpa_priv_cmd_get_ssid(iface, &from, fromlen);
break;
case PRIVSEP_CMD_SET_KEY:
wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len);
break;
case PRIVSEP_CMD_GET_CAPA:
- wpa_priv_cmd_get_capa(iface, &from);
+ wpa_priv_cmd_get_capa(iface, &from, fromlen);
break;
case PRIVSEP_CMD_L2_REGISTER:
- wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len);
+ wpa_priv_cmd_l2_register(iface, &from, fromlen,
+ cmd_buf, cmd_len);
break;
case PRIVSEP_CMD_L2_UNREGISTER:
- wpa_priv_cmd_l2_unregister(iface, &from);
+ wpa_priv_cmd_l2_unregister(iface, &from, fromlen);
break;
case PRIVSEP_CMD_L2_NOTIFY_AUTH_START:
wpa_priv_cmd_l2_notify_auth_start(iface, &from);
break;
case PRIVSEP_CMD_L2_SEND:
- wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len);
+ wpa_priv_cmd_l2_send(iface, &from, fromlen, cmd_buf, cmd_len);
break;
case PRIVSEP_CMD_SET_COUNTRY:
pos = cmd_buf;
@@ -625,8 +708,14 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
{
- if (iface->drv_priv && iface->driver->deinit)
- iface->driver->deinit(iface->drv_priv);
+ int i;
+
+ if (iface->drv_priv) {
+ if (iface->driver->deinit)
+ iface->driver->deinit(iface->drv_priv);
+ if (iface->drv_global_priv)
+ iface->driver->global_deinit(iface->drv_global_priv);
+ }
if (iface->fd >= 0) {
eloop_unregister_read_sock(iface->fd);
@@ -634,8 +723,10 @@ static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
unlink(iface->sock_name);
}
- if (iface->l2)
- l2_packet_deinit(iface->l2);
+ for (i = 0; i < WPA_PRIV_MAX_L2; i++) {
+ if (iface->l2[i])
+ l2_packet_deinit(iface->l2[i]);
+ }
os_free(iface->ifname);
os_free(iface->driver_name);
@@ -777,7 +868,7 @@ static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event,
msg.msg_iov = io;
msg.msg_iovlen = data ? 2 : 1;
msg.msg_name = &iface->drv_addr;
- msg.msg_namelen = sizeof(iface->drv_addr);
+ msg.msg_namelen = iface->drv_addr_len;
if (sendmsg(iface->fd, &msg, 0) < 0) {
wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
@@ -796,7 +887,7 @@ static void wpa_priv_send_auth(struct wpa_priv_interface *iface,
struct privsep_event_auth *auth;
u8 *buf, *pos;
- buf = os_malloc(buflen);
+ buf = os_zalloc(buflen);
if (buf == NULL)
return;
@@ -990,12 +1081,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
&data->pmkid_candidate,
sizeof(struct pmkid_candidate));
break;
- case EVENT_STKSTART:
- if (data == NULL)
- return;
- wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART,
- &data->stkstart.peer, ETH_ALEN);
- break;
case EVENT_FT_RESPONSE:
wpa_priv_send_ft_response(iface, data);
break;
@@ -1061,7 +1146,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
msg.msg_iov = io;
msg.msg_iovlen = 3;
msg.msg_name = &iface->drv_addr;
- msg.msg_namelen = sizeof(iface->drv_addr);
+ msg.msg_namelen = iface->drv_addr_len;
if (sendmsg(iface->fd, &msg, 0) < 0)
wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
@@ -1099,7 +1184,7 @@ static void wpa_priv_fd_workaround(void)
static void usage(void)
{
printf("wpa_priv v" VERSION_STR "\n"
- "Copyright (c) 2007-2016, Jouni Malinen <j@w1.fi> and "
+ "Copyright (c) 2007-2017, Jouni Malinen <j@w1.fi> and "
"contributors\n"
"\n"
"usage:\n"
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 7361ee96d1df..e587d7e3cd69 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant
- * Copyright (c) 2003-2016, 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.
@@ -38,6 +38,7 @@
#include "common/wpa_ctrl.h"
#include "common/ieee802_11_defs.h"
#include "common/hw_features_common.h"
+#include "common/gas_server.h"
#include "p2p/p2p.h"
#include "fst/fst.h"
#include "blacklist.h"
@@ -59,10 +60,15 @@
#include "wnm_sta.h"
#include "wpas_kay.h"
#include "mesh.h"
+#include "dpp_supplicant.h"
+#ifdef CONFIG_MESH
+#include "ap/ap_config.h"
+#include "ap/hostapd.h"
+#endif /* CONFIG_MESH */
const char *const wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors";
const char *const wpa_supplicant_license =
"This software may be distributed under the terms of the BSD license.\n"
@@ -112,6 +118,13 @@ const char *const wpa_supplicant_full_license5 =
"\n";
#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx);
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s);
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
+
+
/* Configure default/group WEP keys for static WEP */
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
@@ -230,10 +243,30 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
"%d usec", sec, usec);
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+ wpa_s->last_auth_timeout_sec = sec;
eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
}
+/*
+ * wpas_auth_timeout_restart - Restart and change timeout for authentication
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec_diff: difference in seconds applied to original timeout value
+ */
+void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff)
+{
+ int new_sec = wpa_s->last_auth_timeout_sec + sec_diff;
+
+ if (eloop_is_timeout_registered(wpa_supplicant_timeout, wpa_s, NULL)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Authentication timeout restart: %d sec", new_sec);
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+ eloop_register_timeout(new_sec, 0, wpa_supplicant_timeout,
+ wpa_s, NULL);
+ }
+}
+
+
/**
* wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout
* @wpa_s: Pointer to wpa_supplicant data
@@ -247,6 +280,9 @@ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
wpa_blacklist_del(wpa_s, wpa_s->bssid);
+ os_free(wpa_s->last_con_fail_realm);
+ wpa_s->last_con_fail_realm = NULL;
+ wpa_s->last_con_fail_realm_len = 0;
}
@@ -329,7 +365,12 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
- ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#ifdef CONFIG_MACSEC
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE && ssid->mka_psk_set)
+ ieee802_1x_create_preshared_mka(wpa_s, ssid);
+ else
+ ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#endif /* CONFIG_MACSEC */
#endif /* IEEE8021X_EAPOL */
}
@@ -409,12 +450,26 @@ static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
+ eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
dl_list_del(&bss->list);
os_free(bss);
}
}
+void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s)
+{
+ struct fils_hlp_req *req;
+
+ while ((req = dl_list_first(&wpa_s->fils_hlp_req, struct fils_hlp_req,
+ list)) != NULL) {
+ dl_list_del(&req->list);
+ wpabuf_free(req->pkt);
+ os_free(req);
+ }
+}
+
+
static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{
int i;
@@ -434,6 +489,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
#ifdef CONFIG_TESTING_OPTIONS
l2_packet_deinit(wpa_s->l2_test);
wpa_s->l2_test = NULL;
+ os_free(wpa_s->get_pref_freq_list_override);
+ wpa_s->get_pref_freq_list_override = NULL;
+ wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
+ wpa_s->last_assoc_req_wpa_ie = NULL;
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->conf != NULL) {
@@ -448,6 +507,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
os_free(wpa_s->confanother);
wpa_s->confanother = NULL;
+ os_free(wpa_s->last_con_fail_realm);
+ wpa_s->last_con_fail_realm = NULL;
+ wpa_s->last_con_fail_realm_len = 0;
+
wpa_sm_set_eapol(wpa_s->wpa, NULL);
eapol_sm_deinit(wpa_s->eapol);
wpa_s->eapol = NULL;
@@ -506,6 +569,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
os_free(wpa_s->manual_scan_freqs);
wpa_s->manual_scan_freqs = NULL;
+ os_free(wpa_s->select_network_scan_freqs);
+ wpa_s->select_network_scan_freqs = NULL;
os_free(wpa_s->manual_sched_scan_freqs);
wpa_s->manual_sched_scan_freqs = NULL;
@@ -524,6 +589,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
radio_remove_works(wpa_s, "gas-query", 0);
gas_query_deinit(wpa_s->gas);
wpa_s->gas = NULL;
+ gas_server_deinit(wpa_s->gas_server);
+ wpa_s->gas_server = NULL;
free_hw_features(wpa_s);
@@ -580,6 +647,32 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
wpabuf_free(wpa_s->lci);
wpa_s->lci = NULL;
+ wpas_clear_beacon_rep_data(wpa_s);
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+ {
+ struct external_pmksa_cache *entry;
+
+ while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache,
+ struct external_pmksa_cache,
+ list)) != NULL) {
+ dl_list_del(&entry->list);
+ os_free(entry->pmksa_cache);
+ os_free(entry);
+ }
+ }
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+ wpas_flush_fils_hlp_req(wpa_s);
+
+ wpabuf_free(wpa_s->ric_ies);
+ wpa_s->ric_ies = NULL;
+
+#ifdef CONFIG_DPP
+ wpas_dpp_deinit(wpa_s);
+#endif /* CONFIG_DPP */
}
@@ -793,12 +886,24 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
if (state == WPA_COMPLETED && wpa_s->new_connection) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
+ int fils_hlp_sent = 0;
+
+#ifdef CONFIG_SME
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ wpa_auth_alg_fils(wpa_s->sme.auth_alg))
+ fils_hlp_sent = 1;
+#endif /* CONFIG_SME */
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ wpa_auth_alg_fils(wpa_s->auth_alg))
+ fils_hlp_sent = 1;
+
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
- MACSTR " completed [id=%d id_str=%s]",
+ MACSTR " completed [id=%d id_str=%s%s]",
MAC2STR(wpa_s->bssid),
ssid ? ssid->id : -1,
- ssid && ssid->id_str ? ssid->id_str : "");
+ ssid && ssid->id_str ? ssid->id_str : "",
+ fils_hlp_sent ? " FILS_HLP_SENT" : "");
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
wpas_clear_temp_disabled(wpa_s, ssid, 1);
wpa_blacklist_clear(wpa_s);
@@ -813,6 +918,11 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
wpas_p2p_completed(wpa_s);
sme_sched_obss_scan(wpa_s, 1);
+
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+ if (!fils_hlp_sent && ssid && ssid->eap.erp)
+ wpas_update_fils_connect_params(wpa_s);
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
state == WPA_ASSOCIATED) {
wpa_s->new_connection = 1;
@@ -927,7 +1037,13 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
"file '%s' - exiting", wpa_s->confname);
return -1;
}
- wpa_config_read(wpa_s->confanother, conf);
+ if (wpa_s->confanother &&
+ !wpa_config_read(wpa_s->confanother, conf)) {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "Failed to parse the configuration file '%s' - exiting",
+ wpa_s->confanother);
+ return -1;
+ }
conf->changed_parameters = (unsigned int) -1;
@@ -953,7 +1069,9 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
* TODO: should notify EAPOL SM about changes in opensc_engine_path,
* pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
*/
- if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
/*
* Clear forced success to clear EAP state for next
* authentication.
@@ -1098,14 +1216,20 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
proto = WPA_PROTO_WPA;
#ifdef CONFIG_HS20
- } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) {
+ } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN) &&
+ wpa_parse_wpa_ie(bss_osen, 2 + bss_osen[1], &ie) == 0 &&
+ (ie.group_cipher & ssid->group_cipher) &&
+ (ie.pairwise_cipher & ssid->pairwise_cipher) &&
+ (ie.key_mgmt & ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
- /* TODO: parse OSEN element */
- os_memset(&ie, 0, sizeof(ie));
- ie.group_cipher = WPA_CIPHER_CCMP;
- ie.pairwise_cipher = WPA_CIPHER_CCMP;
- ie.key_mgmt = WPA_KEY_MGMT_OSEN;
proto = WPA_PROTO_OSEN;
+ } else if (bss_rsn && (ssid->proto & WPA_PROTO_OSEN) &&
+ wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
+ (ie.group_cipher & ssid->group_cipher) &&
+ (ie.pairwise_cipher & ssid->pairwise_cipher) &&
+ (ie.key_mgmt & ssid->key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using OSEN (within RSN)");
+ proto = WPA_PROTO_RSN;
#endif /* CONFIG_HS20 */
} else if (bss) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
@@ -1157,10 +1281,35 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
ie.pairwise_cipher = ssid->pairwise_cipher;
ie.key_mgmt = ssid->key_mgmt;
#ifdef CONFIG_IEEE80211W
- ie.mgmt_group_cipher =
- ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
- WPA_CIPHER_AES_128_CMAC : 0;
+ ie.mgmt_group_cipher = 0;
+ if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ if (ssid->group_mgmt_cipher &
+ WPA_CIPHER_BIP_GMAC_256)
+ ie.mgmt_group_cipher =
+ WPA_CIPHER_BIP_GMAC_256;
+ else if (ssid->group_mgmt_cipher &
+ WPA_CIPHER_BIP_CMAC_256)
+ ie.mgmt_group_cipher =
+ WPA_CIPHER_BIP_CMAC_256;
+ else if (ssid->group_mgmt_cipher &
+ WPA_CIPHER_BIP_GMAC_128)
+ ie.mgmt_group_cipher =
+ WPA_CIPHER_BIP_GMAC_128;
+ else
+ ie.mgmt_group_cipher =
+ WPA_CIPHER_AES_128_CMAC;
+ }
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OWE
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
+ !ssid->owe_only &&
+ !bss_wpa && !bss_rsn && !bss_osen) {
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ wpa_s->wpa_proto = 0;
+ *wpa_ie_len = 0;
+ return 0;
+ }
+#endif /* CONFIG_OWE */
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
"based on configuration");
} else
@@ -1233,10 +1382,46 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT 802.1X with Suite B");
#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R
+ } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384");
+ } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
+#endif /* CONFIG_IEEE80211R */
+ } else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384");
+ } else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256");
+#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SHA384
+ } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using KEY_MGMT FT/802.1X-SHA384");
+ if (pmksa_cache_get_current(wpa_s->wpa)) {
+ /* PMKSA caching with FT is not fully functional, so
+ * disable the case for now. */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: Disable PMKSA caching for FT/802.1X connection");
+ pmksa_cache_clear_current(wpa_s->wpa);
+ }
+#endif /* CONFIG_SHA384 */
} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
+ if (pmksa_cache_get_current(wpa_s->wpa)) {
+ /* PMKSA caching with FT is not fully functional, so
+ * disable the case for now. */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: Disable PMKSA caching for FT/802.1X connection");
+ pmksa_cache_clear_current(wpa_s->wpa);
+ }
} else if (sel & WPA_KEY_MGMT_FT_PSK) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
@@ -1273,6 +1458,16 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_OWE
+ } else if (sel & WPA_KEY_MGMT_OWE) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_OWE;
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE");
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ } else if (sel & WPA_KEY_MGMT_DPP) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_DPP;
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP");
+#endif /* CONFIG_DPP */
} else {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
"authenticated key management type");
@@ -1286,6 +1481,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_IEEE80211W
sel = ie.mgmt_group_cipher;
+ if (ssid->group_mgmt_cipher)
+ sel &= ssid->group_mgmt_cipher;
if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
!(ie.capabilities & WPA_CAPABILITY_MFPC))
sel = 0;
@@ -1322,15 +1519,27 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
int psk_set = 0;
+ int sae_only;
+
+ sae_only = (ssid->key_mgmt & (WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256)) == 0;
- if (ssid->psk_set) {
+ if (ssid->psk_set && !sae_only) {
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)",
+ ssid->psk, PMK_LEN);
wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
NULL);
psk_set = 1;
}
+
+ if (wpa_key_mgmt_sae(ssid->key_mgmt) &&
+ (ssid->sae_password || ssid->passphrase))
+ psk_set = 1;
+
#ifndef CONFIG_NO_PBKDF2
if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
- ssid->passphrase) {
+ ssid->passphrase && !sae_only) {
u8 psk[PMK_LEN];
pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
4096, psk, PMK_LEN);
@@ -1342,7 +1551,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_NO_PBKDF2 */
#ifdef CONFIG_EXT_PASSWORD
- if (ssid->ext_psk) {
+ if (ssid->ext_psk && !sae_only) {
struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
ssid->ext_psk);
char pw_str[64 + 1];
@@ -1388,6 +1597,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
ext_password_free(pw);
return -1;
}
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "PSK (from external PSK)",
+ psk, PMK_LEN);
wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
NULL);
psk_set = 1;
@@ -1408,8 +1620,15 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
if (!psk_set) {
wpa_msg(wpa_s, MSG_INFO,
"No PSK available for association");
+ wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE");
return -1;
}
+#ifdef CONFIG_OWE
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
+ /* OWE Diffie-Hellman exchange in (Re)Association
+ * Request/Response frames set the PMK, so do not override it
+ * here. */
+#endif /* CONFIG_OWE */
} else
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
@@ -1425,6 +1644,10 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
case 0: /* Bits 0-7 */
break;
case 1: /* Bits 8-15 */
+ if (wpa_s->conf->coloc_intf_reporting) {
+ /* Bit 13 - Collocated Interference Reporting */
+ *pos |= 0x20;
+ }
break;
case 2: /* Bits 16-23 */
#ifdef CONFIG_WNM
@@ -1443,7 +1666,7 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
break;
case 4: /* Bits 32-39 */
#ifdef CONFIG_INTERWORKING
- if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING)
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING)
*pos |= 0x01; /* Bit 32 - QoS Map */
#endif /* CONFIG_INTERWORKING */
break;
@@ -1466,6 +1689,12 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
if (wpa_s->conf->ftm_initiator)
*pos |= 0x80; /* Bit 71 - FTM initiator */
break;
+ case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+ if (!wpa_s->disable_fils)
+ *pos |= 0x01;
+#endif /* CONFIG_FILS */
+ break;
}
}
@@ -1473,11 +1702,8 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
{
u8 *pos = buf;
- u8 len = 6, i;
+ u8 len = 10, i;
- if (len < 9 &&
- (wpa_s->conf->ftm_initiator || wpa_s->conf->ftm_responder))
- len = 9;
if (len < wpa_s->extended_capa_len)
len = wpa_s->extended_capa_len;
if (buflen < (size_t) len + 2) {
@@ -1665,6 +1891,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
wmm_ac_clear_saved_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 0;
wpa_s->reassoc_same_ess = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ wpa_s->testing_resend_assoc = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->last_ssid == ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
@@ -1673,11 +1902,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
wmm_ac_save_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 1;
}
- } else if (rand_style > 0) {
+ }
+
+ if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
if (wpas_update_random_addr(wpa_s, rand_style) < 0)
return;
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
- } else if (wpa_s->mac_addr_changed) {
+ } else if (rand_style == 0 && wpa_s->mac_addr_changed) {
if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not restore permanent MAC address");
@@ -1696,6 +1927,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
wpa_s->ibss_rsn = NULL;
+#else /* CONFIG_IBSS_RSN */
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "IBSS RSN not supported in the build");
+ return;
+ }
#endif /* CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
@@ -1737,6 +1975,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
ssid->id);
+ wpas_notify_mesh_group_started(wpa_s, ssid);
#else /* CONFIG_MESH */
wpa_msg(wpa_s, MSG_ERROR,
"mesh mode support not included in the build");
@@ -1744,6 +1983,20 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
return;
}
+ /*
+ * Set WPA state machine configuration to match the selected network now
+ * so that the information is available before wpas_start_assoc_cb()
+ * gets called. This is needed at least for RSN pre-authentication where
+ * candidate APs are added to a list based on scan result processing
+ * before completion of the first association.
+ */
+ wpa_supplicant_rsn_supp_set_config(wpa_s, ssid);
+
+#ifdef CONFIG_DPP
+ if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0)
+ return;
+#endif /* CONFIG_DPP */
+
#ifdef CONFIG_TDLS
if (bss)
wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
@@ -1766,6 +2019,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
return;
}
+#ifdef CONFIG_SME
+ if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) {
+ /* Clear possibly set auth_alg, if any, from last attempt. */
+ wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN;
+ }
+#endif /* CONFIG_SME */
+
wpas_abort_ongoing_scan(wpa_s);
cwork = os_zalloc(sizeof(*cwork));
@@ -1797,11 +2057,6 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s,
u8 channel;
int i;
-#ifdef CONFIG_HT_OVERRIDES
- if (ssid->disable_ht)
- return 0;
-#endif /* CONFIG_HT_OVERRIDES */
-
hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
if (hw_mode == NUM_HOSTAPD_MODES)
return 0;
@@ -2000,6 +2255,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
vht_freq = *freq;
+#ifdef CONFIG_VHT_OVERRIDES
+ if (ssid->disable_vht) {
+ freq->vht_enabled = 0;
+ return;
+ }
+#endif /* CONFIG_VHT_OVERRIDES */
+
vht_freq.vht_enabled = vht_supported(mode);
if (!vht_freq.vht_enabled)
return;
@@ -2084,147 +2346,170 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
}
-static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+#ifdef CONFIG_FILS
+static size_t wpas_add_fils_hlp_req(struct wpa_supplicant *wpa_s, u8 *ie_buf,
+ size_t ie_buf_len)
{
- struct wpa_connect_work *cwork = work->ctx;
- struct wpa_bss *bss = cwork->bss;
- struct wpa_ssid *ssid = cwork->ssid;
- struct wpa_supplicant *wpa_s = work->wpa_s;
- u8 wpa_ie[200];
- size_t wpa_ie_len;
- int use_crypt, ret, i, bssid_changed;
- int algs = WPA_AUTH_ALG_OPEN;
- unsigned int cipher_pairwise, cipher_group;
- struct wpa_driver_associate_params params;
- int wep_keys_set = 0;
- int assoc_failed = 0;
- struct wpa_ssid *old_ssid;
- u8 prev_bssid[ETH_ALEN];
-#ifdef CONFIG_HT_OVERRIDES
- struct ieee80211_ht_capabilities htcaps;
- struct ieee80211_ht_capabilities htcaps_mask;
-#endif /* CONFIG_HT_OVERRIDES */
-#ifdef CONFIG_VHT_OVERRIDES
- struct ieee80211_vht_capabilities vhtcaps;
- struct ieee80211_vht_capabilities vhtcaps_mask;
-#endif /* CONFIG_VHT_OVERRIDES */
-#ifdef CONFIG_MBO
- const u8 *mbo = NULL;
-#endif /* CONFIG_MBO */
+ struct fils_hlp_req *req;
+ size_t rem_len, hdr_len, hlp_len, len, ie_len = 0;
+ const u8 *pos;
+ u8 *buf = ie_buf;
- if (deinit) {
- if (work->started) {
- wpa_s->connect_work = NULL;
+ dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+ list) {
+ rem_len = ie_buf_len - ie_len;
+ pos = wpabuf_head(req->pkt);
+ hdr_len = 1 + 2 * ETH_ALEN + 6;
+ hlp_len = wpabuf_len(req->pkt);
- /* cancel possible auth. timeout */
- eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
- NULL);
+ if (rem_len < 2 + hdr_len + hlp_len) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Cannot fit HLP - rem_len=%lu to_fill=%lu",
+ (unsigned long) rem_len,
+ (unsigned long) (2 + hdr_len + hlp_len));
+ break;
+ }
+
+ len = (hdr_len + hlp_len) > 255 ? 255 : hdr_len + hlp_len;
+ /* Element ID */
+ *buf++ = WLAN_EID_EXTENSION;
+ /* Length */
+ *buf++ = len;
+ /* Element ID Extension */
+ *buf++ = WLAN_EID_EXT_FILS_HLP_CONTAINER;
+ /* Destination MAC address */
+ os_memcpy(buf, req->dst, ETH_ALEN);
+ buf += ETH_ALEN;
+ /* Source MAC address */
+ os_memcpy(buf, wpa_s->own_addr, ETH_ALEN);
+ buf += ETH_ALEN;
+ /* LLC/SNAP Header */
+ os_memcpy(buf, "\xaa\xaa\x03\x00\x00\x00", 6);
+ buf += 6;
+ /* HLP Packet */
+ os_memcpy(buf, pos, len - hdr_len);
+ buf += len - hdr_len;
+ pos += len - hdr_len;
+
+ hlp_len -= len - hdr_len;
+ ie_len += 2 + len;
+ rem_len -= 2 + len;
+
+ while (hlp_len) {
+ len = (hlp_len > 255) ? 255 : hlp_len;
+ if (rem_len < 2 + len)
+ break;
+ *buf++ = WLAN_EID_FRAGMENT;
+ *buf++ = len;
+ os_memcpy(buf, pos, len);
+ buf += len;
+ pos += len;
+
+ hlp_len -= len;
+ ie_len += 2 + len;
+ rem_len -= 2 + len;
}
- wpas_connect_work_free(cwork);
- return;
}
- wpa_s->connect_work = work;
+ return ie_len;
+}
- if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
- wpas_network_disabled(wpa_s, ssid)) {
- wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
- wpas_connect_work_done(wpa_s);
- return;
- }
- os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
- os_memset(&params, 0, sizeof(params));
- wpa_s->reassociate = 0;
- wpa_s->eap_expected_failure = 0;
- if (bss &&
- (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
-#ifdef CONFIG_IEEE80211R
- const u8 *ie, *md = NULL;
-#endif /* CONFIG_IEEE80211R */
- wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
- " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
- wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
- bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
- os_memset(wpa_s->bssid, 0, ETH_ALEN);
- os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
- if (bssid_changed)
- wpas_notify_bssid_changed(wpa_s);
-#ifdef CONFIG_IEEE80211R
- ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
- if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
- md = ie + 2;
- wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
- if (md) {
- /* Prepare for the next transition */
- wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
- }
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_WPS
- } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
- wpa_s->conf->ap_scan == 2 &&
- (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
- /* Use ap_scan==1 style network selection to find the network
- */
- wpas_connect_work_done(wpa_s);
- wpa_s->scan_req = MANUAL_SCAN_REQ;
- wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
- return;
-#endif /* CONFIG_WPS */
- } else {
- wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
- wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
- if (bss)
- os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
- else
- os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
- }
- if (!wpa_s->pno)
- wpa_supplicant_cancel_sched_scan(wpa_s);
+int wpa_is_fils_supported(struct wpa_supplicant *wpa_s)
+{
+ return (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) ||
+ (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)));
+}
- wpa_supplicant_cancel_scan(wpa_s);
- /* Starting new association, so clear the possibly used WPA IE from the
- * previous association. */
- wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_FILS_SK_PFS
+ return (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS);
+#else /* CONFIG_FILS_SK_PFS */
+ return 0;
+#endif /* CONFIG_FILS_SK_PFS */
+}
-#ifdef IEEE8021X_EAPOL
- if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
- if (ssid->leap) {
- if (ssid->non_leap == 0)
- algs = WPA_AUTH_ALG_LEAP;
- else
- algs |= WPA_AUTH_ALG_LEAP;
- }
+#endif /* CONFIG_FILS */
+
+
+static u8 * wpas_populate_assoc_ies(
+ struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params,
+ enum wpa_drv_update_connect_params_mask *mask)
+{
+ u8 *wpa_ie;
+ size_t max_wpa_ie_len = 500;
+ size_t wpa_ie_len;
+ int algs = WPA_AUTH_ALG_OPEN;
+#ifdef CONFIG_MBO
+ const u8 *mbo_ie;
+#endif
+#ifdef CONFIG_FILS
+ const u8 *realm, *username, *rrk;
+ size_t realm_len, username_len, rrk_len;
+ u16 next_seq_num;
+ struct fils_hlp_req *req;
+
+ dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+ list) {
+ max_wpa_ie_len += 3 + 2 * ETH_ALEN + 6 + wpabuf_len(req->pkt) +
+ 2 + 2 * wpabuf_len(req->pkt) / 255;
}
-#endif /* IEEE8021X_EAPOL */
- wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
- if (ssid->auth_alg) {
- algs = ssid->auth_alg;
- wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
- "0x%x", algs);
+#endif /* CONFIG_FILS */
+
+ wpa_ie = os_malloc(max_wpa_ie_len);
+ if (!wpa_ie) {
+ wpa_printf(MSG_ERROR,
+ "Failed to allocate connect IE buffer for %lu bytes",
+ (unsigned long) max_wpa_ie_len);
+ return NULL;
}
if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
wpa_key_mgmt_wpa(ssid->key_mgmt)) {
int try_opportunistic;
+ const u8 *cache_id = NULL;
+
try_opportunistic = (ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc :
ssid->proactive_key_caching) &&
(ssid->proto & WPA_PROTO_RSN);
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(ssid->key_mgmt))
+ cache_id = wpa_bss_get_fils_cache_id(bss);
+#endif /* CONFIG_FILS */
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
- ssid, try_opportunistic) == 0)
+ ssid, try_opportunistic,
+ cache_id, 0) == 0)
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
- wpa_ie_len = sizeof(wpa_ie);
+ wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_ie, &wpa_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites");
- wpas_connect_work_done(wpa_s);
- return;
+ os_free(wpa_ie);
+ return NULL;
}
+#ifdef CONFIG_HS20
+ } else if (bss && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) &&
+ (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) {
+ /* No PMKSA caching, but otherwise similar to RSN/WPA */
+ wpa_ie_len = max_wpa_ie_len;
+ if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+ wpa_ie, &wpa_ie_len)) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+ "key management and encryption suites");
+ os_free(wpa_ie);
+ return NULL;
+ }
+#endif /* CONFIG_HS20 */
} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
/*
@@ -2236,20 +2521,20 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
wpa_ie_len = 0;
wpa_s->wpa_proto = 0;
} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
- wpa_ie_len = sizeof(wpa_ie);
+ wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_ie, &wpa_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites (no "
"scan results)");
- wpas_connect_work_done(wpa_s);
- return;
+ os_free(wpa_ie);
+ return NULL;
}
#ifdef CONFIG_WPS
} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
struct wpabuf *wps_ie;
wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
- if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) {
+ if (wps_ie && wpabuf_len(wps_ie) <= max_wpa_ie_len) {
wpa_ie_len = wpabuf_len(wps_ie);
os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
} else
@@ -2257,9 +2542,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
wpabuf_free(wps_ie);
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
- params.wps = WPS_MODE_PRIVACY;
+ params->wps = WPS_MODE_PRIVACY;
else
- params.wps = WPS_MODE_OPEN;
+ params->wps = WPS_MODE_OPEN;
wpa_s->wpa_proto = 0;
#endif /* CONFIG_WPS */
} else {
@@ -2268,13 +2553,61 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
wpa_s->wpa_proto = 0;
}
+#ifdef IEEE8021X_EAPOL
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ if (ssid->leap) {
+ if (ssid->non_leap == 0)
+ algs = WPA_AUTH_ALG_LEAP;
+ else
+ algs |= WPA_AUTH_ALG_LEAP;
+ }
+ }
+
+#ifdef CONFIG_FILS
+ /* Clear FILS association */
+ wpa_sm_set_reset_fils_completed(wpa_s->wpa, 0);
+
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) &&
+ ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) &&
+ eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username,
+ &username_len, &realm, &realm_len,
+ &next_seq_num, &rrk, &rrk_len) == 0 &&
+ (!wpa_s->last_con_fail_realm ||
+ wpa_s->last_con_fail_realm_len != realm_len ||
+ os_memcmp(wpa_s->last_con_fail_realm, realm, realm_len) != 0)) {
+ algs = WPA_AUTH_ALG_FILS;
+ params->fils_erp_username = username;
+ params->fils_erp_username_len = username_len;
+ params->fils_erp_realm = realm;
+ params->fils_erp_realm_len = realm_len;
+ params->fils_erp_next_seq_num = next_seq_num;
+ params->fils_erp_rrk = rrk;
+ params->fils_erp_rrk_len = rrk_len;
+
+ if (mask)
+ *mask |= WPA_DRV_UPDATE_FILS_ERP_INFO;
+ }
+#endif /* CONFIG_FILS */
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_SAE
+ if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))
+ algs = WPA_AUTH_ALG_SAE;
+#endif /* CONFIG_SAE */
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+ if (ssid->auth_alg) {
+ algs = ssid->auth_alg;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Overriding auth_alg selection: 0x%x", algs);
+ }
+
#ifdef CONFIG_P2P
if (wpa_s->global->p2p) {
u8 *pos;
size_t len;
int res;
pos = wpa_ie + wpa_ie_len;
- len = sizeof(wpa_ie) - wpa_ie_len;
+ len = max_wpa_ie_len - wpa_ie_len;
res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
ssid->p2p_group);
if (res >= 0)
@@ -2299,21 +2632,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
#endif /* CONFIG_P2P */
-#ifdef CONFIG_MBO
if (bss) {
- mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
- if (mbo) {
- int len;
-
- len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq,
- wpa_ie + wpa_ie_len,
- sizeof(wpa_ie) -
- wpa_ie_len);
- if (len > 0)
- wpa_ie_len += len;
- }
+ wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq,
+ wpa_ie + wpa_ie_len,
+ max_wpa_ie_len -
+ wpa_ie_len);
}
-#endif /* CONFIG_MBO */
/*
* Workaround: Add Extended Capabilities element only if the AP
@@ -2333,7 +2657,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
int ext_capab_len;
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
sizeof(ext_capab));
- if (ext_capab_len > 0) {
+ if (ext_capab_len > 0 &&
+ wpa_ie_len + ext_capab_len <= max_wpa_ie_len) {
u8 *pos = wpa_ie;
if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
pos += 2 + pos[1];
@@ -2348,13 +2673,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
if (is_hs20_network(wpa_s, ssid, bss)) {
struct wpabuf *hs20;
- hs20 = wpabuf_alloc(20);
+ hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN);
if (hs20) {
int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
size_t len;
wpas_hs20_add_indication(hs20, pps_mo_id);
- len = sizeof(wpa_ie) - wpa_ie_len;
+ wpas_hs20_add_roam_cons_sel(hs20, ssid);
+ len = max_wpa_ie_len - wpa_ie_len;
if (wpabuf_len(hs20) <= len) {
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(hs20), wpabuf_len(hs20));
@@ -2371,7 +2697,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
size_t len;
- len = sizeof(wpa_ie) - wpa_ie_len;
+ len = max_wpa_ie_len - wpa_ie_len;
if (wpabuf_len(buf) <= len) {
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(buf), wpabuf_len(buf));
@@ -2383,7 +2709,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
if (wpa_s->fst_ies) {
int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
- if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
+ if (wpa_ie_len + fst_ies_len <= max_wpa_ie_len) {
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(wpa_s->fst_ies), fst_ies_len);
wpa_ie_len += fst_ies_len;
@@ -2392,20 +2718,249 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
#endif /* CONFIG_FST */
#ifdef CONFIG_MBO
- if (mbo) {
+ mbo_ie = bss ? wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE) : NULL;
+ if (mbo_ie) {
int len;
len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
- sizeof(wpa_ie) - wpa_ie_len);
+ max_wpa_ie_len - wpa_ie_len,
+ !!mbo_attr_from_mbo_ie(mbo_ie,
+ OCE_ATTR_ID_CAPA_IND));
if (len >= 0)
wpa_ie_len += len;
}
#endif /* CONFIG_MBO */
+#ifdef CONFIG_FILS
+ if (algs == WPA_AUTH_ALG_FILS) {
+ size_t len;
+
+ len = wpas_add_fils_hlp_req(wpa_s, wpa_ie + wpa_ie_len,
+ max_wpa_ie_len - wpa_ie_len);
+ wpa_ie_len += len;
+ }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+#ifdef CONFIG_TESTING_OPTIONS
+ if (get_ie_ext(wpa_ie, wpa_ie_len, WLAN_EID_EXT_OWE_DH_PARAM)) {
+ wpa_printf(MSG_INFO, "TESTING: Override OWE DH element");
+ } else
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (algs == WPA_AUTH_ALG_OPEN &&
+ ssid->key_mgmt == WPA_KEY_MGMT_OWE) {
+ struct wpabuf *owe_ie;
+ u16 group;
+
+ if (ssid->owe_group) {
+ group = ssid->owe_group;
+ } else if (wpa_s->assoc_status_code ==
+ WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
+ if (wpa_s->last_owe_group == 19)
+ group = 20;
+ else if (wpa_s->last_owe_group == 20)
+ group = 21;
+ else
+ group = OWE_DH_GROUP;
+ } else {
+ group = OWE_DH_GROUP;
+ }
+
+ wpa_s->last_owe_group = group;
+ wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group);
+ owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
+ if (owe_ie &&
+ wpabuf_len(owe_ie) <= max_wpa_ie_len - wpa_ie_len) {
+ os_memcpy(wpa_ie + wpa_ie_len,
+ wpabuf_head(owe_ie), wpabuf_len(owe_ie));
+ wpa_ie_len += wpabuf_len(owe_ie);
+ wpabuf_free(owe_ie);
+ }
+ }
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_IEEE80211R
+ /*
+ * Add MDIE under these conditions: the network profile allows FT,
+ * the AP supports FT, and the mobility domain ID matches.
+ */
+ if (bss && wpa_key_mgmt_ft(wpa_sm_get_key_mgmt(wpa_s->wpa))) {
+ const u8 *mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+
+ if (mdie && mdie[1] >= MOBILITY_DOMAIN_ID_LEN) {
+ size_t len = 0;
+ const u8 *md = mdie + 2;
+ const u8 *wpa_md = wpa_sm_get_ft_md(wpa_s->wpa);
+
+ if (os_memcmp(md, wpa_md,
+ MOBILITY_DOMAIN_ID_LEN) == 0) {
+ /* Add mobility domain IE */
+ len = wpa_ft_add_mdie(
+ wpa_s->wpa, wpa_ie + wpa_ie_len,
+ max_wpa_ie_len - wpa_ie_len, mdie);
+ wpa_ie_len += len;
+ }
+#ifdef CONFIG_SME
+ if (len > 0 && wpa_s->sme.ft_used &&
+ wpa_sm_has_ptk(wpa_s->wpa)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SME: Trying to use FT over-the-air");
+ algs |= WPA_AUTH_ALG_FT;
+ }
+#endif /* CONFIG_SME */
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ params->wpa_ie = wpa_ie;
+ params->wpa_ie_len = wpa_ie_len;
+ params->auth_alg = algs;
+ if (mask)
+ *mask |= WPA_DRV_UPDATE_ASSOC_IES | WPA_DRV_UPDATE_AUTH_TYPE;
+
+ return wpa_ie;
+}
+
+
+#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
+static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_driver_associate_params params;
+ enum wpa_drv_update_connect_params_mask mask = 0;
+ u8 *wpa_ie;
+
+ if (wpa_s->auth_alg != WPA_AUTH_ALG_OPEN)
+ return; /* nothing to do */
+
+ os_memset(&params, 0, sizeof(params));
+ wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss,
+ wpa_s->current_ssid, &params, &mask);
+ if (!wpa_ie)
+ return;
+
+ if (params.auth_alg != WPA_AUTH_ALG_FILS) {
+ os_free(wpa_ie);
+ return;
+ }
+
+ wpa_s->auth_alg = params.auth_alg;
+ wpa_drv_update_connect_params(wpa_s, &params, mask);
+ os_free(wpa_ie);
+}
+#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_connect_work *cwork = work->ctx;
+ struct wpa_bss *bss = cwork->bss;
+ struct wpa_ssid *ssid = cwork->ssid;
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ u8 *wpa_ie;
+ int use_crypt, ret, i, bssid_changed;
+ unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
+ struct wpa_driver_associate_params params;
+ int wep_keys_set = 0;
+ int assoc_failed = 0;
+ struct wpa_ssid *old_ssid;
+ u8 prev_bssid[ETH_ALEN];
+#ifdef CONFIG_HT_OVERRIDES
+ struct ieee80211_ht_capabilities htcaps;
+ struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ struct ieee80211_vht_capabilities vhtcaps;
+ struct ieee80211_vht_capabilities vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+ if (deinit) {
+ if (work->started) {
+ wpa_s->connect_work = NULL;
+
+ /* cancel possible auth. timeout */
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
+ NULL);
+ }
+ wpas_connect_work_free(cwork);
+ return;
+ }
+
+ wpa_s->connect_work = work;
+
+ if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
+ wpas_network_disabled(wpa_s, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+
+ os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
+ os_memset(&params, 0, sizeof(params));
+ wpa_s->reassociate = 0;
+ wpa_s->eap_expected_failure = 0;
+ if (bss &&
+ (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
+#ifdef CONFIG_IEEE80211R
+ const u8 *ie, *md = NULL;
+#endif /* CONFIG_IEEE80211R */
+ wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+ " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
+ bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+ os_memset(wpa_s->bssid, 0, ETH_ALEN);
+ os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+ if (bssid_changed)
+ wpas_notify_bssid_changed(wpa_s);
+#ifdef CONFIG_IEEE80211R
+ ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+ if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
+ md = ie + 2;
+ wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+ if (md) {
+ /* Prepare for the next transition */
+ wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+ }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+ } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
+ wpa_s->conf->ap_scan == 2 &&
+ (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ /* Use ap_scan==1 style network selection to find the network
+ */
+ wpas_connect_work_done(wpa_s);
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ return;
+#endif /* CONFIG_WPS */
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ if (bss)
+ os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+ else
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ }
+ if (!wpa_s->pno)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ wpa_supplicant_cancel_scan(wpa_s);
+
+ /* Starting new association, so clear the possibly used WPA IE from the
+ * previous association. */
+ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+ wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, &params, NULL);
+ if (!wpa_ie) {
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
cipher_pairwise = wpa_s->pairwise_cipher;
cipher_group = wpa_s->group_cipher;
+ cipher_group_mgmt = wpa_s->mgmt_group_cipher;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
@@ -2443,12 +2998,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
if (bss) {
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
- if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+ if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
MACSTR " freq=%u MHz based on scan results "
- "(bssid_set=%d)",
+ "(bssid_set=%d wps=%d)",
MAC2STR(bss->bssid), bss->freq,
- ssid->bssid_set);
+ ssid->bssid_set,
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
params.bssid = bss->bssid;
params.freq.freq = bss->freq;
}
@@ -2456,6 +3013,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
params.freq_hint = bss->freq;
params.pbss = bss_is_pbss(bss);
} else {
+ if (ssid->bssid_hint_set)
+ params.bssid_hint = ssid->bssid_hint;
+
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0;
@@ -2480,13 +3040,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
params.beacon_int = wpa_s->conf->beacon_int;
}
- params.wpa_ie = wpa_ie;
- params.wpa_ie_len = wpa_ie_len;
params.pairwise_suite = cipher_pairwise;
params.group_suite = cipher_group;
+ params.mgmt_group_suite = cipher_group_mgmt;
params.key_mgmt_suite = wpa_s->key_mgmt;
params.wpa_proto = wpa_s->wpa_proto;
- params.auth_alg = algs;
+ wpa_s->auth_alg = params.auth_alg;
params.mode = ssid->mode;
params.bg_scan_period = ssid->bg_scan_period;
for (i = 0; i < NUM_WEP_KEYS; i++) {
@@ -2536,6 +3095,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
"MFP: require MFP");
params.mgmt_frame_protection =
MGMT_FRAME_PROTECTION_REQUIRED;
+#ifdef CONFIG_OWE
+ } else if (!rsn && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
+ !ssid->owe_only) {
+ params.mgmt_frame_protection = NO_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_OWE */
}
}
#endif /* CONFIG_IEEE80211W */
@@ -2578,6 +3142,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
if (wpas_p2p_handle_frequency_conflicts(
wpa_s, params.freq.freq, ssid) < 0) {
wpas_connect_work_done(wpa_s);
+ os_free(wpa_ie);
return;
}
}
@@ -2589,6 +3154,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
params.prev_bssid = prev_bssid;
ret = wpa_drv_associate(wpa_s, &params);
+ os_free(wpa_ie);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
"failed");
@@ -2730,8 +3296,13 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_MESH
if (wpa_s->ifmsh) {
+ struct mesh_conf *mconf;
+
+ mconf = wpa_s->ifmsh->mconf;
wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
wpa_s->ifname);
+ wpas_notify_mesh_group_removed(wpa_s, mconf->meshid,
+ mconf->meshid_len, reason_code);
wpa_supplicant_leave_mesh(wpa_s);
}
#endif /* CONFIG_MESH */
@@ -2756,6 +3327,7 @@ static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
return;
ssid->disabled = 0;
+ ssid->owe_transition_bss_select_count = 0;
wpas_clear_temp_disabled(wpa_s, ssid, 1);
wpas_notify_network_enabled_changed(wpa_s, ssid);
@@ -2921,13 +3493,19 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
wpas_notify_network_enabled_changed(
wpa_s, other_ssid);
}
- if (wpa_s->current_ssid)
+ if (wpa_s->current_ssid) {
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ }
} else if (ssid->disabled != 2) {
- if (ssid == wpa_s->current_ssid)
+ if (ssid == wpa_s->current_ssid) {
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ }
was_disabled = ssid->disabled;
@@ -3013,6 +3591,9 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
+ wpa_s->last_owe_group = 0;
+ if (ssid)
+ ssid->owe_transition_bss_select_count = 0;
if (wpa_s->connect_without_scan ||
wpa_supplicant_fast_associate(wpa_s) != 1) {
@@ -3230,6 +3811,41 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
}
+#ifdef CONFIG_OWE
+static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *entry_ssid, size_t entry_ssid_len)
+{
+ const u8 *owe, *pos, *end;
+ u8 ssid_len;
+ struct wpa_bss *bss;
+
+ /* Check network profile SSID aganst the SSID in the
+ * OWE Transition Mode element. */
+
+ bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+ if (!bss)
+ return 0;
+
+ owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+ if (!owe)
+ return 0;
+
+ pos = owe + 6;
+ end = owe + 2 + owe[1];
+
+ if (end - pos < ETH_ALEN + 1)
+ return 0;
+ pos += ETH_ALEN;
+ ssid_len = *pos++;
+ if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
+ return 0;
+
+ return entry_ssid_len == ssid_len &&
+ os_memcmp(pos, entry_ssid, ssid_len) == 0;
+}
+#endif /* CONFIG_OWE */
+
+
/**
* wpa_supplicant_get_ssid - Get a pointer to the current network structure
* @wpa_s: Pointer to wpa_supplicant data
@@ -3278,6 +3894,15 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
return entry;
#endif /* CONFIG_WPS */
+#ifdef CONFIG_OWE
+ if (!wpas_network_disabled(wpa_s, entry) &&
+ owe_trans_ssid_match(wpa_s, bssid, entry->ssid,
+ entry->ssid_len) &&
+ (!entry->bssid_set ||
+ os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+ return entry;
+#endif /* CONFIG_OWE */
+
if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
entry->ssid_len == 0 &&
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
@@ -3385,16 +4010,6 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
}
#endif /* CONFIG_TESTING_OPTIONS */
-#ifdef CONFIG_PEERKEY
- if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
- wpa_s->current_ssid->peerkey &&
- !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
- wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) {
- wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key");
- return;
- }
-#endif /* CONFIG_PEERKEY */
-
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
(wpa_s->last_eapol_matches_bssid &&
#ifdef CONFIG_AP
@@ -3505,6 +4120,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_OWE &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_DPP &&
eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
return;
wpa_drv_poll(wpa_s);
@@ -3534,6 +4151,11 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
wpa_supplicant_rx_eapol, wpa_s, 0);
if (wpa_s->l2 == NULL)
return -1;
+
+ if (l2_packet_set_packet_filter(wpa_s->l2,
+ L2_PACKET_FILTER_PKTTYPE))
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Failed to attach pkt_type filter");
} else {
const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
if (addr)
@@ -3673,6 +4295,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
wpa_s->sched_scanning = 0;
dl_list_init(&wpa_s->bss_tmp_disallowed);
+ dl_list_init(&wpa_s->fils_hlp_req);
return wpa_s;
}
@@ -3700,8 +4323,11 @@ static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ long v;
+
errno = 0;
- long v = strtol(tmp, &end, 16);
+ v = strtol(tmp, &end, 16);
+
if (errno == 0) {
wpa_msg(wpa_s, MSG_DEBUG,
"htcap value[%i]: %ld end: %p tmp: %p",
@@ -3811,18 +4437,10 @@ static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
- /* Masking these out disables HT40 */
- le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
- HT_CAP_INFO_SHORT_GI40MHZ);
-
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
- if (disabled)
- htcaps->ht_capabilities_info &= ~msk;
- else
- htcaps->ht_capabilities_info |= msk;
-
- htcaps_mask->ht_capabilities_info |= msk;
+ set_disable_ht40(htcaps, disabled);
+ set_disable_ht40(htcaps_mask, 0);
return 0;
}
@@ -4098,10 +4716,14 @@ static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
{
struct wpa_supplicant *wpa_s = ctx;
- WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0);
+ if (os_memcmp(wpa_s->bssid, da, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "FST:%s:bssid=" MACSTR " != da=" MACSTR,
+ __func__, MAC2STR(wpa_s->bssid), MAC2STR(da));
+ return -1;
+ }
return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
- wpa_s->own_addr, wpa_s->bssid,
- wpabuf_head(data), wpabuf_len(data),
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(data), wpabuf_len(data),
0);
}
@@ -4289,7 +4911,7 @@ static void radio_work_free(struct wpa_radio_work *work)
if (work->started) {
work->wpa_s->radio->num_active_works--;
wpa_dbg(work->wpa_s, MSG_DEBUG,
- "radio_work_free('%s'@%p: num_active_works --> %u",
+ "radio_work_free('%s'@%p): num_active_works --> %u",
work->type, work,
work->wpa_s->radio->num_active_works);
}
@@ -4299,6 +4921,20 @@ static void radio_work_free(struct wpa_radio_work *work)
}
+static int radio_work_is_connect(struct wpa_radio_work *work)
+{
+ return os_strcmp(work->type, "sme-connect") == 0 ||
+ os_strcmp(work->type, "connect") == 0;
+}
+
+
+static int radio_work_is_scan(struct wpa_radio_work *work)
+{
+ return os_strcmp(work->type, "scan") == 0 ||
+ os_strcmp(work->type, "p2p-scan") == 0;
+}
+
+
static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
{
struct wpa_radio_work *active_work = NULL;
@@ -4328,8 +4964,7 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
return NULL;
}
- if (os_strcmp(active_work->type, "sme-connect") == 0 ||
- os_strcmp(active_work->type, "connect") == 0) {
+ if (radio_work_is_connect(active_work)) {
/*
* If the active work is either connect or sme-connect,
* do not parallelize them with other radio works.
@@ -4348,10 +4983,20 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
* If connect or sme-connect are enqueued, parallelize only
* those operations ahead of them in the queue.
*/
- if (os_strcmp(tmp->type, "connect") == 0 ||
- os_strcmp(tmp->type, "sme-connect") == 0)
+ if (radio_work_is_connect(tmp))
break;
+ /* Serialize parallel scan and p2p_scan operations on the same
+ * interface since the driver_nl80211 mechanism for tracking
+ * scan cookies does not yet have support for this. */
+ if (active_work->wpa_s == tmp->wpa_s &&
+ radio_work_is_scan(active_work) &&
+ radio_work_is_scan(tmp)) {
+ wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+ "Do not start work '%s' when another work '%s' is already scheduled",
+ tmp->type, active_work->type);
+ continue;
+ }
/*
* Check that the radio works are distinct and
* on different bands.
@@ -4473,6 +5118,22 @@ void radio_remove_works(struct wpa_supplicant *wpa_s,
}
+void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx)
+{
+ struct wpa_radio_work *work;
+ struct wpa_radio *radio = wpa_s->radio;
+
+ dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+ if (work->ctx != ctx)
+ continue;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Free pending radio work '%s'@%p%s",
+ work->type, work, work->started ? " (started)" : "");
+ radio_work_free(work);
+ break;
+ }
+}
+
+
static void radio_remove_interface(struct wpa_supplicant *wpa_s)
{
struct wpa_radio *radio = wpa_s->radio;
@@ -4625,7 +5286,7 @@ radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
static int wpas_init_driver(struct wpa_supplicant *wpa_s,
- struct wpa_interface *iface)
+ const struct wpa_interface *iface)
{
const char *ifname, *driver, *rn;
@@ -4673,11 +5334,47 @@ next_driver:
}
+#ifdef CONFIG_GAS_SERVER
+
+static void wpas_gas_server_tx_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result result)
+{
+ wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
+ " result=%s",
+ freq, MAC2STR(dst),
+ result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+ (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+ "FAILED"));
+ gas_server_tx_status(wpa_s->gas_server, dst, data, data_len,
+ result == OFFCHANNEL_SEND_ACTION_SUCCESS);
+}
+
+
+static void wpas_gas_server_tx(void *ctx, int freq, const u8 *da,
+ struct wpabuf *buf, unsigned int wait_time)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (wait_time > wpa_s->max_remain_on_chan)
+ wait_time = wpa_s->max_remain_on_chan;
+
+ offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, broadcast,
+ wpabuf_head(buf), wpabuf_len(buf),
+ wait_time, wpas_gas_server_tx_status, 0);
+}
+
+#endif /* CONFIG_GAS_SERVER */
+
static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
- struct wpa_interface *iface)
+ const struct wpa_interface *iface)
{
struct wpa_driver_capa capa;
int capa_res;
+ u8 dfs_domain;
wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
"'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
@@ -4707,7 +5404,13 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
return -1;
}
wpa_s->confanother = os_rel2abs_path(iface->confanother);
- wpa_config_read(wpa_s->confanother, wpa_s->conf);
+ if (wpa_s->confanother &&
+ !wpa_config_read(wpa_s->confanother, wpa_s->conf)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to read or parse configuration '%s'.",
+ wpa_s->confanother);
+ return -1;
+ }
/*
* Override ctrl_interface and driver_param if set on command
@@ -4805,7 +5508,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
&wpa_s->hw.num_modes,
- &wpa_s->hw.flags);
+ &wpa_s->hw.flags,
+ &dfs_domain);
if (wpa_s->hw.modes) {
u16 i;
@@ -4867,8 +5571,6 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
*/
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
wpa_s->p2p_mgmt = iface->p2p_mgmt;
- else
- iface->p2p_mgmt = 1;
if (wpa_s->num_multichan_concurrent == 0)
wpa_s->num_multichan_concurrent = 1;
@@ -4877,10 +5579,7 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
return -1;
#ifdef CONFIG_TDLS
- if ((!iface->p2p_mgmt ||
- !(wpa_s->drv_flags &
- WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
- wpa_tdls_init(wpa_s->wpa))
+ if (!iface->p2p_mgmt && wpa_tdls_init(wpa_s->wpa))
return -1;
#endif /* CONFIG_TDLS */
@@ -4915,6 +5614,19 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
if (wpas_wps_init(wpa_s))
return -1;
+#ifdef CONFIG_GAS_SERVER
+ wpa_s->gas_server = gas_server_init(wpa_s, wpas_gas_server_tx);
+ if (!wpa_s->gas_server) {
+ wpa_printf(MSG_ERROR, "Failed to initialize GAS server");
+ return -1;
+ }
+#endif /* CONFIG_GAS_SERVER */
+
+#ifdef CONFIG_DPP
+ if (wpas_dpp_init(wpa_s) < 0)
+ return -1;
+#endif /* CONFIG_DPP */
+
if (wpa_supplicant_init_eapol(wpa_s) < 0)
return -1;
wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
@@ -4939,7 +5651,9 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
return -1;
}
- if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+ if ((!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) ||
+ wpa_s->p2p_mgmt) &&
+ wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
return -1;
}
@@ -4947,6 +5661,12 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
if (wpa_bss_init(wpa_s) < 0)
return -1;
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+ dl_list_init(&wpa_s->mesh_external_pmksa_cache);
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
/*
* Set Wake-on-WLAN triggers, if configured.
* Note: We don't restore/remove the triggers on shutdown (it doesn't
@@ -4958,8 +5678,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_EAP_PROXY
{
size_t len;
- wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi,
- &len);
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
+ wpa_s->imsi, &len);
if (wpa_s->mnc_len > 0) {
wpa_s->imsi[len] = '\0';
wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
@@ -4984,6 +5704,17 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
hs20_init(wpa_s);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
+ if (wpa_s->conf->oce) {
+ if ((wpa_s->conf->oce & OCE_STA) &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
+ wpa_s->enable_oce = OCE_STA;
+ if ((wpa_s->conf->oce & OCE_STA_CFON) &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
+ /* TODO: Need to add STA-CFON support */
+ wpa_printf(MSG_ERROR,
+ "OCE STA-CFON feature is not yet supported");
+ }
+ }
wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
#endif /* CONFIG_MBO */
@@ -5248,6 +5979,7 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
#ifdef CONFIG_MESH
unsigned int mesh_if_created = wpa_s->mesh_if_created;
char *ifname = NULL;
+ struct wpa_supplicant *parent = wpa_s->parent;
#endif /* CONFIG_MESH */
/* Remove interface from the global list of interfaces */
@@ -5283,7 +6015,7 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
#ifdef CONFIG_MESH
if (mesh_if_created) {
- wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname);
+ wpa_drv_if_remove(parent, WPA_IF_MESH, ifname);
os_free(ifname);
}
#endif /* CONFIG_MESH */
@@ -5647,6 +6379,16 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_WOWLAN_TRIGGERS) {
+ struct wpa_driver_capa capa;
+ int res = wpa_drv_get_capa(wpa_s, &capa);
+
+ if (res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+ wpa_printf(MSG_ERROR,
+ "Failed to update wowlan_triggers to '%s'",
+ wpa_s->conf->wowlan_triggers);
+ }
+
#ifdef CONFIG_WPS
wpas_wps_update_config(wpa_s);
#endif /* CONFIG_WPS */
@@ -5806,6 +6548,35 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
}
+#ifdef CONFIG_FILS
+void fils_connection_failure(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const u8 *realm, *username, *rrk;
+ size_t realm_len, username_len, rrk_len;
+ u16 next_seq_num;
+
+ if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt) ||
+ eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap,
+ &username, &username_len,
+ &realm, &realm_len, &next_seq_num,
+ &rrk, &rrk_len) != 0 ||
+ !realm)
+ return;
+
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "FILS: Store last connection failure realm",
+ realm, realm_len);
+ os_free(wpa_s->last_con_fail_realm);
+ wpa_s->last_con_fail_realm = os_malloc(realm_len);
+ if (wpa_s->last_con_fail_realm) {
+ wpa_s->last_con_fail_realm_len = realm_len;
+ os_memcpy(wpa_s->last_con_fail_realm, realm, realm_len);
+ }
+}
+#endif /* CONFIG_FILS */
+
+
int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s)
{
return wpa_s->conf->ap_scan == 2 ||
@@ -5876,6 +6647,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
case WPA_CTRL_REQ_SIM:
str_clear_free(eap->external_sim_resp);
eap->external_sim_resp = os_strdup(value);
+ eap->pending_req_sim = 0;
break;
case WPA_CTRL_REQ_PSK_PASSPHRASE:
if (wpa_config_set(ssid, "psk", value, 0) < 0)
@@ -5944,6 +6716,7 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
(!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
+ !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) &&
!ssid->mem_only_psk)
return 1;
@@ -6128,6 +6901,7 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s)
wpa_s->extra_blacklist_count = 0;
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
+ wpa_s->last_owe_group = 0;
if (wpa_supplicant_fast_associate(wpa_s) != 1)
wpa_supplicant_req_scan(wpa_s, 0, 0);
@@ -6254,489 +7028,6 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
}
-static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
-{
- struct rrm_data *rrm = data;
-
- if (!rrm->notify_neighbor_rep) {
- wpa_printf(MSG_ERROR,
- "RRM: Unexpected neighbor report timeout");
- return;
- }
-
- wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
- rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
-
- rrm->notify_neighbor_rep = NULL;
- rrm->neighbor_rep_cb_ctx = NULL;
-}
-
-
-/*
- * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
- * @wpa_s: Pointer to wpa_supplicant
- */
-void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
-{
- wpa_s->rrm.rrm_used = 0;
-
- eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
- NULL);
- if (wpa_s->rrm.notify_neighbor_rep)
- wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
- wpa_s->rrm.next_neighbor_rep_token = 1;
-}
-
-
-/*
- * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
- * @wpa_s: Pointer to wpa_supplicant
- * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
- * @report_len: Length of neighbor report buffer
- */
-void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
- const u8 *report, size_t report_len)
-{
- struct wpabuf *neighbor_rep;
-
- wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
- if (report_len < 1)
- return;
-
- if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
- wpa_printf(MSG_DEBUG,
- "RRM: Discarding neighbor report with token %d (expected %d)",
- report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
- return;
- }
-
- eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
- NULL);
-
- if (!wpa_s->rrm.notify_neighbor_rep) {
- wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
- return;
- }
-
- /* skipping the first byte, which is only an id (dialog token) */
- neighbor_rep = wpabuf_alloc(report_len - 1);
- if (neighbor_rep == NULL)
- return;
- wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
- wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
- report[0]);
- wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
- neighbor_rep);
- wpa_s->rrm.notify_neighbor_rep = NULL;
- wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
-}
-
-
-#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
-/* Workaround different, undefined for Windows, error codes used here */
-#define ENOTCONN -1
-#define EOPNOTSUPP -1
-#define ECANCELED -1
-#endif
-
-/* Measurement Request element + Location Subject + Maximum Age subelement */
-#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4)
-/* Measurement Request element + Location Civic Request */
-#define MEASURE_REQUEST_CIVIC_LEN (3 + 5)
-
-
-/**
- * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
- * @wpa_s: Pointer to wpa_supplicant
- * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
- * is sent in the request.
- * @lci: if set, neighbor request will include LCI request
- * @civic: if set, neighbor request will include civic location request
- * @cb: Callback function to be called once the requested report arrives, or
- * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
- * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
- * the requester's responsibility to free it.
- * In the latter case NULL will be sent in 'neighbor_rep'.
- * @cb_ctx: Context value to send the callback function
- * Returns: 0 in case of success, negative error code otherwise
- *
- * In case there is a previous request which has not been answered yet, the
- * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
- * Request must contain a callback function.
- */
-int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
- const struct wpa_ssid_value *ssid,
- int lci, int civic,
- void (*cb)(void *ctx,
- struct wpabuf *neighbor_rep),
- void *cb_ctx)
-{
- struct wpabuf *buf;
- const u8 *rrm_ie;
-
- if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
- wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
- return -ENOTCONN;
- }
-
- if (!wpa_s->rrm.rrm_used) {
- wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
- return -EOPNOTSUPP;
- }
-
- rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
- WLAN_EID_RRM_ENABLED_CAPABILITIES);
- if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
- !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
- wpa_printf(MSG_DEBUG,
- "RRM: No network support for Neighbor Report.");
- return -EOPNOTSUPP;
- }
-
- if (!cb) {
- wpa_printf(MSG_DEBUG,
- "RRM: Neighbor Report request must provide a callback.");
- return -EINVAL;
- }
-
- /* Refuse if there's a live request */
- if (wpa_s->rrm.notify_neighbor_rep) {
- wpa_printf(MSG_DEBUG,
- "RRM: Currently handling previous Neighbor Report.");
- return -EBUSY;
- }
-
- /* 3 = action category + action code + dialog token */
- buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) +
- (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) +
- (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0));
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG,
- "RRM: Failed to allocate Neighbor Report Request");
- return -ENOMEM;
- }
-
- wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
- (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
- wpa_s->rrm.next_neighbor_rep_token);
-
- wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
- wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
- wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
- if (ssid) {
- wpabuf_put_u8(buf, WLAN_EID_SSID);
- wpabuf_put_u8(buf, ssid->ssid_len);
- wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
- }
-
- if (lci) {
- /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
- wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
- wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN);
-
- /*
- * Measurement token; nonzero number that is unique among the
- * Measurement Request elements in a particular frame.
- */
- wpabuf_put_u8(buf, 1); /* Measurement Token */
-
- /*
- * Parallel, Enable, Request, and Report bits are 0, Duration is
- * reserved.
- */
- wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
- wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */
-
- /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */
- /* Location Subject */
- wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
-
- /* Optional Subelements */
- /*
- * IEEE P802.11-REVmc/D5.0 Figure 9-170
- * The Maximum Age subelement is required, otherwise the AP can
- * send only data that was determined after receiving the
- * request. Setting it here to unlimited age.
- */
- wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
- wpabuf_put_u8(buf, 2);
- wpabuf_put_le16(buf, 0xffff);
- }
-
- if (civic) {
- /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
- wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
- wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN);
-
- /*
- * Measurement token; nonzero number that is unique among the
- * Measurement Request elements in a particular frame.
- */
- wpabuf_put_u8(buf, 2); /* Measurement Token */
-
- /*
- * Parallel, Enable, Request, and Report bits are 0, Duration is
- * reserved.
- */
- wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
- /* Measurement Type */
- wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC);
-
- /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14:
- * Location Civic request */
- /* Location Subject */
- wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
- wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */
- /* Location Service Interval Units: Seconds */
- wpabuf_put_u8(buf, 0);
- /* Location Service Interval: 0 - Only one report is requested
- */
- wpabuf_put_le16(buf, 0);
- /* No optional subelements */
- }
-
- wpa_s->rrm.next_neighbor_rep_token++;
-
- if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
- wpa_s->own_addr, wpa_s->bssid,
- wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
- wpa_printf(MSG_DEBUG,
- "RRM: Failed to send Neighbor Report Request");
- wpabuf_free(buf);
- return -ECANCELED;
- }
-
- wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
- wpa_s->rrm.notify_neighbor_rep = cb;
- eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
- wpas_rrm_neighbor_rep_timeout_handler,
- &wpa_s->rrm, NULL);
-
- wpabuf_free(buf);
- return 0;
-}
-
-
-static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s,
- const u8 *request, size_t len,
- struct wpabuf *report)
-{
- u8 token, type, subject;
- u16 max_age = 0;
- struct os_reltime t, diff;
- unsigned long diff_l;
- u8 *ptoken;
- const u8 *subelem;
-
- if (!wpa_s->lci || len < 3 + 4)
- return report;
-
- token = *request++;
- /* Measurement request mode isn't used */
- request++;
- type = *request++;
- subject = *request++;
-
- wpa_printf(MSG_DEBUG,
- "Measurement request token %u type %u location subject %u",
- token, type, subject);
-
- if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) {
- wpa_printf(MSG_INFO,
- "Not building LCI report - bad type or location subject");
- return report;
- }
-
- /* Subelements are formatted exactly like elements */
- subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE);
- if (subelem && subelem[1] == 2)
- max_age = WPA_GET_LE16(subelem + 2);
-
- if (os_get_reltime(&t))
- return report;
-
- os_reltime_sub(&t, &wpa_s->lci_time, &diff);
- /* LCI age is calculated in 10th of a second units. */
- diff_l = diff.sec * 10 + diff.usec / 100000;
-
- if (max_age != 0xffff && max_age < diff_l)
- return report;
-
- if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci)))
- return report;
-
- wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT);
- wpabuf_put_u8(report, wpabuf_len(wpa_s->lci));
- /* We'll override user's measurement token */
- ptoken = wpabuf_put(report, 0);
- wpabuf_put_buf(report, wpa_s->lci);
- *ptoken = token;
-
- return report;
-}
-
-
-void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
- const u8 *src,
- const u8 *frame, size_t len)
-{
- struct wpabuf *buf, *report;
- u8 token;
- const u8 *ie, *end;
-
- if (wpa_s->wpa_state != WPA_COMPLETED) {
- wpa_printf(MSG_INFO,
- "RRM: Ignoring radio measurement request: Not associated");
- return;
- }
-
- if (!wpa_s->rrm.rrm_used) {
- wpa_printf(MSG_INFO,
- "RRM: Ignoring radio measurement request: Not RRM network");
- return;
- }
-
- if (len < 3) {
- wpa_printf(MSG_INFO,
- "RRM: Ignoring too short radio measurement request");
- return;
- }
-
- end = frame + len;
-
- token = *frame++;
-
- /* Ignore number of repetitions because it's not used in LCI request */
- frame += 2;
-
- report = NULL;
- while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) &&
- ie[1] >= 3) {
- u8 msmt_type;
-
- msmt_type = ie[4];
- wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type);
-
- switch (msmt_type) {
- case MEASURE_TYPE_LCI:
- report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1],
- report);
- break;
- default:
- wpa_printf(MSG_INFO,
- "RRM: Unsupported radio measurement request %d",
- msmt_type);
- break;
- }
-
- frame = ie + ie[1] + 2;
- }
-
- if (!report)
- return;
-
- buf = wpabuf_alloc(3 + wpabuf_len(report));
- if (!buf) {
- wpabuf_free(report);
- return;
- }
-
- wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
- wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT);
- wpabuf_put_u8(buf, token);
-
- wpabuf_put_buf(buf, report);
- wpabuf_free(report);
-
- if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
- wpa_s->own_addr, wpa_s->bssid,
- wpabuf_head(buf), wpabuf_len(buf), 0)) {
- wpa_printf(MSG_ERROR,
- "RRM: Radio measurement report failed: Sending Action frame failed");
- }
- wpabuf_free(buf);
-}
-
-
-void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
- const u8 *src,
- const u8 *frame, size_t len,
- int rssi)
-{
- struct wpabuf *buf;
- const struct rrm_link_measurement_request *req;
- struct rrm_link_measurement_report report;
-
- if (wpa_s->wpa_state != WPA_COMPLETED) {
- wpa_printf(MSG_INFO,
- "RRM: Ignoring link measurement request. Not associated");
- return;
- }
-
- if (!wpa_s->rrm.rrm_used) {
- wpa_printf(MSG_INFO,
- "RRM: Ignoring link measurement request. Not RRM network");
- return;
- }
-
- if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
- wpa_printf(MSG_INFO,
- "RRM: Measurement report failed. TX power insertion not supported");
- return;
- }
-
- req = (const struct rrm_link_measurement_request *) frame;
- if (len < sizeof(*req)) {
- wpa_printf(MSG_INFO,
- "RRM: Link measurement report failed. Request too short");
- return;
- }
-
- os_memset(&report, 0, sizeof(report));
- report.tpc.eid = WLAN_EID_TPC_REPORT;
- report.tpc.len = 2;
- report.rsni = 255; /* 255 indicates that RSNI is not available */
- report.dialog_token = req->dialog_token;
-
- /*
- * It's possible to estimate RCPI based on RSSI in dBm. This
- * calculation will not reflect the correct value for high rates,
- * but it's good enough for Action frames which are transmitted
- * with up to 24 Mbps rates.
- */
- if (!rssi)
- report.rcpi = 255; /* not available */
- else if (rssi < -110)
- report.rcpi = 0;
- else if (rssi > 0)
- report.rcpi = 220;
- else
- report.rcpi = (rssi + 110) * 2;
-
- /* action_category + action_code */
- buf = wpabuf_alloc(2 + sizeof(report));
- if (buf == NULL) {
- wpa_printf(MSG_ERROR,
- "RRM: Link measurement report failed. Buffer allocation failed");
- return;
- }
-
- wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
- wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
- wpabuf_put_data(buf, &report, sizeof(report));
- wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
- wpabuf_head(buf), wpabuf_len(buf));
-
- if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
- wpa_s->own_addr, wpa_s->bssid,
- wpabuf_head(buf), wpabuf_len(buf), 0)) {
- wpa_printf(MSG_ERROR,
- "RRM: Link measurement report failed. Send action failed");
- }
- wpabuf_free(buf);
-}
-
-
struct wpa_supplicant *
wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame)
{
@@ -6850,18 +7141,56 @@ wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
}
+static int wpa_set_driver_tmp_disallow_list(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss_tmp_disallowed *tmp;
+ unsigned int num_bssid = 0;
+ u8 *bssids;
+ int ret;
+
+ bssids = os_malloc(dl_list_len(&wpa_s->bss_tmp_disallowed) * ETH_ALEN);
+ if (!bssids)
+ return -1;
+ dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
+ struct wpa_bss_tmp_disallowed, list) {
+ os_memcpy(&bssids[num_bssid * ETH_ALEN], tmp->bssid,
+ ETH_ALEN);
+ num_bssid++;
+ }
+ ret = wpa_drv_set_bssid_blacklist(wpa_s, num_bssid, bssids);
+ os_free(bssids);
+ return ret;
+}
+
+
+static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx;
+
+ /* Make sure the bss is not already freed */
+ dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
+ struct wpa_bss_tmp_disallowed, list) {
+ if (bss == tmp) {
+ dl_list_del(&tmp->list);
+ os_free(tmp);
+ wpa_set_driver_tmp_disallow_list(wpa_s);
+ break;
+ }
+ }
+}
+
+
void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
unsigned int sec)
{
struct wpa_bss_tmp_disallowed *bss;
- struct os_reltime until;
-
- os_get_reltime(&until);
- until.sec += sec;
bss = wpas_get_disallowed_bss(wpa_s, bssid);
if (bss) {
- bss->disallowed_until = until;
+ eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
+ eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout,
+ wpa_s, bss);
return;
}
@@ -6872,27 +7201,20 @@ void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
return;
}
- bss->disallowed_until = until;
os_memcpy(bss->bssid, bssid, ETH_ALEN);
dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+ wpa_set_driver_tmp_disallow_list(wpa_s);
+ eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout,
+ wpa_s, bss);
}
int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev;
- struct os_reltime now, age;
-
- os_get_reltime(&now);
dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
- if (!os_reltime_before(&now, &tmp->disallowed_until)) {
- /* This BSS is not disallowed anymore */
- dl_list_del(&tmp->list);
- os_free(tmp);
- continue;
- }
if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
bss = tmp;
break;
@@ -6901,9 +7223,5 @@ int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
if (!bss)
return 0;
- os_reltime_sub(&bss->disallowed_until, &now, &age);
- wpa_printf(MSG_DEBUG,
- "BSS " MACSTR " disabled for %ld.%0ld seconds",
- MAC2STR(bss->bssid), age.sec, age.usec);
return 1;
}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index b3138e301798..4f5916025aab 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -98,9 +98,7 @@ eapol_version=1
# parameters (e.g., WPA IE generation); this mode can also be used with
# non-WPA drivers when using IEEE 802.1X mode; do not try to associate with
# APs (i.e., external program needs to control association). This mode must
-# also be used when using wired Ethernet drivers.
-# Note: macsec_qca driver is one type of Ethernet driver which implements
-# macsec feature.
+# also be used when using wired Ethernet drivers (including MACsec).
# 2: like 0, but associate with APs using security policy and SSID (but not
# BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to
# enable operation with hidden SSIDs and optimized roaming; in this mode,
@@ -185,13 +183,13 @@ fast_reauth=1
# 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 wpa_supplicant is
# built to use OpenSSL.
#openssl_ciphers=DEFAULT:!EXP:!LOW
-
# Dynamic EAP methods
# If EAP methods were built dynamically as shared object files, they need to be
# loaded here before being used in the network blocks. By default, EAP methods
@@ -220,9 +218,15 @@ fast_reauth=1
# Wi-Fi Protected Setup (WPS) parameters
# Universally Unique IDentifier (UUID; see RFC 4122) of the device
-# If not configured, UUID will be generated based on the local MAC address.
+# If not configured, UUID will be generated based on the mechanism selected with
+# the auto_uuid parameter.
#uuid=12345678-9abc-def0-1234-56789abcdef0
+# Automatic UUID behavior
+# 0 = generate static value based on the local MAC address (default)
+# 1 = generate a random UUID every time wpa_supplicant starts
+#auto_uuid=0
+
# Device Name
# User-friendly description of device; up to 32 octets encoded in UTF-8
#device_name=Wireless Client
@@ -424,11 +428,50 @@ fast_reauth=1
# 2 = like 1, but maintain OUI (with local admin bit set)
#preassoc_mac_addr=0
+# MAC address policy for GAS operations
+# 0 = use permanent MAC address
+# 1 = use random MAC address
+# 2 = like 1, but maintain OUI (with local admin bit set)
+#gas_rand_mac_addr=0
+
+# Lifetime of GAS random MAC address in seconds (default: 60)
+#gas_rand_addr_lifetime=60
+
# Interworking (IEEE 802.11u)
# Enable Interworking
# interworking=1
+# Enable P2P GO advertisement of Interworking
+# go_interworking=1
+
+# P2P GO Interworking: Access Network Type
+# 0 = Private network
+# 1 = Private network with guest access
+# 2 = Chargeable public network
+# 3 = Free public network
+# 4 = Personal device network
+# 5 = Emergency services only network
+# 14 = Test or experimental
+# 15 = Wildcard
+#go_access_network_type=0
+
+# P2P GO Interworking: Whether the network provides connectivity to the Internet
+# 0 = Unspecified
+# 1 = Network provides connectivity to the Internet
+#go_internet=1
+
+# P2P GO Interworking: Group Venue Info (optional)
+# The available values are defined in IEEE Std 802.11-2016, 9.4.1.35.
+# Example values (group,type):
+# 0,0 = Unspecified
+# 1,7 = Convention Center
+# 1,13 = Coffee Shop
+# 2,0 = Unspecified Business
+# 7,1 Private Residence
+#go_venue_group=7
+#go_venue_type=1
+
# Homogenous ESS identifier
# If this is set, scans will be used to request response only from BSSes
# belonging to the specified Homogeneous ESS. This is used only if interworking
@@ -554,6 +597,20 @@ fast_reauth=1
# pre-configured with the credential since the NAI Realm information
# may not be available or fetched.
#
+# required_roaming_consortium: Required Roaming Consortium OI
+# If required_roaming_consortium_len is non-zero, this field contains the
+# Roaming Consortium OI that is required to be advertised by the AP for
+# the credential to be considered matching.
+#
+# roaming_consortiums: Roaming Consortium OI(s) memberships
+# This string field contains one or more comma delimited OIs (hexdump)
+# identifying the roaming consortiums of which the provider is a member.
+# The list is sorted from the most preferred one to the least preferred
+# one. A match between the Roaming Consortium OIs advertised by an AP and
+# the OIs in this list indicates that successful authentication is
+# possible.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/RoamingConsortiumOI)
+#
# eap: Pre-configured EAP method
# This optional field can be used to specify which EAP method will be
# used with this credential. If not set, the EAP method is selected
@@ -681,7 +738,7 @@ fast_reauth=1
# Format:
# non_pref_chan=<oper_class>:<chan>:<preference>:<reason>
# Example:
-# non_pref_chan="81:5:10:2 81:1:0:2 81:9:0:2"
+# non_pref_chan=81:5:10:2 81:1:0:2 81:9:0:2
# MBO Cellular Data Capabilities
# 1 = Cellular data connection available
@@ -689,6 +746,13 @@ fast_reauth=1
# 3 = Not cellular capable (default)
#mbo_cell_capa=3
+# Optimized Connectivity Experience (OCE)
+# oce: Enable OCE features (bitmap)
+# Set BIT(0) to Enable OCE in non-AP STA mode (default; disabled if the driver
+# does not indicate support for OCE in STA mode)
+# Set BIT(1) to Enable OCE in STA-CFON mode
+#oce=1
+
# network block
#
# Each network (usually AP's sharing the same SSID) is configured as a separate
@@ -801,6 +865,7 @@ fast_reauth=1
# proto: list of accepted protocols
# WPA = WPA/IEEE 802.11i/D3.0
# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN)
+# Note that RSN is used also for WPA3.
# If not set, this defaults to: WPA RSN
#
# key_mgmt: list of accepted authenticated key management protocols
@@ -813,15 +878,23 @@ fast_reauth=1
# instead)
# FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key
# FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication
+# FT-EAP-SHA384 = Fast BSS Transition (IEEE 802.11r) with EAP authentication
+# and using SHA384
# WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms
# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
# SAE = Simultaneous authentication of equals; pre-shared key/password -based
# authentication with stronger security than WPA-PSK especially when using
-# not that strong password
+# not that strong password; a.k.a. WPA3-Personal
# FT-SAE = SAE with FT
# WPA-EAP-SUITE-B = Suite B 128-bit level
# WPA-EAP-SUITE-B-192 = Suite B 192-bit level
# OSEN = Hotspot 2.0 Rel 2 online signup connection
+# 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
# If not set, this defaults to: WPA-PSK WPA-EAP
#
# ieee80211w: whether management frame protection is enabled
@@ -855,6 +928,14 @@ fast_reauth=1
# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11]
# If not set, this defaults to: CCMP TKIP WEP104 WEP40
#
+# group_mgmt: list of accepted group management ciphers for RSN (PMF)
+# AES-128-CMAC = BIP-CMAC-128
+# BIP-GMAC-128
+# BIP-GMAC-256
+# BIP-CMAC-256
+# If not set, no constraint on the cipher, i.e., accept whichever cipher the AP
+# indicates.
+#
# psk: WPA preshared key; 256-bit pre-shared key
# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
@@ -872,22 +953,54 @@ fast_reauth=1
# 1 = do not store psk/passphrase to the configuration file
#mem_only_psk=0
#
+# sae_password: SAE password
+# This parameter can be used to set a password for SAE. By default, the
+# passphrase from the psk parameter is used if this separate parameter is not
+# used, but psk follows the WPA-PSK constraints (8..63 characters) even though
+# SAE passwords do not have such constraints.
+#
+# sae_password_id: SAE password identifier
+# This parameter can be used to set an identifier for the SAE password. By
+# default, no such identifier is used. If set, the specified identifier value
+# is used by the other peer to select which password to use for authentication.
+#
# eapol_flags: IEEE 802.1X/EAPOL options (bit field)
# Dynamic WEP key required for non-WPA mode
# bit0 (1): require dynamically generated unicast WEP key
# bit1 (2): require dynamically generated broadcast WEP key
# (3 = require both keys; default)
-# Note: When using wired authentication (including macsec_qca driver),
+# Note: When using wired authentication (including MACsec drivers),
# eapol_flags must be set to 0 for the authentication to be completed
# successfully.
#
# macsec_policy: IEEE 802.1X/MACsec options
-# This determines how sessions are secured with MACsec. It is currently
-# applicable only when using the macsec_qca driver interface.
+# This determines how sessions are secured with MACsec (only for MACsec
+# drivers).
# 0: MACsec not in use (default)
# 1: MACsec enabled - Should secure, accept key server's advice to
# determine whether to use a secure session or not.
#
+# macsec_integ_only: IEEE 802.1X/MACsec transmit mode
+# This setting applies only when MACsec is in use, i.e.,
+# - macsec_policy is enabled
+# - the key server has decided to enable MACsec
+# 0: Encrypt traffic (default)
+# 1: Integrity only
+#
+# macsec_port: IEEE 802.1X/MACsec port
+# Port component of the SCI
+# Range: 1-65534 (default: 1)
+#
+# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode
+# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
+# In this mode, instances of wpa_supplicant can act as MACsec peers. The peer
+# with lower priority will become the key server and start distributing SAKs.
+# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-bytes (128 bit)
+# hex-string (32 hex-digits)
+# mka_ckn (CKN = CAK Name) takes a 32-bytes (256 bit) hex-string (64 hex-digits)
+# mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being
+# default priority
+#
# mixed_cell: This option can be used to configure whether so called mixed
# cells, i.e., networks that use both plaintext and encryption in the same
# SSID, are allowed when selecting a BSS from scan results.
@@ -903,18 +1016,12 @@ fast_reauth=1
# hex without quotation, e.g., 0102030405)
# wep_tx_keyidx: Default WEP key index (TX) (0..3)
#
-# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is
-# allowed. This is only used with RSN/WPA2.
-# 0 = disabled (default)
-# 1 = enabled
-#peerkey=1
-#
# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
#
# group_rekey: Group rekeying time in seconds. This value, if non-zero, is used
# as the dot11RSNAConfigGroupRekeyTime parameter when operating in
-# Authenticator role in IBSS.
+# Authenticator role in IBSS, or in AP and mesh modes.
#
# Following fields are only used with internal EAP implementation.
# eap: space-separated list of accepted EAP methods
@@ -1113,12 +1220,17 @@ fast_reauth=1
# that have issues interoperating with updated TLS version)
# tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers
# that have issues interoperating with updated TLS version)
+# tls_disable_tlsv1_3=1 - disable use of TLSv1.3 (a workaround for AAA servers
+# that have issues interoperating with updated TLS version)
# tls_ext_cert_check=0 - No external server certificate validation (default)
# tls_ext_cert_check=1 - External server certificate validation enabled; this
# requires an external program doing validation of server certificate
# chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control
# interface and report the result of the validation with
# CTRL-RSP_EXT_CERT_CHECK.
+# tls_suiteb=0 - do not apply Suite B 192-bit constraints on TLS (default)
+# tls_suiteb=1 - apply Suite B 192-bit constraints on TLS; this is used in
+# particular when using Suite B with RSA keys of >= 3K (3072) bits
#
# Following certificate/private key fields are used in inner Phase2
# authentication when using EAP-TTLS or EAP-PEAP.
@@ -1187,6 +1299,10 @@ fast_reauth=1
# update_identifier: PPS MO ID
# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# roaming_consortium_selection: Roaming Consortium Selection
+# The matching Roaming Consortium OI that was used to generate this
+# network profile.
# Station inactivity limit
#
@@ -1216,6 +1332,11 @@ fast_reauth=1
# 1 = WPS disabled
#wps_disabled=0
+# FILS DH Group
+# 0 = PFS disabled with FILS shared key authentication (default)
+# 1-65535 = DH Group to use for FILS PFS
+#fils_dh_group=0
+
# MAC address policy
# 0 = use permanent MAC address
# 1 = use random MAC address for each ESS connection
@@ -1674,15 +1795,26 @@ network={
}
-# Example MACsec configuration
-#network={
-# key_mgmt=IEEE8021X
-# eap=TTLS
-# phase2="auth=PAP"
-# anonymous_identity="anonymous@example.com"
-# identity="user@example.com"
-# password="secretr"
-# ca_cert="/etc/cert/ca.pem"
-# eapol_flags=0
-# macsec_policy=1
-#}
+# Example configuration using EAP-TTLS for authentication and key
+# generation for MACsec
+network={
+ key_mgmt=IEEE8021X
+ eap=TTLS
+ phase2="auth=PAP"
+ anonymous_identity="anonymous@example.com"
+ identity="user@example.com"
+ password="secretr"
+ ca_cert="/etc/cert/ca.pem"
+ eapol_flags=0
+ macsec_policy=1
+}
+
+# Example configuration for MACsec with preshared key
+network={
+ key_mgmt=NONE
+ eapol_flags=0
+ macsec_policy=1
+ mka_cak=0123456789ABCDEF0123456789ABCDEF
+ mka_ckn=6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435
+ mka_priority=128
+}
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index ef9273d09a32..8b749f44e235 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -9,6 +9,7 @@
#ifndef WPA_SUPPLICANT_I_H
#define WPA_SUPPLICANT_I_H
+#include "utils/bitfield.h"
#include "utils/list.h"
#include "common/defs.h"
#include "common/sae.h"
@@ -295,7 +296,7 @@ struct wpa_global {
#ifdef CONFIG_WIFI_DISPLAY
int wifi_display;
-#define MAX_WFD_SUBELEMS 10
+#define MAX_WFD_SUBELEMS 12
struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
#endif /* CONFIG_WIFI_DISPLAY */
@@ -344,6 +345,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
void radio_work_done(struct wpa_radio_work *work);
void radio_remove_works(struct wpa_supplicant *wpa_s,
const char *type, int remove_all);
+void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx);
void radio_work_check_next(struct wpa_supplicant *wpa_s);
struct wpa_radio_work *
radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
@@ -424,6 +426,12 @@ struct rrm_data {
/* next_neighbor_rep_token - Next request's dialog token */
u8 next_neighbor_rep_token;
+
+ /* token - Dialog token of the current radio measurement */
+ u8 token;
+
+ /* destination address of the current radio measurement request */
+ u8 dst_addr[ETH_ALEN];
};
enum wpa_supplicant_test_failure {
@@ -443,7 +451,28 @@ struct icon_entry {
struct wpa_bss_tmp_disallowed {
struct dl_list list;
u8 bssid[ETH_ALEN];
- struct os_reltime disallowed_until;
+};
+
+struct beacon_rep_data {
+ u8 token;
+ struct wpa_driver_scan_params scan_params;
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ u8 bssid[ETH_ALEN];
+ enum beacon_report_detail report_detail;
+ struct bitfield *eids;
+};
+
+
+struct external_pmksa_cache {
+ struct dl_list list;
+ void *pmksa_cache;
+};
+
+struct fils_hlp_req {
+ struct dl_list list;
+ u8 dst[ETH_ALEN];
+ struct wpabuf *pkt;
};
/**
@@ -503,6 +532,8 @@ struct wpa_supplicant {
struct wpa_bss *current_bss;
int ap_ies_from_associnfo;
unsigned int assoc_freq;
+ u8 *last_con_fail_realm;
+ size_t last_con_fail_realm_len;
/* Selected configuration (based on Beacon/ProbeResp WPA IE) */
int pairwise_cipher;
@@ -639,6 +670,7 @@ struct wpa_supplicant {
struct os_reltime scan_min_time;
int scan_runs; /* number of scan runs since WPS was started */
int *next_scan_freqs;
+ int *select_network_scan_freqs;
int *manual_scan_freqs;
int *manual_sched_scan_freqs;
unsigned int manual_scan_passive:1;
@@ -652,6 +684,12 @@ struct wpa_supplicant {
int normal_scans; /* normal scans run before sched_scan */
int scan_for_connection; /* whether the scan request was triggered for
* finding a connection */
+ /*
+ * A unique cookie representing the vendor scan request. This cookie is
+ * returned from the driver interface. 0 indicates that there is no
+ * pending vendor scan request.
+ */
+ u64 curr_scan_cookie;
#define MAX_SCAN_ID 16
int scan_id[MAX_SCAN_ID];
unsigned int scan_id_count;
@@ -705,6 +743,8 @@ struct wpa_supplicant {
unsigned int mac_addr_changed:1;
unsigned int added_vif:1;
unsigned int wnmsleep_used:1;
+ unsigned int owe_transition_select:1;
+ unsigned int owe_transition_search:1;
struct os_reltime last_mac_addr_change;
int last_mac_addr_style;
@@ -715,13 +755,15 @@ struct wpa_supplicant {
int sta_uapsd;
int set_ap_uapsd;
int ap_uapsd;
+ int auth_alg;
+ u16 last_owe_group;
#ifdef CONFIG_SME
struct {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
int freq;
- u8 assoc_req_ie[200];
+ u8 assoc_req_ie[1500];
size_t assoc_req_ie_len;
int mfp;
int ft_used;
@@ -752,6 +794,8 @@ struct wpa_supplicant {
struct wpabuf *sae_token;
int sae_group_index;
unsigned int sae_pmksa_caching:1;
+ u16 seq_num;
+ struct external_auth ext_auth;
#endif /* CONFIG_SAE */
} sme;
#endif /* CONFIG_SME */
@@ -770,6 +814,10 @@ struct wpa_supplicant {
unsigned int mesh_if_created:1;
unsigned int mesh_ht_enabled:1;
unsigned int mesh_vht_enabled:1;
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+ /* struct external_pmksa_cache::list */
+ struct dl_list mesh_external_pmksa_cache;
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
#endif /* CONFIG_MESH */
unsigned int off_channel_freq;
@@ -789,6 +837,7 @@ struct wpa_supplicant {
result);
unsigned int roc_waiting_drv_freq;
int action_tx_wait_time;
+ int action_tx_wait_time_used;
int p2p_mgmt;
@@ -856,6 +905,7 @@ struct wpa_supplicant {
unsigned int p2p_auto_join:1;
unsigned int p2p_auto_pd:1;
+ unsigned int p2p_go_do_acs:1;
unsigned int p2p_persistent_group:1;
unsigned int p2p_fallback_to_go_neg:1;
unsigned int p2p_pd_before_go_neg:1;
@@ -871,6 +921,7 @@ struct wpa_supplicant {
unsigned int p2p_disable_ip_addr_req:1;
unsigned int p2ps_method_config_any:1;
unsigned int p2p_cli_probe:1;
+ enum hostapd_hw_mode p2p_go_acs_band;
int p2p_persistent_go_freq;
int p2p_persistent_id;
int p2p_go_intent;
@@ -923,6 +974,7 @@ struct wpa_supplicant {
int best_overall_freq;
struct gas_query *gas;
+ struct gas_server *gas_server;
#ifdef CONFIG_INTERWORKING
unsigned int fetch_anqp_in_progress:1;
@@ -981,6 +1033,7 @@ struct wpa_supplicant {
unsigned int wmm_ac_supported:1;
unsigned int ext_work_in_progress:1;
unsigned int own_disconnect_req:1;
+ unsigned int ignore_post_flush_scan_res:1;
#define MAC_ADDR_RAND_SCAN BIT(0)
#define MAC_ADDR_RAND_SCHED_SCAN BIT(1)
@@ -1006,6 +1059,14 @@ struct wpa_supplicant {
struct neighbor_report *wnm_neighbor_report_elements;
struct os_reltime wnm_cand_valid_until;
u8 wnm_cand_from_bss[ETH_ALEN];
+ struct wpabuf *coloc_intf_elems;
+ u8 coloc_intf_dialog_token;
+ u8 coloc_intf_auto_report;
+ u8 coloc_intf_timeout;
+#ifdef CONFIG_MBO
+ unsigned int wnm_mbo_trans_reason_present:1;
+ u8 wnm_mbo_transition_reason;
+#endif /* CONFIG_MBO */
#endif /* CONFIG_WNM */
#ifdef CONFIG_TESTING_GET_GTK
@@ -1024,10 +1085,19 @@ struct wpa_supplicant {
struct l2_packet_data *l2_test;
unsigned int extra_roc_dur;
enum wpa_supplicant_test_failure test_failure;
+ char *get_pref_freq_list_override;
unsigned int reject_btm_req_reason;
unsigned int p2p_go_csa_on_inv:1;
unsigned int ignore_auth_resp:1;
unsigned int ignore_assoc_disallow:1;
+ unsigned int testing_resend_assoc:1;
+ struct wpabuf *sae_commit_override;
+ enum wpa_alg last_tk_alg;
+ u8 last_tk_addr[ETH_ALEN];
+ int last_tk_key_idx;
+ u8 last_tk[WPA_TK_MAX_LEN];
+ size_t last_tk_len;
+ struct wpabuf *last_assoc_req_wpa_ie;
#endif /* CONFIG_TESTING_OPTIONS */
struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1038,6 +1108,7 @@ struct wpa_supplicant {
u8 last_tspecs_count;
struct rrm_data rrm;
+ struct beacon_rep_data beacon_rep_data;
#ifdef CONFIG_FST
struct fst_iface *fst;
@@ -1055,6 +1126,14 @@ struct wpa_supplicant {
} *non_pref_chan;
size_t non_pref_chan_num;
u8 mbo_wnm_token;
+ /**
+ * enable_oce - Enable OCE if it is enabled by user and device also
+ * supports OCE.
+ * User can enable OCE with wpa_config's 'oce' parameter as follows -
+ * - Set BIT(0) to enable OCE in non-AP STA mode.
+ * - Set BIT(1) to enable OCE in STA-CFON mode.
+ */
+ u8 enable_oce;
#endif /* CONFIG_MBO */
/*
@@ -1069,6 +1148,92 @@ struct wpa_supplicant {
*/
struct wpabuf *lci;
struct os_reltime lci_time;
+
+ struct os_reltime beacon_rep_scan;
+
+ /* FILS HLP requests (struct fils_hlp_req) */
+ struct dl_list fils_hlp_req;
+
+ struct sched_scan_relative_params {
+ /**
+ * relative_rssi_set - Enable relatively preferred BSS reporting
+ *
+ * 0 = Disable reporting relatively preferred BSSs
+ * 1 = Enable reporting relatively preferred BSSs
+ */
+ int relative_rssi_set;
+
+ /**
+ * relative_rssi - Relative RSSI for reporting better BSSs
+ *
+ * Amount of RSSI by which a BSS should be better than the
+ * current connected BSS so that the new BSS can be reported
+ * to user space. This applies to sched_scan operations.
+ */
+ int relative_rssi;
+
+ /**
+ * relative_adjust_band - Band in which RSSI is to be adjusted
+ */
+ enum set_band relative_adjust_band;
+
+ /**
+ * relative_adjust_rssi - RSSI adjustment
+ *
+ * An amount of relative_adjust_rssi should be added to the
+ * BSSs that belong to the relative_adjust_band while comparing
+ * with other bands for BSS reporting.
+ */
+ int relative_adjust_rssi;
+ } srp;
+
+ /* RIC elements for FT protocol */
+ struct wpabuf *ric_ies;
+
+ int last_auth_timeout_sec;
+
+#ifdef CONFIG_DPP
+ struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
+ struct dl_list dpp_configurator; /* struct dpp_configurator */
+ int dpp_init_done;
+ struct dpp_authentication *dpp_auth;
+ struct wpa_radio_work *dpp_listen_work;
+ unsigned int dpp_pending_listen_freq;
+ unsigned int dpp_listen_freq;
+ u8 dpp_allowed_roles;
+ int dpp_qr_mutual;
+ int dpp_netrole_ap;
+ int dpp_auth_ok_on_ack;
+ int dpp_in_response_listen;
+ int dpp_gas_client;
+ int dpp_gas_dialog_token;
+ u8 dpp_intro_bssid[ETH_ALEN];
+ void *dpp_intro_network;
+ 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 */
+
+#ifdef CONFIG_FILS
+ unsigned int disable_fils:1;
+#endif /* CONFIG_FILS */
+ unsigned int ieee80211ac:1;
};
@@ -1101,6 +1266,7 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s);
void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr);
void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
int sec, int usec);
+void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff);
void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s);
void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
enum wpa_states state);
@@ -1158,6 +1324,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+void fils_connection_failure(struct wpa_supplicant *wpa_s);
int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason);
@@ -1183,22 +1350,28 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
struct wpabuf *neighbor_rep),
void *cb_ctx);
void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
- const u8 *src,
+ const u8 *src, const u8 *dst,
const u8 *frame, size_t len);
void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
const u8 *src,
const u8 *frame, size_t len,
int rssi);
+void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s);
+int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res,
+ struct scan_info *info);
+void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s);
+void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s);
/* MBO functions */
-int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len);
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
+ int add_oce_capa);
+const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr);
const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr);
int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
const char *non_pref_chan);
void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie);
-int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
- size_t len);
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
size_t len);
size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
@@ -1206,7 +1379,20 @@ size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
enum mbo_transition_reject_reason reason);
void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
- struct wpa_bss *bss);
+ struct wpa_bss *bss, u32 mbo_subtypes);
+void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, const u8 *sa,
+ const u8 *data, size_t slen);
+
+/* op_classes.c */
+enum chan_allowed {
+ NOT_ALLOWED, NO_IR, ALLOWED
+};
+
+enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel,
+ u8 bw);
+size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
+ size_t len);
/**
* wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@@ -1238,6 +1424,7 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid **selected_ssid);
+int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
/* eap_register.c */
int eap_register_methods(void);
@@ -1296,6 +1483,14 @@ int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid);
struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
struct wpa_ssid *group,
- int only_first_ssid);
+ int only_first_ssid, int debug_print);
+
+int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ unsigned int *freq_list);
+
+int wpa_is_fils_supported(struct wpa_supplicant *wpa_s);
+int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);
#endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index f3f2a6417d2e..f55227f82685 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -4,3 +4,4 @@ eapol_version=1
ap_scan=1
fast_reauth=1
pmf=1
+p2p_add_cli_chan=1
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index f84c8b90ac2f..4634ed7fc368 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "eapol_supp/eapol_supp_sm.h"
+#include "eap_peer/eap.h"
#include "rsn_supp/wpa.h"
#include "eloop.h"
#include "config.h"
@@ -145,6 +146,8 @@ static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf,
* extra copy here */
if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
/* Current SSID is not using IEEE 802.1X/EAP, so drop possible
* EAPOL frames (mainly, EAPOL-Start) from EAPOL state
@@ -499,6 +502,16 @@ static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
wpa_s->last_gtk_len = key_len;
}
#endif /* CONFIG_TESTING_GET_GTK */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (addr && !is_broadcast_ether_addr(addr)) {
+ wpa_s->last_tk_alg = alg;
+ os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN);
+ wpa_s->last_tk_key_idx = key_idx;
+ if (key)
+ os_memcpy(wpa_s->last_tk, key, key_len);
+ wpa_s->last_tk_len = key_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
key, key_len);
}
@@ -513,17 +526,74 @@ static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
}
-static int wpa_supplicant_add_pmkid(void *wpa_s,
- const u8 *bssid, const u8 *pmkid)
+static struct wpa_ssid * wpas_get_network_ctx(struct wpa_supplicant *wpa_s,
+ void *network_ctx)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (network_ctx == ssid)
+ return ssid;
+ }
+
+ return NULL;
+}
+
+
+static int wpa_supplicant_add_pmkid(void *_wpa_s, void *network_ctx,
+ const u8 *bssid, const u8 *pmkid,
+ const u8 *fils_cache_id,
+ const u8 *pmk, size_t pmk_len)
{
- return wpa_drv_add_pmkid(wpa_s, bssid, pmkid);
+ struct wpa_supplicant *wpa_s = _wpa_s;
+ struct wpa_ssid *ssid;
+ struct wpa_pmkid_params params;
+
+ os_memset(&params, 0, sizeof(params));
+ ssid = wpas_get_network_ctx(wpa_s, network_ctx);
+ if (ssid)
+ wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_ADDED MACSTR " %d",
+ MAC2STR(bssid), ssid->id);
+ if (ssid && fils_cache_id) {
+ params.ssid = ssid->ssid;
+ params.ssid_len = ssid->ssid_len;
+ params.fils_cache_id = fils_cache_id;
+ } else {
+ params.bssid = bssid;
+ }
+
+ params.pmkid = pmkid;
+ params.pmk = pmk;
+ params.pmk_len = pmk_len;
+
+ return wpa_drv_add_pmkid(wpa_s, &params);
}
-static int wpa_supplicant_remove_pmkid(void *wpa_s,
- const u8 *bssid, const u8 *pmkid)
+static int wpa_supplicant_remove_pmkid(void *_wpa_s, void *network_ctx,
+ const u8 *bssid, const u8 *pmkid,
+ const u8 *fils_cache_id)
{
- return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid);
+ struct wpa_supplicant *wpa_s = _wpa_s;
+ struct wpa_ssid *ssid;
+ struct wpa_pmkid_params params;
+
+ os_memset(&params, 0, sizeof(params));
+ ssid = wpas_get_network_ctx(wpa_s, network_ctx);
+ if (ssid)
+ wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_REMOVED MACSTR " %d",
+ MAC2STR(bssid), ssid->id);
+ if (ssid && fils_cache_id) {
+ params.ssid = ssid->ssid;
+ params.ssid_len = ssid->ssid_len;
+ params.fils_cache_id = fils_cache_id;
+ } else {
+ params.bssid = bssid;
+ }
+
+ params.pmkid = pmkid;
+
+ return wpa_drv_remove_pmkid(wpa_s, &params);
}
@@ -865,12 +935,13 @@ static void wpa_supplicant_eap_param_needed(void *ctx,
#ifdef CONFIG_EAP_PROXY
+
static void wpa_supplicant_eap_proxy_cb(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
size_t len;
- wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
wpa_s->imsi, &len);
if (wpa_s->mnc_len > 0) {
wpa_s->imsi[len] = '\0';
@@ -880,6 +951,52 @@ static void wpa_supplicant_eap_proxy_cb(void *ctx)
wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
}
}
+
+
+static void wpa_sm_sim_state_error_handler(struct wpa_supplicant *wpa_s)
+{
+ int i;
+ struct wpa_ssid *ssid;
+ const struct eap_method_type *eap_methods;
+
+ if (!wpa_s->conf)
+ return;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ eap_methods = ssid->eap.eap_methods;
+ if (!eap_methods)
+ continue;
+
+ for (i = 0; eap_methods[i].method != EAP_TYPE_NONE; i++) {
+ if (eap_methods[i].vendor == EAP_VENDOR_IETF &&
+ (eap_methods[i].method == EAP_TYPE_SIM ||
+ eap_methods[i].method == EAP_TYPE_AKA ||
+ eap_methods[i].method == EAP_TYPE_AKA_PRIME)) {
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+ break;
+ }
+ }
+ }
+}
+
+
+static void
+wpa_supplicant_eap_proxy_notify_sim_status(void *ctx,
+ enum eap_proxy_sim_state sim_state)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_printf(MSG_DEBUG, "eap_proxy: SIM card status %u", sim_state);
+ switch (sim_state) {
+ case SIM_STATE_ERROR:
+ wpa_sm_sim_state_error_handler(wpa_s);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "eap_proxy: SIM card status unknown");
+ break;
+ }
+}
+
#endif /* CONFIG_EAP_PROXY */
@@ -921,6 +1038,14 @@ static void wpa_supplicant_status_cb(void *ctx, const char *status,
}
+static void wpa_supplicant_eap_error_cb(void *ctx, int error_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpas_notify_eap_error(wpa_s, error_code);
+}
+
+
static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -990,12 +1115,15 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
#ifdef CONFIG_EAP_PROXY
ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb;
+ ctx->eap_proxy_notify_sim_status =
+ wpa_supplicant_eap_proxy_notify_sim_status;
#endif /* CONFIG_EAP_PROXY */
ctx->port_cb = wpa_supplicant_port_cb;
ctx->cb = wpa_supplicant_eapol_cb;
ctx->cert_cb = wpa_supplicant_cert_cb;
ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
ctx->status_cb = wpa_supplicant_status_cb;
+ ctx->eap_error_cb = wpa_supplicant_eap_error_cb;
ctx->set_anon_id = wpa_supplicant_set_anon_id;
ctx->cb_ctx = wpa_s;
wpa_s->eapol = eapol_sm_init(ctx);
@@ -1012,6 +1140,7 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
#ifndef CONFIG_NO_WPA
+
static void wpa_supplicant_set_rekey_offload(void *ctx,
const u8 *kek, size_t kek_len,
const u8 *kck, size_t kck_len,
@@ -1035,6 +1164,25 @@ static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
else
return 0;
}
+
+
+static void wpa_supplicant_fils_hlp_rx(void *ctx, const u8 *dst, const u8 *src,
+ const u8 *pkt, size_t pkt_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ char *hex;
+ size_t hexlen;
+
+ hexlen = pkt_len * 2 + 1;
+ hex = os_malloc(hexlen);
+ if (!hex)
+ return;
+ wpa_snprintf_hex(hex, hexlen, pkt, pkt_len);
+ wpa_msg(wpa_s, MSG_INFO, FILS_HLP_RX "dst=" MACSTR " src=" MACSTR
+ " frame=%s", MAC2STR(dst), MAC2STR(src), hex);
+ os_free(hex);
+}
+
#endif /* CONFIG_NO_WPA */
@@ -1084,6 +1232,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
#endif /* CONFIG_TDLS */
ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
+ ctx->fils_hlp_rx = wpa_supplicant_fils_hlp_rx;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {
@@ -1105,7 +1254,6 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
if (ssid) {
os_memset(&conf, 0, sizeof(conf));
conf.network_ctx = ssid;
- conf.peerkey_enabled = ssid->peerkey;
conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
#ifdef IEEE8021X_EAPOL
conf.proactive_key_caching = ssid->proactive_key_caching < 0 ?
@@ -1133,6 +1281,11 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_P2P */
conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation;
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(wpa_s->key_mgmt))
+ conf.fils_cache_id =
+ wpa_bss_get_fils_cache_id(wpa_s->current_bss);
+#endif /* CONFIG_FILS */
}
wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
}
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index d6ec8c5090e9..d3d06b8ae231 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -5,7 +5,7 @@
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
-#include <openssl/ssl.h>
+
#include "utils/includes.h"
#include "utils/common.h"
@@ -38,12 +38,24 @@ static int wpas_macsec_deinit(void *priv)
}
+static int wpas_macsec_get_capability(void *priv, enum macsec_cap *cap)
+{
+ return wpa_drv_macsec_get_capability(priv, cap);
+}
+
+
static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled)
{
return wpa_drv_enable_protect_frames(wpa_s, enabled);
}
+static int wpas_enable_encrypt(void *wpa_s, Boolean enabled)
+{
+ return wpa_drv_enable_encrypt(wpa_s, enabled);
+}
+
+
static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window)
{
return wpa_drv_set_replay_protect(wpa_s, enabled, window);
@@ -62,30 +74,21 @@ static int wpas_enable_controlled_port(void *wpa_s, Boolean enabled)
}
-static int wpas_get_receive_lowest_pn(void *wpa_s, u32 channel,
- u8 an, u32 *lowest_pn)
-{
- return wpa_drv_get_receive_lowest_pn(wpa_s, channel, an, lowest_pn);
-}
-
-
-static int wpas_get_transmit_next_pn(void *wpa_s, u32 channel,
- u8 an, u32 *next_pn)
+static int wpas_get_receive_lowest_pn(void *wpa_s, struct receive_sa *sa)
{
- return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn);
+ return wpa_drv_get_receive_lowest_pn(wpa_s, sa);
}
-static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel,
- u8 an, u32 next_pn)
+static int wpas_get_transmit_next_pn(void *wpa_s, struct transmit_sa *sa)
{
- return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn);
+ return wpa_drv_get_transmit_next_pn(wpa_s, sa);
}
-static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel)
+static int wpas_set_transmit_next_pn(void *wpa_s, struct transmit_sa *sa)
{
- return wpa_drv_get_available_receive_sc(wpa_s, channel);
+ return wpa_drv_set_transmit_next_pn(wpa_s, sa);
}
@@ -103,83 +106,79 @@ static unsigned int conf_offset_val(enum confidentiality_offset co)
}
-static int wpas_create_receive_sc(void *wpa_s, u32 channel,
- struct ieee802_1x_mka_sci *sci,
+static int wpas_create_receive_sc(void *wpa_s, struct receive_sc *sc,
enum validate_frames vf,
enum confidentiality_offset co)
{
- return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr,
- be_to_host16(sci->port),
- conf_offset_val(co), vf);
+ return wpa_drv_create_receive_sc(wpa_s, sc, conf_offset_val(co), vf);
}
-static int wpas_delete_receive_sc(void *wpa_s, u32 channel)
+static int wpas_delete_receive_sc(void *wpa_s, struct receive_sc *sc)
{
- return wpa_drv_delete_receive_sc(wpa_s, channel);
+ return wpa_drv_delete_receive_sc(wpa_s, sc);
}
-static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an,
- u32 lowest_pn, const u8 *sak)
+static int wpas_create_receive_sa(void *wpa_s, struct receive_sa *sa)
{
- return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak);
+ return wpa_drv_create_receive_sa(wpa_s, sa);
}
-static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_delete_receive_sa(void *wpa_s, struct receive_sa *sa)
{
- return wpa_drv_enable_receive_sa(wpa_s, channel, an);
+ return wpa_drv_delete_receive_sa(wpa_s, sa);
}
-static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_enable_receive_sa(void *wpa_s, struct receive_sa *sa)
{
- return wpa_drv_disable_receive_sa(wpa_s, channel, an);
+ return wpa_drv_enable_receive_sa(wpa_s, sa);
}
-static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel)
+static int wpas_disable_receive_sa(void *wpa_s, struct receive_sa *sa)
{
- return wpa_drv_get_available_transmit_sc(wpa_s, channel);
+ return wpa_drv_disable_receive_sa(wpa_s, sa);
}
static int
-wpas_create_transmit_sc(void *wpa_s, u32 channel,
- const struct ieee802_1x_mka_sci *sci,
+wpas_create_transmit_sc(void *wpa_s, struct transmit_sc *sc,
enum confidentiality_offset co)
{
- return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr,
- be_to_host16(sci->port),
- conf_offset_val(co));
+ return wpa_drv_create_transmit_sc(wpa_s, sc, conf_offset_val(co));
+}
+
+
+static int wpas_delete_transmit_sc(void *wpa_s, struct transmit_sc *sc)
+{
+ return wpa_drv_delete_transmit_sc(wpa_s, sc);
}
-static int wpas_delete_transmit_sc(void *wpa_s, u32 channel)
+static int wpas_create_transmit_sa(void *wpa_s, struct transmit_sa *sa)
{
- return wpa_drv_delete_transmit_sc(wpa_s, channel);
+ return wpa_drv_create_transmit_sa(wpa_s, sa);
}
-static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an,
- u32 next_pn, Boolean confidentiality,
- const u8 *sak)
+static int wpas_delete_transmit_sa(void *wpa_s, struct transmit_sa *sa)
{
- return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn,
- confidentiality, sak);
+ return wpa_drv_delete_transmit_sa(wpa_s, sa);
}
-static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_enable_transmit_sa(void *wpa_s, struct transmit_sa *sa)
{
- return wpa_drv_enable_transmit_sa(wpa_s, channel, an);
+ return wpa_drv_enable_transmit_sa(wpa_s, sa);
}
-static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_disable_transmit_sa(void *wpa_s, struct transmit_sa *sa)
{
- return wpa_drv_disable_transmit_sa(wpa_s, channel, an);
+ return wpa_drv_disable_transmit_sa(wpa_s, sa);
}
@@ -194,7 +193,14 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
if (!ssid || ssid->macsec_policy == 0)
return 0;
- policy = ssid->macsec_policy == 1 ? SHOULD_SECURE : DO_NOT_SECURE;
+ if (ssid->macsec_policy == 1) {
+ if (ssid->macsec_integ_only == 1)
+ policy = SHOULD_SECURE;
+ else
+ policy = SHOULD_ENCRYPT;
+ } else {
+ policy = DO_NOT_SECURE;
+ }
kay_ctx = os_zalloc(sizeof(*kay_ctx));
if (!kay_ctx)
@@ -204,32 +210,34 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
kay_ctx->macsec_init = wpas_macsec_init;
kay_ctx->macsec_deinit = wpas_macsec_deinit;
+ kay_ctx->macsec_get_capability = wpas_macsec_get_capability;
kay_ctx->enable_protect_frames = wpas_enable_protect_frames;
+ kay_ctx->enable_encrypt = wpas_enable_encrypt;
kay_ctx->set_replay_protect = wpas_set_replay_protect;
kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite;
kay_ctx->enable_controlled_port = wpas_enable_controlled_port;
kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn;
kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn;
- kay_ctx->get_available_receive_sc = wpas_get_available_receive_sc;
kay_ctx->create_receive_sc = wpas_create_receive_sc;
kay_ctx->delete_receive_sc = wpas_delete_receive_sc;
kay_ctx->create_receive_sa = wpas_create_receive_sa;
+ kay_ctx->delete_receive_sa = wpas_delete_receive_sa;
kay_ctx->enable_receive_sa = wpas_enable_receive_sa;
kay_ctx->disable_receive_sa = wpas_disable_receive_sa;
- kay_ctx->get_available_transmit_sc = wpas_get_available_transmit_sc;
kay_ctx->create_transmit_sc = wpas_create_transmit_sc;
kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc;
kay_ctx->create_transmit_sa = wpas_create_transmit_sa;
+ kay_ctx->delete_transmit_sa = wpas_delete_transmit_sa;
kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa;
kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa;
- res = ieee802_1x_kay_init(kay_ctx, policy, wpa_s->ifname,
+ res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_port,
+ ssid->mka_priority, wpa_s->ifname,
wpa_s->own_addr);
- if (res == NULL) {
- os_free(kay_ctx);
+ /* ieee802_1x_kay_init() frees kay_ctx on failure */
+ if (res == NULL)
return -1;
- }
wpa_s->kay = res;
@@ -260,7 +268,7 @@ static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s,
return -1;
}
- need_len = 1 + 2 * SSL3_RANDOM_SIZE;
+ need_len = 1 + 2 * 32 /* random size */;
if (need_len > id_len) {
wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
return -1;
@@ -377,3 +385,49 @@ fail:
return res;
}
+
+
+void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct mka_key *cak;
+ struct mka_key_name *ckn;
+ void *res = NULL;
+
+ if ((ssid->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET)
+ goto end;
+
+ ckn = os_zalloc(sizeof(*ckn));
+ if (!ckn)
+ goto end;
+
+ cak = os_zalloc(sizeof(*cak));
+ if (!cak)
+ goto free_ckn;
+
+ if (ieee802_1x_alloc_kay_sm(wpa_s, ssid) < 0 || !wpa_s->kay)
+ goto free_cak;
+
+ if (wpa_s->kay->policy == DO_NOT_SECURE)
+ goto dealloc;
+
+ cak->len = MACSEC_CAK_LEN;
+ os_memcpy(cak->key, ssid->mka_cak, cak->len);
+
+ ckn->len = MACSEC_CKN_LEN;
+ os_memcpy(ckn->name, ssid->mka_ckn, ckn->len);
+
+ res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, PSK, FALSE);
+ if (res)
+ goto free_cak;
+
+dealloc:
+ /* Failed to create MKA */
+ ieee802_1x_dealloc_kay_sm(wpa_s);
+free_cak:
+ os_free(cak);
+free_ckn:
+ os_free(ckn);
+end:
+ return res;
+}
diff --git a/wpa_supplicant/wpas_kay.h b/wpa_supplicant/wpas_kay.h
index b7236d0776c4..81f8e0ce329e 100644
--- a/wpa_supplicant/wpas_kay.h
+++ b/wpa_supplicant/wpas_kay.h
@@ -17,6 +17,9 @@ void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
const u8 *peer_addr);
void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s);
+void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+
#else /* CONFIG_MACSEC */
static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
@@ -36,6 +39,13 @@ static inline void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
{
}
+static inline void *
+ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
#endif /* CONFIG_MACSEC */
#endif /* WPAS_KAY_H */
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 74a420c671d0..1a2677b8eea4 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -203,6 +203,9 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
if (ssid->ssid == NULL)
return;
bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
+ if (!bss)
+ bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+ ssid->ssid, ssid->ssid_len);
if (bss == NULL) {
wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
"table - use credential as-is");
@@ -490,6 +493,16 @@ static int wpa_supplicant_wps_cred(void *ctx,
ssid->pairwise_cipher |= WPA_CIPHER_GCMP;
ssid->group_cipher |= WPA_CIPHER_GCMP;
}
+ if (wpa_s->drv_capa_known &&
+ (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP_256)) {
+ ssid->pairwise_cipher |= WPA_CIPHER_GCMP_256;
+ ssid->group_cipher |= WPA_CIPHER_GCMP_256;
+ }
+ if (wpa_s->drv_capa_known &&
+ (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP_256)) {
+ ssid->pairwise_cipher |= WPA_CIPHER_CCMP_256;
+ ssid->group_cipher |= WPA_CIPHER_CCMP_256;
+ }
break;
}
@@ -1027,10 +1040,9 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
continue;
os_free(ssid->ssid);
- ssid->ssid = os_malloc(bss->ssid_len);
+ ssid->ssid = os_memdup(bss->ssid, bss->ssid_len);
if (ssid->ssid == NULL)
break;
- os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
ssid->ssid_len = bss->ssid_len;
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
"scan results",
@@ -1169,6 +1181,7 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
return -1;
if (wpa_s->wps_fragment_size)
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
+ wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
@@ -1481,6 +1494,9 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
wpa_s->global->ifaces->wps->uuid,
WPS_UUID_LEN);
src = "from the first interface";
+ } else if (wpa_s->conf->auto_uuid == 1) {
+ uuid_random(wps->uuid);
+ src = "based on random data";
} else {
uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
src = "based on MAC address";