[wpa_supplicant] Cumulative patch from commit 257b119c2
Bug: 265294868
Test: Verify Passpoint ANQP functionality and Passpoint association
Test: Connect to Passpoint, Open, WPA2, WPA3 networks and run traffic
Test: Regression test b/218404053 (Bug: TBD)
BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from Open sourcie
257b119c2 QCA vendor attribute of update roaming cached statistics info
18436f393 Enhance QCA vendor interface for Concurrent AP Policy for XR
58fba11e1 Enhance QCA vendor interface with new hang reason codes
0f3f9cdca dpp-nfc: Try to request with alternative URL in additional cases
8b36248cd Add QCA vendor command to get the monitor mode status
0dd8bcef8 QCA vendor attributes for MLO and EHT capabilities
e5602989c QCA vendor attributes to configure EHT capabilities
d9d5e55c5 DPP: Respond to GAS on the same channel it was received on
651c9e957 Add new status code strings
3a2d27552 Make MFPR value from an associated STA available as hostapdMFPR
546debd5e Force MFPR=1 to be used on the 6 GHz band
f9c6ab834 P2P: Support preferred GO band based optimization for scanning
093bedc05 P2P: Allow persistent group join retry limit to be configured via D-Bus
8717110db Do not flush PMKSA cache on restoring dedicated per-ESS MAC address
1d4027fdb Make random MAC address style parameters use common enum values
681856c35 Check both sec and usec values to see if MAC address was changed
bdbb6e003 wpa_supplicant: Handle MAC address randomization changes for same ESS
4bd1efe07 dbus: Do not bring down primary interface when removing created AP interface
f4096e7cd EHT: Update EHT Operation element to P802.11be/D2.3 in AP settings
e869fdfee wpa_supplicant: Use MLD address in SAE authentication
8c0f83ae8 SME: Accept Authentication frame from an MLD AP
23039f5e4 SME: Add support for handling association with MLD
8f89661df SME: Add support for handling authentication with MLD
694a1c687 SAE: Make sme_sae_auth() return IE offset
870edfd67 WPA3: Update transition disable bitmap based on port authorized event
8fdf3c447 Sync with wireless-next.git include/uapi/linux/nl80211.h
f9804e306 nl80211: Enforce unique address for AP iftype
87bad8afa dbus: Pass in MAC address in CreateInterface method
b0722cf75 dbus: Fix a memory leak on error path in CreateInterface method
a7f6b8518 crypto: Check if crypto_bignum_to_bin() is successful
2749a2c6b nl80211: Actually get and store TX retries
998aeca3c crypto: Clear secrets from stack in hmac_sha256_vector()
909864ab1 HS 2.0: Restore ifdef for DEFINE_STACK_OF in est.c
af0ab435a PASN: Use the assigned status code from IEEE P802.11az/D7.0
3d798ff2a PASN: Align RSNXE with IEEE P802.11az/D7.0 definitions
ab2cb379d Define all assigned BSS membership selector values
ed0a7b480 wpa_supplicant: Implement HE membership selector check
054fcfab6 hostapd: Add require_he configuration
c46351d10 DFS: Clear cac_started when AP is disabled
3df42cf3c EHT: Use HE operating channel width in MCS length calculation
23e31eb68 SAE: Support cross AKM roaming between SAE AKMs in external auth case
a17026707 PASN: Avoid clearing secure context for the PASN deauthentication event
75a9c4bd4 Add new attributes in SCS rule config QCA vendor subcommand
7d8b96dcf wpa_supplicant: Apply same restrictions for MLD as for 6 GHz BSS
7216f79b9 nl80211: Support get_sta_mlo_info for SME-in-wpa_supplicant drivers
06eb608d5 nl80211: Handle scan results with MLD connection
033a57d26 nl80211: Get MLO support capability
32b745448 wpa_supplicant: Make valid_links u16
a2c4c0b1b nl80211: Support MLD association request
a134b4dc5 nl80211: Add support for MLD authentication
e3e68668c ctrl_iface: Report RNR and ML in BSS command
5f17763ad common: Combine definitions for Multi-Link and per STA profile control
7a7ce9574 dbus: Emit more information over D-Bus
ad4fa5dd3 Add more nl80211 info to struct wpa_signal_info
090f0f8c7 mbssid: Indicate MBSSID information in RNR
a1c4adda1 mbssid: Add nl80211 support
54b1352ef mbssid: Make the AID space shared
10749c3c4 mbssid: Process Known BSSID element
15690faad mbssid: Add MBSSID Configuration element
fc2e4bac5 mbssid: Set extended capabilities
a004bf2cd mbssid: Configure parameters and element data
c5a09b051 mbssid: Add Non-Inheritance element
920b56322 mbssid: Functions for building Multiple BSSID elements
931e5d4f9 mbssid: Configure all BSSes before beacon setup
78d0b9899 mbssid: Retrieve driver capabilities
7452e5447 mbssid: Add new configuration option
bb67d5b52 AP: Add testing option to delay EAPOL Tx
1897abad9 dbus: Add D-Bus property for current MAC address
1a800a940 EAP-TEAP server: Allow tunneled EAP method sequence to be optimized
f791b5bbc EAP-TEAP peer: Process Crypto-Binding TLV before EAP Payload TLV
5a9bd8a06 EAP-TEAP: Use EAP-FAST-MSCHAPv2 in the tunnel
364b6500b EAP-FAST: Move EAP-MSCHAPv2 special MSK handling into MSCHAPv2
81dedfbd7 nl80211: Increase the scan frequencies buffer
9a2781f24 wpa_supplicant: Support throughput estimation for EHT rates
755aaeb97 wpa_supplicant: Add missing memory allocation checks
69725c4cf OpenSSL: Fix BN_rshift() argument order
e9b4ad236 OpenSSL: Apply connection flags before reading certificates
bbd5a4689 SAE: Add an enum for defining sae_pwe parameter values
3a0edb2cd SAE: Enable H2E for 6 GHz BSS
20bfd4feb AP: Enable H2E on 6 GHz when SAE is used
b43e19f3f WPS: Cross band overlap detection with multiple interfaces
e2d88f86e DPP: Expose own and peer bootstrap info ids on authentication success
043dedee8 DPP: Expose enrollee pubkey hash for identification
2d8974e31 DPP: Move DPP_EVENT_AUTH_SUCCESS to a helper
d8d2b3a33 Implement read-only mode for SSIDs from the additional config (-I)
4cb23b66d ACS: Allow selecting a better channel when using 40/80/160 MHz
472101684 ACS: introduce acs_adjust_secondary
60e2934cb ACS: Introduce acs_get_bw_center_chan()
ed8e13dec ACS: Extract bw40/80/160 freqs out of acs_usable_bwXXX_chan()
9025def55 wpa_supplicant: Add support for pregenerated MAC
5da3e1ca4 mesh: Do not allow open mode key in 6 GHz
50a9b7d3d P2P: Include only 6 GHz PSCs in full scan
0d6cd88ee DPP: Use existing TCP connection to replay duplicate Presence Announcement
6af717f73 DPP: Don't close TCP connection for duplicate Presence Announcements
46e6b72b7 Add a callback to notify added PMKSA cache entry details
af1528a12 hostapd: Add RELOAD_BSS
bc2b88b25 hostapd: Add config_id to GET_CONFIG output
b37c3fbad hostapd: Add config_id parameter
46f6a3277 Split BSS-specific hostapd_clear_old_bss() from hostapd_clear_old()
2afb9b1a5 dbus: Add dbus notify when wpa_s->key_mgmt changes
98e9d553f nl80211: Check previous MAC address for locally-generated-deauth
87ffa1bec wpa_supplicant: Convert SSID into printable form before printing
416386060 Mark authorization completed on driver indication during 4-way HS offload
da2ec9459 D-Bus: Split set_cred_properties() into two functions
f5ce680ee D-Bus: Hotspot 2.0 credentials with multiple domains
2f739c71c ctrl: Fix compilation with UDP control interface
6d4548187 RSN: Split EAPOL-Key msg 3/4 processing for WPA(v1)
5b7957b7e RSN: Split EAPOL-Key msg 1/4 processing for WPA(v1)
e5dfce38f RSN: Split EAPOL-Key group msg 1/2 processing more completely for WPA(v1)
5ab43c738 RSN: Split WPA(v1) processing of EAPOL-Key frames into a separate function
f7fd891c7 Fix a typo in driver ops poll() documentation
3268ec0ac HS20: Use required_home_ois in hs20-osu-client
58eb905ad HS20: Support credentials with multiple home OIs
0143dc1cb OpenSSL: Load OpenSSL 3.0 legacy provider but let default be loaded
fef4c6cb0 OpenSSL: Don't provide implementation of DES/RC4 for FIPS builds
1d42dafce RSN: Do not include RC4 use in FIPS builds
df5ae2aad Add more detailed description of RADIUS attributes in EAP user file
0ba266d86 dbus: Add virtual interface create/remove logic to be inline with ctrl_iface
5102d7411 wpa_passphrase: Disable terminal echo when reading from stdin
86ab28217 PASN: Fix passing own address and peer address to pasn_deauthenticate()
a9062432e wpa_cli: Fix PASN control interface commands
b6d3fd05e FT: Use SHA256 to derive PMKID for AKM 00-0F-AC:3 (FT-EAP)
ef70f814a Add a new QCA vendor attribute to configure wifi calling (wfc) state
05ec48568 WPS: Pick WPS AP based on latest received WPS IE
ca4fa867d Enable PMF automatically if OCV is enabled
c823197bd SAE: Use Challenge Failure status code in confirm message failure cases
65c8633d9 Allow a lower priority BSS to be tried after network disabling
e91ac53d5 DFS: Do not allow channel checks to go beyond the channel list
f96dfdeef PASN: Fix missing libraries for libpasn.so on Android
91d148f50 PASN: Fix is_pasn_auth_frame() for mgmt tx status frames
b6c38cee9 Skip CAC if the driver switches channel to non-DFS
080afc03d Add hostapd control interface command to stop logging to file
0fd13c90e Add QCA vendor interface for AP doze mode configuration
4e1f55a11 Roam control configuration for 6 GHz in full scan only on prior discovery
34d93b0c9 HS 2.0: Deauthenticate STA on deauth-imminent more quickly if no URL
2e40f969b nl80211: Fix wrong requested links bitmap in sta_mlo_info.req_links
b6e226496 MLD STA: Fix IGTK and BIGTK MLO KDEs validation
2050130be Add a vendor attribute for roam control configuration for full scan
12f16c27b TLS: Fix unsigned int underflow in internal TLS 1.0/1.1 implementation
802b67bce Update tls_connection_set_verify() documentation to verify_peer=2
0202b9774 DPP: Fix memory leak of intro.peer_key in station handling
f723f7f8a P2P: Check dev pointer consistently when building PD Response
30403e965 WPS: Check NDEF record length fields separately
cd0e8653a TDLS: Use stored FTE length in MIC calculation
7e85e24f3 TDLS: Use stored peer RSNE length in MIC calculation
40a42613e FT: Simplify FTE parsing for FT-SAE-EXT-KEY using MIC Length subfield
5ea7a2f54 DPP: Drop PMKSA entry if AP reject association due to invalid PMKID
4840b45a2 Fix empty pmksa_cache_get()
3abd0c471 SAE: Print rejection of peer element clearly in debug log
9ff778fa4 Check for own address (SPA) match when finding PMKSA entries
9f04a9c8d Store own MAC address (SPA) in supplicant PMKSA cache entries
309765eb6 PASN: Use separate variables for BSSID and peer address
42f0c44d8 PASN: Use peer address instead of BSSID as the destination for initiator
15583802b nl80211: Allow up to 64-byte PMK in NL80211_CMD_SET_PMKSA
bbe5f0c1e FT: Do not try to use FT protocol between mobility domains
b92f61885 Don't use default RSNE/RSNXE when the driver indicates cross SSID roaming
d7febe33f MLO: Remove unnecessary debug prints about clearing AP RSNE/RSNXE
16d913bfd Define AFC vendor commands and events
46f5cf928 OpenSSL: Fix additional HPKE corner cases
bdc35acd5 SAE: Allow loading of the password from an external database
48dd8994a Fix external passwords with 4-way handshake offloading
e5a7c852c systemd: Use interface name in description of interface-specific units
a0628f8a5 OpenSSL: Remove unused assignment from HPKE expand
3e1a04afa nl80211: Check that attribute addition succeeds in offloaded PASN case
0658a22ef GAS: Try to make buffer length determination easier for static analyzers
271ce71c7 FT: Fix PMK-R0 derivation for FT-SAE-EXT-KEY with SHA512
2f61d703a MLD STA: Group key handshake processing for GTK/IGTK/BIGTK rekeying
f0760aa6d MLD STA: Use AP MLD address as destination for 4-way handshake EAPOL-Key frames
8f2e493be MLD STA: Validation of MLO KDEs for 4-way handshake EAPOL-Key frames
f15cc834c MLD STA: Processing of EAPOL-Key msg 3/4 frame when using MLO
08512e5f3 MLD STA: Extend key configuration functions to support Link ID
a4adb2f3e MLD STA: Configure TK to the driver using AP MLD address
fa5cad61a MLD STA: Use AP MLD address in PMKSA entry
052bf8a51 MLD STA: Use AP MLD address to derive pairwise keys
e78437256 MLD STA: Add MLO KDEs for EAPOL-Key msg 2/4 and 4/4
472a0b8d6 MLD STA: Set MLO connection info to wpa_sm
cc2236299 nl80211: Get all requested MLO links information from (re)association events
1ca5c2ec2 PASN: Fix spelling of RSNE in debug messages
a43536a72 PASN: Verify explicitly that elements are present before parsing
7e3852407 PASN: Fix MIC check not to modify const data
8481c7509 PASN: Fix Authentication frame checks
f899d7f37 dbus: Apply PMK properties immediately
c6f8af507 Add option to disable SAE key_mgmt without PMF
7ad757ec0 Document crypto_ec_key_get_subject_public_key() to use compressed format
6527a7656 DPP: Stop listen mode for chirp-initiated Authentication exchange
2e7339442 P2P: Discount current operating frequency when scanning new connection
00a762c26 Do not drop connection attempt when reconnecting to the same ESS
368de263b P2P: Skip Extended Listen timeout to allow scans during group formation
cff55f348 P2P: Clone sae_pwe config to new group interface config
ae517789f P2P: Allow PSC channel to be used for 6 GHz BW40
9c830d917 P2P: Track peer 6 GHz capability more robustly
1ca403a8b Add QCA vendor subcommand to notify about primary netdev
70d89f90e A vendor roam control configuration for delaying hand off for RX
fff81a468 PASN: Change pasn_use_384() to be a non-static function
ea241cbe9 PASN: Rename struct wpas_pasn to pasn_data
6be84343a PASN: Add pairing verification wrapper function for Wi-Fi Aware
325236948 PASN: Mark wpas_pasn_start() comeback argument const
b1ed44b6a PASN: Allow extra elements to be added into PASN Authentication frames
08abcdf4e PASN: Makefile and Android.mk changes for libpasn.so
78c5bb7f5 PASN: Move responder functionality into a separate file
c7edfce79 PASN: Move initiator changes into a separate file
975b7a02c Move SAE comeback token functionality into a separate file
1711fe912 PASN: Compute MIC from RSNE and RSNXE of the frame for Wi-Fi Aware
6f80014b1 PASN: Allow custom PMKID in Authentication frames for Wi-Fi Aware
e99047da2 PASN: Add a handler func to send mgmt frames to the driver from AP
4022ffc5d PASN: Store AKMP in the PTKSA cache
c55eadede PASN: Remove hapd dependency in processing PASN Authentication frames
6dc833bc5 PASN: Remove hapd dependency for PASN and SAE comeback
1861f5716 PASN: Remove hapd dependency for pasn_derive_keys()
1fa266e99 PASN: Remove hapd dependency for SAE and FILS wrapped data
bc9fbe1b2 PASN: Common wpas_pasn structure for initiator and responder
14b5ebce7 PASN: Add a common header file for initiator and responder
af5eec3b3 PASN: Function handler to transmit Authentication frames
629bbc91b PASN: Remove dependency of wpa_ssid for initiator
e7f45ca11 PASN: Remove wpa_s dependency for wpas_pasn_start()
0be131265 PASN: Remove wpa_s dependency for wpas_pasn_build_auth_1()
086ccdc59 PASN: Remove wpa_s dependency for the functions processing RX frames
e2e87b90b PASN: Remove wpa_s dependency for wpas_pasn_auth_tx_status()
de4b73a36 PASN: Remove wpa_s dependency for FILS wrapped data
90bb73c51 PASN: Remove wpa_sm dependency to add an entry to PMKSA cache
5313e5a79 PASN: Remove unused wpa_s parameter for wpas_pasn_sae_setup_pt()
f636cbd04 PASN: Remove wpa_s dependency for SAE wrapped data functions
5535fbcfa PASN: Add wpa_pasn_reset() to eliminate need for struct wpa_supplicant
10e455c44 Enable use of PMKSA caching independent of RSN supplicant state machine
1d0ee1908 Fix the vendor ID assignment for configuring periodic sounding
b17b86da4 QCA vendor attribute to configure periodic sounding
6f3efa21c MLD STA: Fix crash caused by NULL wpa_s->current_ssid
ef5a9a009 nl80211: Fix parsing PASN peer and src addresses from vendor nl attributes
2c55c9273 More debug prints for EAPOL-Key message generation (Authenticator)
90cef4f21 MLD STA: Fully clear MLO info to avoid use of uninitialized members
496a1ced1 MLD STA: Use MLD addresses for sending non-Public Action frames
17ae98873 MLD STA: Support processing of M1 received before association event
9dafad1ea EHT: Definitions for STA Control fields of Basic Multi-Link element
1fbea7d43 EHT: Multi-Link element defragmentation
ec03b71ee common: Refactor element defragmentation
347ea8f0a EHT: Parse Multi-Link elements
73f540b6a MLD STA: Fetch MLO association Link ID info to core wpa_supplicant
ee46b7d6d nl80211: Check MLO link status info in NL80211_CMD_CONNECT
6e015cd45 RADIUS: Add Filter-Id attribute
06800f612 Add QCA vendor attributes for EHT support in external ACS
042368663 Vendor attribute to configure QoS/AC upgrade for UDP frames
e5d15e225 EHT: Allow EHT to be disabled using disable_eht=1 in wpa_supplicant
041f6cea9 SAE: Accept FT and -EXT-KEY AKMs for external auth
b72922796 P2P: Get cached scan results on iface creation to avoid scan
8392ea9e7 SAE: Fix AKM suite selector check for external authentication
ebe6a7c94 FT: Cover variable length KCK in function documentation
eda4ba081 FT: Reassociation Response frame validation for FT-SAE-EXT-KEY
0f7253d35 FT: Response processing for FT-SAE-EXT-KEY
a1eb1bb0e FT: Supplicant side FTE generation for FT-SAE-EXT-KEY
883e33594 FT: Authentication request frame processing for FT-SAE-EXT-KEY
879363bbc FT: Reassociation Request frame parsing for FT-SAE-EXT-KEY
e8f23c948 FT: Association Response frame FTE generation for FT-SAE-EXT-KEY
a76a314c1 FT: Extend PMK-R0 derivation for FT-SAE-EXT-KEY
79cd846b2 FT: Extend PTK derivation for FT-SAE-EXT-KEY
39b60f334 FT: Extend PMK-R1 derivation for FT-SAE-EXT-KEY
fb4fc704c FT: Debug print FTE subelements during parsing
25b52e5f8 FT: Extend FTE parsing for FT-SAE-EXT-KEY
4f58afee9 FT: Extend MIC derivation for FT-SAE-EXT-KEY
dcd46edf5 FT: Extend PMKR1Name derivation for FT-SAE-EXT-KEY
9fd245564 FT: Support longer SAE PMK for FT in INITPSK AP
c41bd98be FT: AP mode FTE writing to support FT-SAE-KEY-EXT
efa0f51d3 FT: Accept 512-bit PMK-R1 from RRB
eb0821c90 Add service class id attribute in SCS rule config vendor subcommand
5607abe2e WNM: Print unsupported neighbor report subelements in debug log
fcdd76fa2 Interworking: Print unsupported inner EAP-TTLS method in debug log
f8a05de66 Move default action from after switch to within
7614fcebe ACS: Filter out 6 GHz channels if HE or EHT is not enabled
1864664ca Android: Delay QCA roam+auth event until NL80211_CMD_ROAM is received
5f3cdc064 Override ieee80211w from pmf for AP mode in wpa_supplicant
2b972a35b DPP: Require PMF when profile is for SAE without PSK
8219d2b7d PASN: Fix CONFIG_PASN=y build without CONFIG_IEEE80211R=y
Change-Id: Ifd6be0d096df54c13162fdda164cd8e804a51692
Merged-In: Ifd6be0d096df54c13162fdda164cd8e804a51692
(cherry picked from commit 38ad1edd94f7b4f3c3937dd21a9b7ef52140babf)
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index 0c5d96b..e4b3718 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -330,6 +330,8 @@
"src/utils/json.c",
"src/ap/wmm.c",
"src/ap/ap_list.c",
+ "src/ap/comeback_token.c",
+ "src/pasn/pasn_responder.c",
"src/ap/ieee802_11.c",
"src/ap/hw_features.c",
"src/ap/dfs.c",
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 14a7464..7a1b612 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -1056,6 +1056,8 @@
ifdef NEED_AP_MLME
OBJS += src/ap/wmm.c
OBJS += src/ap/ap_list.c
+OBJS += src/ap/comeback_token.c
+OBJS += src/pasn/pasn_responder.c
OBJS += src/ap/ieee802_11.c
OBJS += src/ap/hw_features.c
OBJS += src/ap/dfs.c
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 88a2e18..a2adc85 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -1186,6 +1186,8 @@
ifdef NEED_AP_MLME
OBJS += ../src/ap/wmm.o
OBJS += ../src/ap/ap_list.o
+OBJS += ../src/ap/comeback_token.o
+OBJS += ../src/pasn/pasn_responder.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 24fd297..d41a71a 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2418,6 +2418,9 @@
bss->ap_max_inactivity = atoi(pos);
} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
bss->skip_inactivity_poll = atoi(pos);
+ } else if (os_strcmp(buf, "config_id") == 0) {
+ os_free(bss->config_id);
+ bss->config_id = os_strdup(pos);
} else if (os_strcmp(buf, "country_code") == 0) {
if (pos[0] < 'A' || pos[0] > 'Z' ||
pos[1] < 'A' || pos[1] > 'Z') {
@@ -2582,6 +2585,8 @@
bss->eap_teap_separate_result = atoi(pos);
} else if (os_strcmp(buf, "eap_teap_id") == 0) {
bss->eap_teap_id = atoi(pos);
+ } else if (os_strcmp(buf, "eap_teap_method_sequence") == 0) {
+ bss->eap_teap_method_sequence = atoi(pos);
#endif /* EAP_SERVER_TEAP */
#ifdef EAP_SERVER_SIM
} else if (os_strcmp(buf, "eap_sim_db") == 0) {
@@ -3476,6 +3481,8 @@
#ifdef CONFIG_IEEE80211AX
} else if (os_strcmp(buf, "ieee80211ax") == 0) {
conf->ieee80211ax = atoi(pos);
+ } else if (os_strcmp(buf, "require_he") == 0) {
+ conf->require_he = 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) {
@@ -3633,6 +3640,15 @@
return 1;
}
bss->unsol_bcast_probe_resp_interval = val;
+ } else if (os_strcmp(buf, "mbssid") == 0) {
+ int mbssid = atoi(pos);
+ if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid mbssid (%d): '%s'.",
+ line, mbssid, pos);
+ return 1;
+ }
+ conf->mbssid = mbssid;
#endif /* CONFIG_IEEE80211AX */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
@@ -4270,6 +4286,8 @@
conf->enable_eapol_large_timeout = atoi(pos);
} else if (os_strcmp(buf, "eap_skip_prot_success") == 0) {
bss->eap_skip_prot_success = atoi(pos);
+ } else if (os_strcmp(buf, "delay_eapol_tx") == 0) {
+ conf->delay_eapol_tx = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_SAE
} else if (os_strcmp(buf, "sae_password") == 0) {
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 7fb3fd3..94f31ee 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -983,6 +983,14 @@
return pos - buf;
pos += ret;
+ if ((hapd->conf->config_id)) {
+ ret = os_snprintf(pos, end - pos, "config_id=%s\n",
+ hapd->conf->config_id);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
#ifdef CONFIG_WPS
ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
hapd->conf->wps_state == 0 ? "disabled" :
@@ -1380,6 +1388,16 @@
}
+static int hostapd_ctrl_iface_reload_bss(struct hostapd_data *bss)
+{
+ if (hostapd_reload_bss_only(bss) < 0) {
+ wpa_printf(MSG_ERROR, "Reloading of BSS failed");
+ return -1;
+ }
+ return 0;
+}
+
+
static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
{
if (hostapd_disable_iface(iface) < 0) {
@@ -3199,6 +3217,8 @@
} else if (os_strncmp(buf, "RELOG", 5) == 0) {
if (wpa_debug_reopen_file() < 0)
reply_len = -1;
+ } else if (os_strcmp(buf, "CLOSE_LOG") == 0) {
+ wpa_debug_stop_log();
} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
} else if (os_strcmp(buf, "STATUS") == 0) {
@@ -3370,6 +3390,9 @@
} else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) {
if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
reply_len = -1;
+ } else if (os_strcmp(buf, "RELOAD_BSS") == 0) {
+ if (hostapd_ctrl_iface_reload_bss(hapd))
+ reply_len = -1;
} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
if (hostapd_ctrl_iface_reload(hapd->iface))
reply_len = -1;
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index ea67aa1..c5e74a6 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -812,6 +812,9 @@
# 1 = enabled
#ieee80211ax=1
+# Require stations to support HE PHY (reject association if they do not)
+#require_he=1
+
# disable_11ax: Boolean (0/1) to disable HE for a specific BSS
#disable_11ax=0
@@ -1412,6 +1415,12 @@
# 5 = require both user and machine identity
#eap_teap_id=0
+# EAP-TEAP tunneled EAP method behavior
+# 0 = minimize roundtrips by merging start of the next EAP method with the
+# crypto-binding of the previous one.
+# 1 = complete crypto-binding before starting the next EAP method
+#eap_teap_method_sequence=0
+
# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
# (default: 0 = disabled).
#eap_sim_aka_result_ind=1
@@ -2799,7 +2808,12 @@
# If the RADIUS server indicates that the station is not allowed to connect to
# the BSS/ESS, the AP can allow the station some time to download a
# notification page (URL included in the message). This parameter sets that
-# timeout in seconds.
+# timeout in seconds. If the RADIUS server provides no URL, this value is
+# reduced to two seconds with an additional trigger for immediate
+# deauthentication when the STA acknowledges reception of the deauthentication
+# imminent indication. Note that setting this value to 0 will prevent delivery
+# of the notification to the STA, so a value of at least 1 should be used here
+# for normal use cases.
#hs20_deauth_req_timeout=60
# Operator Friendly Name
@@ -3075,6 +3089,11 @@
# Include only ECSA IE without CSA IE where possible
# (channel switch operating class is needed)
#ecsa_ie_only=0
+#
+# Delay EAPOL-Key messages 1/4 and 3/4 by not sending the frame until the last
+# attempt (wpa_pairwise_update_count). This will trigger a timeout on all
+# previous attempts and thus delays the frame. (testing only)
+#delay_eapol_tx=0
##### Multiple BSSID support ##################################################
#
@@ -3118,3 +3137,63 @@
#bss=wlan0_1
#bssid=00:13:10:95:fe:0b
# ...
+#
+# Multiple BSSID Advertisement in IEEE 802.11ax
+# IEEE Std 802.11ax-2021 added a feature where instead of multiple interfaces
+# on a common radio transmitting individual Beacon frames, those interfaces can
+# form a set with a common Beacon frame transmitted for all. The interface
+# which is brought up first is called the transmitting profile of the MBSSID
+# set which transmits the Beacon frames. The remaining interfaces are called
+# the non-transmitting profiles and these are advertised inside the Multiple
+# BSSID element in the Beacon and Probe Response frames from the first
+# interface.
+#
+# The transmitting interface is visible to all stations in the vicinity, however
+# the stations that do not support parsing of the Multiple BSSID element will
+# not be able to connect to the non-transmitting interfaces.
+#
+# Enhanced Multiple BSSID Advertisements (EMA)
+# When enabled, the non-transmitting interfaces are split into multiple
+# Beacon frames. The number of Beacon frames required to cover all the
+# non-transmitting profiles is called the profile periodicity.
+#
+# Refer to IEEE Std 802.11-2020 for details regarding the procedure and
+# required MAC address assignment.
+#
+# Following configuration is per radio.
+# 0 = Disabled (default)
+# 1 = Multiple BSSID advertisement enabled.
+# 2 = Enhanced multiple BSSID advertisement enabled.
+#mbssid=0
+#
+# The transmitting interface should be added with the 'interface' option while
+# the non-transmitting interfaces should be added using the 'bss' option.
+# Security configuration should be added separately per interface, if required.
+#
+# Example:
+#mbssid=2
+#interface=wlan2
+#ctrl_interface=/var/run/hostapd
+#wpa_passphrase=0123456789
+#ieee80211w=2
+#sae_pwe=1
+#auth_algs=1
+#wpa=2
+#wpa_pairwise=CCMP
+#ssid=<SSID-0>
+#bridge=br-lan
+#wpa_key_mgmt=SAE
+#bssid=00:03:7f:12:84:84
+#
+#bss=wlan2-1
+#ctrl_interface=/var/run/hostapd
+#wpa_passphrase=0123456789
+#ieee80211w=2
+#sae_pwe=1
+#auth_algs=1
+#wpa=2
+#wpa_pairwise=CCMP
+#ssid=<SSID-1>
+#bridge=br-lan
+#wpa_key_mgmt=SAE
+#bssid=00:03:7f:12:84:85
diff --git a/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user
index 00edc95..61ef937 100644
--- a/hostapd/hostapd.eap_user
+++ b/hostapd/hostapd.eap_user
@@ -52,8 +52,8 @@
# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly
# to the way radius_auth_req_attr is used for Access-Request packet in
# hostapd.conf. For EAP server, this is configured separately for each user
-# entry with radius_accept_attr=<value> line(s) following the main user entry
-# line.
+# entry with radius_accept_attr=<attr_id>[:<syntax:value>] line(s) following
+# the main user entry line.
# Phase 1 users
"user" MD5 "password"
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 6376b78..ec40dda 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -252,6 +252,13 @@
}
+static int hostapd_cli_cmd_close_log(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "CLOSE_LOG");
+}
+
+
static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
@@ -1208,6 +1215,13 @@
}
+static int hostapd_cli_cmd_reload_bss(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RELOAD_BSS");
+}
+
+
static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1572,6 +1586,8 @@
"= get MIB variables (dot1x, dot11, radius)" },
{ "relog", hostapd_cli_cmd_relog, NULL,
"= reload/truncate debug log output file" },
+ { "close_log", hostapd_cli_cmd_close_log, NULL,
+ "= disable debug log output file" },
{ "status", hostapd_cli_cmd_status, NULL,
"= show interface status info" },
{ "sta", hostapd_cli_cmd_sta, hostapd_complete_stations,
@@ -1670,6 +1686,8 @@
"= enable hostapd on current interface" },
{ "reload", hostapd_cli_cmd_reload, NULL,
"= reload configuration for current interface" },
+ { "reload_bss", hostapd_cli_cmd_reload_bss, NULL,
+ "= reload configuration for current BSS" },
{ "disable", hostapd_cli_cmd_disable, NULL,
"= disable hostapd on current interface" },
{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
diff --git a/hostapd/main.c b/hostapd/main.c
index 822403f..eb1e861 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -243,6 +243,9 @@
wpa_printf(MSG_ERROR, "set_wowlan failed");
}
os_free(triggs);
+
+ iface->mbssid_max_interfaces = capa.mbssid_max_interfaces;
+ iface->ema_max_periodicity = capa.ema_max_periodicity;
}
return 0;
diff --git a/hs20/client/est.c b/hs20/client/est.c
index c3f27e1..5c6e2f6 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -218,7 +218,9 @@
} d;
} AttrOrOID;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
DEFINE_STACK_OF(AttrOrOID)
+#endif
typedef struct {
int type;
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 01e7b75..2ca85f9 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -1231,12 +1231,13 @@
homeoi, required);
if (required) {
- if (set_cred(ctx->ifname, id, "required_roaming_consortium",
- homeoi) < 0)
- wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
+ if (set_cred_quoted(ctx->ifname, id, "required_home_ois",
+ homeoi) < 0)
+ wpa_printf(MSG_INFO,
+ "Failed to set cred required_home_ois");
} else {
- if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0)
- wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
+ if (set_cred_quoted(ctx->ifname, id, "home_ois", homeoi) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred home_ois");
}
xml_node_get_text_free(ctx->xml, homeoi);
diff --git a/src/Makefile b/src/Makefile
index 6eb7f2a..d15cf32 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae pasn radius rsn_supp tls utils wps
SUBDIRS += fst
all:
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 8ee2e04..8cb5813 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -241,6 +241,57 @@
* [1] http://en.wikipedia.org/wiki/Near_and_far_field
*/
+enum bw_type {
+ ACS_BW40,
+ ACS_BW80,
+ ACS_BW160,
+};
+
+struct bw_item {
+ int first;
+ int last;
+ int center_chan;
+};
+
+static const struct bw_item bw_40[] = {
+ { 5180, 5200, 38 }, { 5220, 5240, 46 }, { 5260, 5280, 54 },
+ { 5300, 5320, 62 }, { 5500, 5520, 102 }, { 5540, 5560, 110 },
+ { 5580, 5600, 110 }, { 5620, 5640, 126}, { 5660, 5680, 134 },
+ { 5700, 5720, 142 }, { 5745, 5765, 151 }, { 5785, 5805, 159 },
+ { 5825, 5845, 167 }, { 5865, 5885, 175 },
+ { 5955, 5975, 3 }, { 5995, 6015, 11 }, { 6035, 6055, 19 },
+ { 6075, 6095, 27 }, { 6115, 6135, 35 }, { 6155, 6175, 43 },
+ { 6195, 6215, 51 }, { 6235, 6255, 59 }, { 6275, 6295, 67 },
+ { 6315, 6335, 75 }, { 6355, 6375, 83 }, { 6395, 6415, 91 },
+ { 6435, 6455, 99 }, { 6475, 6495, 107 }, { 6515, 6535, 115 },
+ { 6555, 6575, 123 }, { 6595, 6615, 131 }, { 6635, 6655, 139 },
+ { 6675, 6695, 147 }, { 6715, 6735, 155 }, { 6755, 6775, 163 },
+ { 6795, 6815, 171 }, { 6835, 6855, 179 }, { 6875, 6895, 187 },
+ { 6915, 6935, 195 }, { 6955, 6975, 203 }, { 6995, 7015, 211 },
+ { 7035, 7055, 219 }, { 7075, 7095, 227}, { -1, -1, -1 }
+};
+static const struct bw_item bw_80[] = {
+ { 5180, 5240, 42 }, { 5260, 5320, 58 }, { 5500, 5560, 106 },
+ { 5580, 5640, 122 }, { 5660, 5720, 138 }, { 5745, 5805, 155 },
+ { 5825, 5885, 171},
+ { 5955, 6015, 7 }, { 6035, 6095, 23 }, { 6115, 6175, 39 },
+ { 6195, 6255, 55 }, { 6275, 6335, 71 }, { 6355, 6415, 87 },
+ { 6435, 6495, 103 }, { 6515, 6575, 119 }, { 6595, 6655, 135 },
+ { 6675, 6735, 151 }, { 6755, 6815, 167 }, { 6835, 6895, 183 },
+ { 6915, 6975, 199 }, { 6995, 7055, 215 }, { -1, -1, -1 }
+};
+static const struct bw_item bw_160[] = {
+ { 5180, 5320, 50 }, { 5500, 5640, 114 }, { 5745, 5885, 163 },
+ { 5955, 6095, 15 }, { 6115, 6255, 47 }, { 6275, 6415, 79 },
+ { 6435, 6575, 111 }, { 6595, 6735, 143 },
+ { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+};
+static const struct bw_item *bw_desc[] = {
+ [ACS_BW40] = bw_40,
+ [ACS_BW80] = bw_80,
+ [ACS_BW160] = bw_160,
+};
+
static int acs_request_scan(struct hostapd_iface *iface);
static int acs_survey_is_sufficient(struct freq_survey *survey);
@@ -370,48 +421,31 @@
}
-static int acs_usable_bw40_chan(const struct hostapd_channel_data *chan)
+static bool acs_usable_bw_chan(const struct hostapd_channel_data *chan,
+ enum bw_type bw)
{
- const int allowed[] = { 5180, 5220, 5260, 5300, 5500, 5540, 5580, 5620,
- 5660, 5745, 5785, 4920, 4960, 5955, 5995, 6035,
- 6075, 6115, 6155, 6195, 6235, 6275, 6315, 6355,
- 6395, 6435, 6475, 6515, 6555, 6595, 6635, 6675,
- 6715, 6755, 6795, 6835, 6875, 6915, 6955, 6995,
- 7035, 7075 };
- unsigned int i;
+ unsigned int i = 0;
- for (i = 0; i < ARRAY_SIZE(allowed); i++)
- if (chan->freq == allowed[i])
- return 1;
+ while (bw_desc[bw][i].first != -1) {
+ if (chan->freq == bw_desc[bw][i].first)
+ return true;
+ i++;
+ }
- return 0;
+ return false;
}
-static int acs_usable_bw80_chan(const struct hostapd_channel_data *chan)
+static int acs_get_bw_center_chan(int freq, enum bw_type bw)
{
- const int allowed[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035,
- 6115, 6195, 6275, 6355, 6435, 6515, 6595, 6675,
- 6755, 6835, 6915, 6995 };
- unsigned int i;
+ unsigned int i = 0;
- for (i = 0; i < ARRAY_SIZE(allowed); i++)
- if (chan->freq == allowed[i])
- return 1;
-
- return 0;
-}
-
-
-static int acs_usable_bw160_chan(const struct hostapd_channel_data *chan)
-{
- const int allowed[] = { 5180, 5500, 5955, 6115, 6275, 6435, 6595, 6755,
- 6915 };
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(allowed); i++)
- if (chan->freq == allowed[i])
- return 1;
+ while (bw_desc[bw][i].first != -1) {
+ if (freq >= bw_desc[bw][i].first &&
+ freq <= bw_desc[bw][i].last)
+ return bw_desc[bw][i].center_chan;
+ i++;
+ }
return 0;
}
@@ -607,6 +641,26 @@
}
+static enum hostapd_hw_mode
+acs_find_mode(struct hostapd_iface *iface, int freq)
+{
+ int i;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode)) {
+ chan = acs_find_chan_mode(mode, freq);
+ if (chan)
+ return mode->mode;
+ }
+ }
+
+ return HOSTAPD_MODE_IEEE80211ANY;
+}
+
+
static struct hostapd_channel_data *
acs_find_chan(struct hostapd_iface *iface, int freq)
{
@@ -665,7 +719,7 @@
struct hostapd_channel_data **ideal_chan,
long double *ideal_factor)
{
- struct hostapd_channel_data *chan, *adj_chan = NULL;
+ struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
long double factor;
int i, j;
unsigned int k;
@@ -673,8 +727,9 @@
for (i = 0; i < mode->num_channels; i++) {
double total_weight;
struct acs_bias *bias, tmp_bias;
+ bool update_best = true;
- chan = &mode->channels[i];
+ best = chan = &mode->channels[i];
/* Since in the current ACS implementation the first channel is
* always a primary channel, skip channels not available as
@@ -713,7 +768,7 @@
((iface->conf->ieee80211n &&
iface->conf->secondary_channel) ||
is_6ghz_freq(chan->freq)) &&
- !acs_usable_bw40_chan(chan)) {
+ !acs_usable_bw_chan(chan, ACS_BW40)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
chan->chan);
@@ -724,7 +779,7 @@
(iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
if (hostapd_get_oper_chwidth(iface->conf) ==
CONF_OPER_CHWIDTH_80MHZ &&
- !acs_usable_bw80_chan(chan)) {
+ !acs_usable_bw_chan(chan, ACS_BW80)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
chan->chan);
@@ -733,7 +788,7 @@
if (hostapd_get_oper_chwidth(iface->conf) ==
CONF_OPER_CHWIDTH_160MHZ &&
- !acs_usable_bw160_chan(chan)) {
+ !acs_usable_bw_chan(chan, ACS_BW160)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
chan->chan);
@@ -761,7 +816,15 @@
if (acs_usable_chan(adj_chan)) {
factor += adj_chan->interference_factor;
total_weight += 1;
+ } else {
+ update_best = false;
}
+
+ /* find the best channel in this segment */
+ if (update_best &&
+ adj_chan->interference_factor <
+ best->interference_factor)
+ best = adj_chan;
}
if (j != n_chans) {
@@ -770,6 +833,18 @@
continue;
}
+ /* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
+ * crowded primary channel if one was found in the segment */
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ chan != best) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
+ best->chan, chan->chan,
+ chan->interference_factor,
+ best->interference_factor);
+ chan = best;
+ }
+
/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
* channel interference factor. */
if (is_24ghz_mode(mode->mode)) {
@@ -923,21 +998,50 @@
}
+static void acs_adjust_secondary(struct hostapd_iface *iface)
+{
+ unsigned int i;
+
+ /* When working with bandwidth over 20 MHz on the 5 GHz or 6 GHz band,
+ * ACS can return a secondary channel which is not the first channel of
+ * the segment and we need to adjust. */
+ if (!iface->conf->secondary_channel ||
+ acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
+ return;
+
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+
+ for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
+ if (iface->freq == bw_desc[ACS_BW40][i].first)
+ iface->conf->secondary_channel = 1;
+ else if (iface->freq == bw_desc[ACS_BW40][i].last)
+ iface->conf->secondary_channel = -1;
+ }
+}
+
+
static void acs_adjust_center_freq(struct hostapd_iface *iface)
{
- int offset;
+ int center;
wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_USE_HT:
- offset = 2 * iface->conf->secondary_channel;
+ if (iface->conf->secondary_channel &&
+ iface->freq >= 2400 && iface->freq < 2500)
+ center = iface->conf->channel +
+ 2 * iface->conf->secondary_channel;
+ else if (iface->conf->secondary_channel)
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW40);
+ else
+ center = iface->conf->channel;
break;
case CONF_OPER_CHWIDTH_80MHZ:
- offset = 6;
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
break;
case CONF_OPER_CHWIDTH_160MHZ:
- offset = 14;
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
break;
default:
/* TODO: How can this be calculated? Adjust
@@ -947,8 +1051,7 @@
return;
}
- hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
- iface->conf->channel + offset);
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, center);
}
@@ -1004,8 +1107,10 @@
iface->conf->channel = ideal_chan->chan;
iface->freq = ideal_chan->freq;
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ acs_adjust_secondary(iface);
acs_adjust_center_freq(iface);
+ }
err = hostapd_select_hw_mode(iface);
if (err) {
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index b73aae2..5dc8a8f 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -481,10 +481,11 @@
struct hostapd_ssid *ssid = &conf->ssid;
struct sae_password_entry *pw;
- if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
+ if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+ !hostapd_sae_pw_id_in_use(conf) &&
!wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
!hostapd_sae_pk_in_use(conf)) ||
- conf->sae_pwe == 3 ||
+ conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
!wpa_key_mgmt_sae(conf->wpa_key_mgmt))
return 0; /* PT not needed */
@@ -798,6 +799,7 @@
os_free(conf->radius_req_attr_sqlite);
os_free(conf->rsn_preauth_interfaces);
os_free(conf->ctrl_interface);
+ os_free(conf->config_id);
os_free(conf->ca_cert);
os_free(conf->server_cert);
os_free(conf->server_cert2);
@@ -1211,6 +1213,14 @@
return false;
}
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) &&
+ bss->sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+ wpa_printf(MSG_INFO, "SAE: Enabling SAE H2E on 6 GHz");
+ bss->sae_pwe = SAE_PWE_BOTH;
+ }
+#endif /* CONFIG_SAE */
+
return true;
}
@@ -1452,6 +1462,12 @@
}
#endif /* CONFIG_IEEE80211BE */
+ if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
+ wpa_printf(MSG_ERROR,
+ "Hidden SSID is not suppored when MBSSID is enabled");
+ return -1;
+ }
+
return 0;
}
@@ -1535,6 +1551,12 @@
}
#endif /* CONFIG_IEEE80211BE */
+ if (full_config && conf->mbssid && !conf->ieee80211ax) {
+ wpa_printf(MSG_ERROR,
+ "Cannot enable multiple BSSID support without ieee80211ax");
+ return -1;
+ }
+
for (i = 0; i < conf->num_bss; i++) {
if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
return -1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 268829a..6dbb223 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -443,6 +443,7 @@
int eap_teap_pac_no_inner;
int eap_teap_separate_result;
int eap_teap_id;
+ int eap_teap_method_sequence;
int eap_sim_aka_result_ind;
int eap_sim_id;
char *imsi_privacy_key;
@@ -670,7 +671,7 @@
unsigned int sae_sync;
int sae_require_mfp;
int sae_confirm_immediate;
- int sae_pwe;
+ enum sae_pwe sae_pwe;
int *sae_groups;
struct sae_password_entry *sae_passwords;
@@ -916,6 +917,8 @@
u8 ext_capa[EXT_CAPA_MAX_LEN];
u8 rnr;
+ char *config_id;
+ bool xrates_supported;
};
/**
@@ -1076,6 +1079,7 @@
int ecsa_ie_only;
unsigned int skip_send_eapol;
unsigned int enable_eapol_large_timeout;
+ bool delay_eapol_tx;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_ACS
@@ -1105,6 +1109,7 @@
u8 he_6ghz_rx_ant_pat;
u8 he_6ghz_tx_ant_pat;
u8 he_6ghz_reg_pwr_type;
+ bool require_he;
#endif /* CONFIG_IEEE80211AX */
/* VHT enable/disable config from CHAN_SWITCH */
@@ -1144,6 +1149,12 @@
#define CH_SWITCH_EHT_ENABLED BIT(0)
#define CH_SWITCH_EHT_DISABLED BIT(1)
unsigned int ch_switch_eht_config;
+
+ enum mbssid {
+ MBSSID_DISABLED = 0,
+ MBSSID_ENABLED = 1,
+ ENHANCED_MBSSID_ENABLED = 2,
+ } mbssid;
};
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index b9fd920..1ffc37f 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -92,7 +92,7 @@
goto fail;
pos = buf;
- pos = hostapd_eid_ext_capab(hapd, pos);
+ pos = hostapd_eid_ext_capab(hapd, pos, false);
if (add_buf_data(&assocresp, buf, pos - buf) < 0)
goto fail;
pos = hostapd_eid_interworking(hapd, pos);
@@ -724,6 +724,7 @@
params.key_len = key_len;
params.vlan_id = vlan_id;
params.key_flag = key_flag;
+ params.link_id = -1;
return hapd->driver->set_key(hapd->drv_priv, ¶ms);
}
@@ -905,8 +906,10 @@
chan->chan)))
continue;
if (is_6ghz_freq(chan->freq) &&
- hapd->iface->conf->acs_exclude_6ghz_non_psc &&
- !is_6ghz_psc_frequency(chan->freq))
+ ((hapd->iface->conf->acs_exclude_6ghz_non_psc &&
+ !is_6ghz_psc_frequency(chan->freq)) ||
+ (!hapd->iface->conf->ieee80211ax &&
+ !hapd->iface->conf->ieee80211be)))
continue;
if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
!(hapd->iface->conf->acs_exclude_dfs &&
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index db8a267..309e69a 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -29,9 +29,9 @@
return "SHARED_KEY";
case WLAN_AUTH_FT:
return "FT";
+ default:
+ return "unknown";
}
-
- return "unknown";
}
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index fd9c96f..4ab2a4a 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -211,6 +211,7 @@
cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
cfg->eap_teap_id = hapd->conf->eap_teap_id;
+ cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
cfg->eap_sim_id = hapd->conf->eap_sim_id;
cfg->imsi_privacy_key = hapd->imsi_privacy_key;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 8676570..dbc6b06 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -462,15 +462,86 @@
}
+static int
+ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_data *tx_bss;
+ size_t len;
+ u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
+
+ if (!iface->mbssid_max_interfaces ||
+ iface->num_bss > iface->mbssid_max_interfaces ||
+ (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ !iface->ema_max_periodicity))
+ goto fail;
+
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
+ NULL, 0);
+ if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ elem_count > iface->ema_max_periodicity))
+ goto fail;
+
+ elem = os_zalloc(len);
+ if (!elem)
+ goto fail;
+
+ elem_offset = os_zalloc(elem_count * sizeof(u8 *));
+ if (!elem_offset)
+ goto fail;
+
+ end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON,
+ elem_count, elem_offset, NULL, 0);
+
+ params->mbssid_tx_iface = tx_bss->conf->iface;
+ params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
+ params->mbssid_elem = elem;
+ params->mbssid_elem_len = end - elem;
+ params->mbssid_elem_count = elem_count;
+ params->mbssid_elem_offset = elem_offset;
+ if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED)
+ params->ema = true;
+
+ return 0;
+
+fail:
+ os_free(elem);
+ wpa_printf(MSG_ERROR, "MBSSID: Configuration failed");
+ return -1;
+}
+
+
+static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid,
+ u8 mbssid_elem_count)
+{
+ struct hostapd_iface *iface = hapd->iface;
+
+ if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) {
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 3;
+ *eid++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
+ *eid++ = iface->num_bss;
+ *eid++ = mbssid_elem_count;
+ }
+
+ return eid;
+}
+
+
static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
const struct ieee80211_mgmt *req,
int is_p2p, size_t *resp_len,
- bool bcast_probe_resp)
+ bool bcast_probe_resp, const u8 *known_bss,
+ u8 known_bss_len)
{
struct ieee80211_mgmt *resp;
u8 *pos, *epos, *csa_pos;
size_t buflen;
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
+
#define MAX_PROBERESP_LEN 768
buflen = MAX_PROBERESP_LEN;
#ifdef CONFIG_WPS
@@ -517,6 +588,8 @@
}
#endif /* CONFIG_IEEE80211BE */
+ buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+ known_bss, known_bss_len);
buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
buflen += hostapd_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
@@ -576,6 +649,8 @@
pos = hostapd_get_rsne(hapd, pos, epos - pos);
pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+ pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+ NULL, known_bss, known_bss_len);
pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
pos = hostapd_get_mde(hapd, pos, epos - pos);
@@ -589,7 +664,11 @@
pos = hostapd_eid_ht_capabilities(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
- pos = hostapd_eid_ext_capab(hapd, pos);
+ /* Probe Response frames always include all non-TX profiles except
+ * when a list of known BSSes is included in the Probe Request frame. */
+ pos = hostapd_eid_ext_capab(hapd, pos,
+ hapd->iconf->mbssid >= MBSSID_ENABLED &&
+ !known_bss_len);
pos = hostapd_eid_time_adv(hapd, pos);
pos = hostapd_eid_time_zone(hapd, pos);
@@ -1141,11 +1220,18 @@
}
#endif /* CONFIG_TESTING_OPTIONS */
+ /* Do not send Probe Response frame from a non-transmitting multiple
+ * BSSID profile unless the Probe Request frame is directed at that
+ * particular BSS. */
+ if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
+ return;
+
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, false);
+ &resp_len, false, elems.mbssid_known_bss,
+ elems.mbssid_known_bss_len);
if (resp == NULL)
return;
@@ -1167,7 +1253,8 @@
hapd->cs_c_off_ecsa_proberesp;
}
- ret = hostapd_drv_send_mlme(hapd, resp, resp_len, noack,
+ ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
+ resp_len, noack,
csa_offs_len ? csa_offs : NULL,
csa_offs_len, 0);
@@ -1214,7 +1301,7 @@
"this");
/* Generate a Probe Response template for the non-P2P case */
- return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false);
+ return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false, NULL, 0);
}
#endif /* NEED_AP_MLME */
@@ -1233,7 +1320,7 @@
return hostapd_gen_probe_resp(hapd, NULL, 0,
¶ms->unsol_bcast_probe_resp_tmpl_len,
- true);
+ true, NULL, 0);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1514,7 +1601,12 @@
#ifdef NEED_AP_MLME
u16 capab_info;
u8 *pos, *tailpos, *tailend, *csa_pos;
+ bool complete = false;
+#endif /* NEED_AP_MLME */
+ os_memset(params, 0, sizeof(*params));
+
+#ifdef NEED_AP_MLME
#define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512
head = os_zalloc(BEACON_HEAD_BUF_SIZE);
@@ -1566,6 +1658,9 @@
}
#endif /* CONFIG_IEEE80211BE */
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ hapd == hostapd_mbssid_get_tx_bss(hapd))
+ tail_len += 5; /* Multiple BSSID Configuration element */
tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
tail_len += hostapd_mbo_ie_len(hapd);
tail_len += hostapd_eid_owe_trans_len(hapd);
@@ -1652,7 +1747,20 @@
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
- tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+ if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
+ if (ieee802_11_build_ap_params_mbssid(hapd, params)) {
+ os_free(head);
+ os_free(tail);
+ wpa_printf(MSG_ERROR,
+ "MBSSID: Failed to set beacon data");
+ return -1;
+ }
+ complete = hapd->iconf->mbssid == MBSSID_ENABLED ||
+ (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ params->mbssid_elem_count == 1);
+ }
+
+ tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete);
/*
* TODO: Time Advertisement element should only be included in some
@@ -1692,6 +1800,8 @@
tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
+ tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
+ params->mbssid_elem_count);
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
@@ -1774,7 +1884,6 @@
resp = hostapd_probe_resp_offloads(hapd, &resp_len);
#endif /* NEED_AP_MLME */
- os_memset(params, 0, sizeof(*params));
params->head = (u8 *) head;
params->head_len = head_len;
params->tail = tail;
@@ -1877,6 +1986,10 @@
params->head = NULL;
os_free(params->proberesp);
params->proberesp = NULL;
+ os_free(params->mbssid_elem);
+ params->mbssid_elem = NULL;
+ os_free(params->mbssid_elem_offset);
+ params->mbssid_elem_offset = NULL;
#ifdef CONFIG_FILS
os_free(params->fd_frame_tmpl);
params->fd_frame_tmpl = NULL;
diff --git a/src/ap/comeback_token.c b/src/ap/comeback_token.c
new file mode 100644
index 0000000..8d9f21b
--- /dev/null
+++ b/src/ap/comeback_token.c
@@ -0,0 +1,139 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "comeback_token.h"
+
+
+#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
+
+static int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
+ addr, ETH_ALEN, hash) < 0)
+ return -1;
+ *idx = hash[0];
+ return 0;
+}
+
+
+int check_comeback_token(const u8 *comeback_key,
+ u16 *comeback_pending_idx, const u8 *addr,
+ const u8 *token, size_t token_len)
+{
+ u8 mac[SHA256_MAC_LEN];
+ const u8 *addrs[2];
+ size_t len[2];
+ u16 token_idx;
+ u8 idx;
+
+ if (token_len != SHA256_MAC_LEN ||
+ comeback_token_hash(comeback_key, addr, &idx) < 0)
+ return -1;
+ token_idx = comeback_pending_idx[idx];
+ if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
+ wpa_printf(MSG_DEBUG,
+ "Comeback: Invalid anti-clogging token from "
+ MACSTR " - token_idx 0x%04x, expected 0x%04x",
+ MAC2STR(addr), WPA_GET_BE16(token), token_idx);
+ return -1;
+ }
+
+ addrs[0] = addr;
+ len[0] = ETH_ALEN;
+ addrs[1] = token;
+ len[1] = 2;
+ if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+ 2, addrs, len, mac) < 0 ||
+ os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
+ return -1;
+
+ comeback_pending_idx[idx] = 0; /* invalidate used token */
+
+ return 0;
+}
+
+
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+ u8 *comeback_key, u16 comeback_idx,
+ u16 *comeback_pending_idx, size_t idx_len,
+ int group, const u8 *addr, int h2e)
+{
+ struct wpabuf *buf;
+ u8 *token;
+ struct os_reltime now;
+ u8 idx[2];
+ const u8 *addrs[2];
+ size_t len[2];
+ u8 p_idx;
+ u16 token_idx;
+
+ os_get_reltime(&now);
+ if (!os_reltime_initialized(last_comeback_key_update) ||
+ os_reltime_expired(&now, last_comeback_key_update, 60) ||
+ comeback_idx == 0xffff) {
+ if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
+ comeback_key, COMEBACK_KEY_SIZE);
+ *last_comeback_key_update = now;
+ comeback_idx = 0;
+ os_memset(comeback_pending_idx, 0, idx_len);
+ }
+
+ buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
+ if (buf == NULL)
+ return NULL;
+
+ if (group)
+ wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
+ if (h2e) {
+ /* Encapsulate Anti-clogging Token field in a container IE */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
+ }
+
+ if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ token_idx = comeback_pending_idx[p_idx];
+ if (!token_idx) {
+ comeback_idx++;
+ token_idx = comeback_idx;
+ comeback_pending_idx[p_idx] = token_idx;
+ }
+ WPA_PUT_BE16(idx, token_idx);
+ token = wpabuf_put(buf, SHA256_MAC_LEN);
+ addrs[0] = addr;
+ len[0] = ETH_ALEN;
+ addrs[1] = idx;
+ len[1] = sizeof(idx);
+ if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+ 2, addrs, len, token) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ WPA_PUT_BE16(token, token_idx);
+
+ return buf;
+}
+
+#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
diff --git a/src/ap/comeback_token.h b/src/ap/comeback_token.h
new file mode 100644
index 0000000..d5de9e6
--- /dev/null
+++ b/src/ap/comeback_token.h
@@ -0,0 +1,21 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * 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.
+ */
+
+#ifndef COMEBACK_TOKEN_H
+#define COMEBACK_TOKEN_H
+
+int check_comeback_token(const u8 *comeback_key,
+ u16 *comeback_pending_idx, const u8 *addr,
+ const u8 *token, size_t token_len);
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+ u8 *comeback_key, u16 comeback_idx,
+ u16 *comeback_pending_idx, size_t idx_len,
+ int group, const u8 *addr, int h2e);
+
+#endif /* COMEBACK_TOKEN_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index ac8f6fa..168e5f5 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -99,7 +99,7 @@
len += ret;
ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
- data.current_rx_rate);
+ data.current_rx_rate / 100);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -131,7 +131,7 @@
len += ret;
ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
- data.current_tx_rate);
+ data.current_tx_rate / 100);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -206,9 +206,9 @@
return "REMOVE";
case STA_DISASSOC_FROM_CLI:
return "DISASSOC_FROM_CLI";
+ default:
+ return "?";
}
-
- return "?";
}
@@ -218,6 +218,7 @@
{
int len, res, ret, i;
const char *keyid;
+ const u8 *dpp_pkhash;
if (!sta)
return 0;
@@ -386,6 +387,18 @@
len += ret;
}
+ dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+ if (dpp_pkhash) {
+ ret = os_snprintf(buf + len, buflen - len, "dpp_pkhash=");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ len += wpa_snprintf_hex(buf + len, buflen - len, dpp_pkhash,
+ SHA256_MAC_LEN);
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
return len;
}
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 7f31f28..e8c5ec9 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -445,6 +445,8 @@
mode = iface->current_mode;
for (i = 0; i < n_chans; i++) {
+ if (start_chan_idx + i >= mode->num_channels)
+ break;
channel = &mode->channels[start_chan_idx + i];
if (channel->flag & HOSTAPD_CHAN_RADAR)
res++;
@@ -797,6 +799,8 @@
mode = iface->current_mode;
for (i = 0; i < n_chans; i++) {
+ if (start_chan_idx + i >= mode->num_channels)
+ break;
channel = &mode->channels[start_chan_idx + i];
if (!(channel->flag & HOSTAPD_CHAN_RADAR))
continue;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 32ddb3b..70dd18e 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1378,8 +1378,7 @@
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);
+ dpp_notify_auth_success(hapd->dpp_auth, initiator);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
wpa_printf(MSG_INFO,
@@ -2052,6 +2051,7 @@
os_time_t expire;
int expiration;
enum dpp_status_error res;
+ u8 pkhash[SHA256_MAC_LEN];
os_memset(&intro, 0, sizeof(intro));
@@ -2088,7 +2088,7 @@
wpabuf_len(hapd->conf->dpp_netaccesskey),
wpabuf_head(hapd->conf->dpp_csign),
wpabuf_len(hapd->conf->dpp_csign),
- connector, connector_len, &expire);
+ connector, connector_len, &expire, pkhash);
if (res == 255) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in internal failure (peer "
@@ -2133,9 +2133,9 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
- WPA_KEY_MGMT_DPP) < 0) {
+ WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
goto done;
}
@@ -2793,6 +2793,7 @@
os_time_t expire;
int expiration;
enum dpp_status_error res;
+ u8 pkhash[SHA256_MAC_LEN];
os_memset(&intro, 0, sizeof(intro));
@@ -2870,7 +2871,7 @@
wpabuf_len(hapd->conf->dpp_netaccesskey),
wpabuf_head(hapd->conf->dpp_csign),
wpabuf_len(hapd->conf->dpp_csign),
- connector, connector_len, &expire);
+ connector, connector_len, &expire, pkhash);
if (res == 255) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in internal failure (peer "
@@ -2904,9 +2905,9 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
- WPA_KEY_MGMT_DPP) < 0) {
+ WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
goto done;
}
@@ -3089,8 +3090,7 @@
* from TX status handler, but since there was no such handler
* call yet, simply send out the event message and proceed with
* exchange. */
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- DPP_EVENT_AUTH_SUCCESS "init=1");
+ dpp_notify_auth_success(hapd->dpp_auth, 1);
hapd->dpp_auth_ok_on_ack = 0;
}
@@ -3376,7 +3376,7 @@
{
struct hostapd_data *hapd = ctx;
- gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf);
+ gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf, 0);
}
#endif /* CONFIG_DPP2 */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index b1cb31e..4c33e86 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -451,7 +451,7 @@
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
- if (hapd->conf->sae_pwe == 2 &&
+ if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
sta->auth_alg == WLAN_AUTH_SAE &&
sta->sae && !sta->sae->h2e &&
ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
@@ -862,14 +862,16 @@
int finished)
{
#ifdef NEED_AP_MLME
- int channel, chwidth, is_dfs;
+ int channel, chwidth, is_dfs0, is_dfs;
u8 seg0_idx = 0, seg1_idx = 0;
size_t i;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+ "driver %s channel switch: iface->freq=%d, freq=%d, ht=%d, vht_ch=0x%x, "
+ "he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
finished ? "had" : "starting",
+ hapd->iface->freq,
freq, ht, hapd->iconf->ch_switch_vht_config,
hapd->iconf->ch_switch_he_config,
hapd->iconf->ch_switch_eht_config, offset,
@@ -882,6 +884,8 @@
return;
}
+ /* Check if any of configured channels require DFS */
+ is_dfs0 = hostapd_is_dfs_required(hapd->iface);
hapd->iface->freq = freq;
channel = hostapd_hw_get_channel(hapd, freq);
@@ -997,11 +1001,11 @@
hapd->iface->num_hw_features);
wpa_msg(hapd->msg_ctx, MSG_INFO,
- "%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d dfs=%d",
+ "%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d is_dfs0=%d dfs=%d",
finished ? WPA_EVENT_CHANNEL_SWITCH :
WPA_EVENT_CHANNEL_SWITCH_STARTED,
freq, ht, offset, channel_width_to_string(width),
- cf1, cf2, is_dfs);
+ cf1, cf2, is_dfs0, is_dfs);
if (!finished)
return;
@@ -1013,6 +1017,14 @@
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
} else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+ /* Complete AP configuration for the first bring up. */
+ if (is_dfs0 > 0 &&
+ hostapd_is_dfs_required(hapd->iface) <= 0 &&
+ hapd->iface->state != HAPD_IFACE_ENABLED) {
+ /* Fake a CAC start bit to skip setting channel */
+ hapd->iface->cac_started = 1;
+ hostapd_setup_interface_complete(hapd->iface, 0);
+ }
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
} else if (is_dfs &&
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index 0310aab..d64fb8c 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -530,9 +530,9 @@
switch (iph->ip_p) {
case 17:
return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+ default:
+ return 0;
}
-
- return 0;
}
@@ -567,9 +567,9 @@
case ETH_P_IP:
return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
end - pkt - 2);
+ default:
+ return 0;
}
-
- return 0;
}
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 90f1577..4642e49 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1524,7 +1524,7 @@
#ifdef CONFIG_DPP
void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
const u8 *sa, u8 dialog_token,
- int prot, struct wpabuf *buf)
+ int prot, struct wpabuf *buf, int freq)
{
struct wpabuf *tx_buf;
@@ -1582,7 +1582,7 @@
return;
if (prot)
convert_to_protected_dual(tx_buf);
- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ hostapd_drv_send_action(hapd, freq ? freq : hapd->iface->freq, 0, sa,
wpabuf_head(tx_buf),
wpabuf_len(tx_buf));
wpabuf_free(tx_buf);
@@ -1593,7 +1593,7 @@
static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
const u8 *sa,
const u8 *data, size_t len, int prot,
- int std_addr3)
+ int std_addr3, int freq)
{
const u8 *pos = data;
const u8 *end = data + len;
@@ -1688,7 +1688,8 @@
data, len);
if (!msg)
return;
- gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
+ gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
+ freq);
return;
}
#endif /* CONFIG_DPP */
@@ -1871,7 +1872,7 @@
switch (data[0]) {
case WLAN_PA_GAS_INITIAL_REQ:
gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
- std_addr3);
+ std_addr3, freq);
break;
case WLAN_PA_GAS_COMEBACK_REQ:
gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 1528af4..7646a98 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -90,6 +90,6 @@
void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
const u8 *sa, u8 dialog_token,
- int prot, struct wpabuf *buf);
+ int prot, struct wpabuf *buf, int freq);
#endif /* GAS_SERV_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 3681df6..58492e5 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -91,6 +91,29 @@
}
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd)
+{
+ if (hapd->iconf->mbssid)
+ return hapd->iface->bss[0];
+
+ return hapd;
+}
+
+
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd)
+{
+ if (hapd->iconf->mbssid) {
+ size_t i;
+
+ for (i = 1; i < hapd->iface->num_bss; i++)
+ if (hapd->iface->bss[i] == hapd)
+ return i;
+ }
+
+ return 0;
+}
+
+
void hostapd_reconfig_encryption(struct hostapd_data *hapd)
{
if (hapd->wpa_auth)
@@ -172,27 +195,34 @@
}
-static void hostapd_clear_old(struct hostapd_iface *iface)
+static void hostapd_clear_old_bss(struct hostapd_data *bss)
{
- size_t j;
+ wpa_printf(MSG_DEBUG, "BSS %s changed - clear old state",
+ bss->conf->iface);
/*
* Deauthenticate all stations since the new configuration may not
* allow them to use the BSS anymore.
*/
- for (j = 0; j < iface->num_bss; j++) {
- hostapd_flush_old_stations(iface->bss[j],
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_flush_old_stations(bss, WLAN_REASON_PREV_AUTH_NOT_VALID);
#ifdef CONFIG_WEP
- hostapd_broadcast_wep_clear(iface->bss[j]);
+ hostapd_broadcast_wep_clear(bss);
#endif /* CONFIG_WEP */
#ifndef CONFIG_NO_RADIUS
- /* TODO: update dynamic data based on changed configuration
- * items (e.g., open/close sockets, etc.) */
- radius_client_flush(iface->bss[j]->radius, 0);
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(bss->radius, 0);
#endif /* CONFIG_NO_RADIUS */
- }
+}
+
+
+static void hostapd_clear_old(struct hostapd_iface *iface)
+{
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++)
+ hostapd_clear_old_bss(iface->bss[j]);
}
@@ -236,13 +266,13 @@
if (newconf == NULL)
return -1;
- hostapd_clear_old(iface);
-
oldconf = hapd->iconf;
if (hostapd_iface_conf_changed(newconf, oldconf)) {
char *fname;
int res;
+ hostapd_clear_old(iface);
+
wpa_printf(MSG_DEBUG,
"Configuration changes include interface/BSS modification - force full disable+enable sequence");
fname = os_strdup(iface->config_fname);
@@ -272,6 +302,10 @@
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
+ if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
+ os_strcmp(hapd->conf->config_id,
+ newconf->bss[j]->config_id) != 0)
+ hostapd_clear_old_bss(hapd);
hapd->iconf = newconf;
hapd->iconf->channel = oldconf->channel;
hapd->iconf->acs = oldconf->acs;
@@ -525,6 +559,7 @@
iface->current_rates = NULL;
os_free(iface->basic_rates);
iface->basic_rates = NULL;
+ iface->cac_started = 0;
ap_list_deinit(iface);
sta_track_deinit(iface);
airtime_policy_update_deinit(iface);
@@ -1104,18 +1139,55 @@
#endif /* CONFIG_NO_RADIUS */
+static int hostapd_start_beacon(struct hostapd_data *hapd,
+ bool flush_old_stations)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ return -1;
+
+ if (flush_old_stations && !conf->start_disabled &&
+ conf->broadcast_deauth) {
+ u8 addr[ETH_ALEN];
+
+ /* Should any previously associated STA not have noticed that
+ * the AP had stopped and restarted, send one more
+ * deauthentication notification now that the AP is ready to
+ * operate. */
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Deauthenticate all stations at BSS start");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+
+ if (hapd->driver && hapd->driver->set_operstate)
+ hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+ return 0;
+}
+
+
/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
* @first: Whether this BSS is the first BSS of an interface; -1 = not first,
* but interface may exist
+ * @start_beacon: Whether Beacon frame template should be configured and
+ * transmission of Beaconf rames started at this time. This is used when
+ * MBSSID element is enabled where the information regarding all BSSes
+ * should be retrieved before configuring the Beacon frame template. The
+ * calling functions are responsible for configuring the Beacon frame
+ * explicitly if this is set to false.
*
* This function is used to initialize all per-BSS data structures and
* resources. This gets called in a loop for each BSS when an interface is
* initialized. Most of the modules that are initialized here will be
* deinitialized in hostapd_cleanup().
*/
-static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ bool start_beacon)
{
struct hostapd_bss_config *conf = hapd->conf;
u8 ssid[SSID_MAX_LEN + 1];
@@ -1388,29 +1460,11 @@
return -1;
}
- if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
- return -1;
-
- if (flush_old_stations && !conf->start_disabled &&
- conf->broadcast_deauth) {
- u8 addr[ETH_ALEN];
-
- /* Should any previously associated STA not have noticed that
- * the AP had stopped and restarted, send one more
- * deauthentication notification now that the AP is ready to
- * operate. */
- wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
- "Deauthenticate all stations at BSS start");
- os_memset(addr, 0xff, ETH_ALEN);
- hostapd_drv_sta_deauth(hapd, addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
- }
-
if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
return -1;
- if (hapd->driver && hapd->driver->set_operstate)
- hapd->driver->set_operstate(hapd->drv_priv, 1);
+ if (start_beacon)
+ return hostapd_start_beacon(hapd, flush_old_stations);
return 0;
}
@@ -2135,7 +2189,7 @@
hapd = iface->bss[j];
if (j)
os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
- if (hostapd_setup_bss(hapd, j == 0)) {
+ if (hostapd_setup_bss(hapd, j == 0, !iface->conf->mbssid)) {
for (;;) {
hapd = iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
@@ -2149,6 +2203,24 @@
if (is_zero_ether_addr(hapd->conf->bssid))
prev_addr = hapd->own_addr;
}
+
+ if (hapd->iconf->mbssid) {
+ for (j = 0; hapd->iconf->mbssid && j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hostapd_start_beacon(hapd, true)) {
+ for (;;) {
+ hapd = iface->bss[j];
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_free_hapd_data(hapd);
+ if (j == 0)
+ break;
+ j--;
+ }
+ goto fail;
+ }
+ }
+ }
+
hapd = iface->bss[0];
hostapd_tx_queue_params(iface);
@@ -2802,6 +2874,21 @@
}
+int hostapd_reload_bss_only(struct hostapd_data *bss)
+{
+
+ wpa_printf(MSG_DEBUG, "Reload BSS %s", bss->conf->iface);
+ hostapd_set_security_params(bss->conf, 1);
+ if (hostapd_config_check(bss->iconf, 1) < 0) {
+ wpa_printf(MSG_ERROR, "Updated BSS configuration is invalid");
+ return -1;
+ }
+ hostapd_clear_old_bss(bss);
+ hostapd_reload_bss(bss);
+ return 0;
+}
+
+
int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
@@ -3030,7 +3117,7 @@
if (start_ctrl_iface_bss(hapd) < 0 ||
(hapd_iface->state == HAPD_IFACE_ENABLED &&
- hostapd_setup_bss(hapd, -1))) {
+ hostapd_setup_bss(hapd, -1, true))) {
hostapd_cleanup(hapd);
hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
hapd_iface->conf->num_bss--;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index e121362..a88f9b6 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -352,11 +352,15 @@
#endif /* CONFIG_SQLITE */
#ifdef CONFIG_SAE
+
+#define COMEBACK_KEY_SIZE 8
+#define COMEBACK_PENDING_IDX_SIZE 256
+
/** Key used for generating SAE anti-clogging tokens */
- u8 comeback_key[8];
+ u8 comeback_key[COMEBACK_KEY_SIZE];
struct os_reltime last_comeback_key_update;
u16 comeback_idx;
- u16 comeback_pending_idx[256];
+ u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
int dot11RSNASAERetransPeriod; /* msec */
struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
#endif /* CONFIG_SAE */
@@ -640,6 +644,11 @@
/* Previous WMM element information */
struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
+ /* Maximum number of interfaces supported for MBSSID advertisement */
+ unsigned int mbssid_max_interfaces;
+ /* Maximum profile periodicity for enhanced MBSSID advertisement */
+ unsigned int ema_max_periodicity;
+
int (*enable_iface_cb)(struct hostapd_iface *iface);
int (*disable_iface_cb)(struct hostapd_iface *iface);
};
@@ -669,6 +678,7 @@
void hostapd_interface_deinit_free(struct hostapd_iface *iface);
int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_bss_only(struct hostapd_data *bss);
int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
void hostapd_bss_deinit_no_free(struct hostapd_data *hapd);
void hostapd_free_hapd_data(struct hostapd_data *hapd);
@@ -740,5 +750,7 @@
#endif /* CONFIG_FST */
int hostapd_set_acl(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
#endif /* HOSTAPD_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index a65a296..e53f0dc 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -55,6 +55,8 @@
#include "fils_hlp.h"
#include "dpp_hostapd.h"
#include "gas_query_ap.h"
+#include "comeback_token.h"
+#include "pasn/pasn_common.h"
#ifdef CONFIG_FILS
@@ -68,11 +70,6 @@
#endif /* CONFIG_FILS */
#ifdef CONFIG_PASN
-
-static int handle_auth_pasn_resp(struct hostapd_data *hapd,
- struct sta_info *sta,
- struct rsn_pmksa_cache_entry *pmksa,
- u16 status);
#ifdef CONFIG_FILS
static void pasn_fils_auth_resp(struct hostapd_data *hapd,
@@ -118,9 +115,13 @@
num++;
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
num++;
- h2e_required = (hapd->conf->sae_pwe == 1 ||
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+ num++;
+#endif /* CONFIG_IEEE80211AX */
+ h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
- hapd->conf->sae_pwe != 3 &&
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
if (h2e_required)
num++;
@@ -150,6 +151,13 @@
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
}
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
+ count++;
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
if (h2e_required && count < 8) {
count++;
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
@@ -165,6 +173,7 @@
int i, num, count;
int h2e_required;
+ hapd->conf->xrates_supported = false;
if (hapd->iface->current_rates == NULL)
return eid;
@@ -173,9 +182,13 @@
num++;
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
num++;
- h2e_required = (hapd->conf->sae_pwe == 1 ||
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+ num++;
+#endif /* CONFIG_IEEE80211AX */
+ h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
- hapd->conf->sae_pwe != 3 &&
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
if (h2e_required)
num++;
@@ -208,12 +221,21 @@
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
}
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
+ count++;
+ if (count > 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
if (h2e_required) {
count++;
if (count > 8)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
}
+ hapd->conf->xrates_supported = true;
return pos;
}
@@ -561,7 +583,7 @@
#endif /* CONFIG_SAE_PK */
}
- if (rx_id && hapd->conf->sae_pwe != 3)
+ if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
use_pt = 1;
else if (status_code == WLAN_STATUS_SUCCESS)
use_pt = 0;
@@ -737,124 +759,6 @@
return 0;
}
-
-static int comeback_token_hash(struct hostapd_data *hapd, const u8 *addr,
- u8 *idx)
-{
- u8 hash[SHA256_MAC_LEN];
-
- if (hmac_sha256(hapd->comeback_key, sizeof(hapd->comeback_key),
- addr, ETH_ALEN, hash) < 0)
- return -1;
- *idx = hash[0];
- return 0;
-}
-
-
-static int check_comeback_token(struct hostapd_data *hapd, const u8 *addr,
- const u8 *token, size_t token_len)
-{
- u8 mac[SHA256_MAC_LEN];
- const u8 *addrs[2];
- size_t len[2];
- u16 token_idx;
- u8 idx;
-
- if (token_len != SHA256_MAC_LEN ||
- comeback_token_hash(hapd, addr, &idx) < 0)
- return -1;
- token_idx = hapd->comeback_pending_idx[idx];
- if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
- wpa_printf(MSG_DEBUG,
- "Comeback: Invalid anti-clogging token from "
- MACSTR " - token_idx 0x%04x, expected 0x%04x",
- MAC2STR(addr), WPA_GET_BE16(token), token_idx);
- return -1;
- }
-
- addrs[0] = addr;
- len[0] = ETH_ALEN;
- addrs[1] = token;
- len[1] = 2;
- if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
- 2, addrs, len, mac) < 0 ||
- os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
- return -1;
-
- hapd->comeback_pending_idx[idx] = 0; /* invalidate used token */
-
- return 0;
-}
-
-
-static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
- int group, const u8 *addr, int h2e)
-{
- struct wpabuf *buf;
- u8 *token;
- struct os_reltime now;
- u8 idx[2];
- const u8 *addrs[2];
- size_t len[2];
- u8 p_idx;
- u16 token_idx;
-
- os_get_reltime(&now);
- if (!os_reltime_initialized(&hapd->last_comeback_key_update) ||
- os_reltime_expired(&now, &hapd->last_comeback_key_update, 60) ||
- hapd->comeback_idx == 0xffff) {
- if (random_get_bytes(hapd->comeback_key,
- sizeof(hapd->comeback_key)) < 0)
- return NULL;
- wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
- hapd->comeback_key, sizeof(hapd->comeback_key));
- hapd->last_comeback_key_update = now;
- hapd->comeback_idx = 0;
- os_memset(hapd->comeback_pending_idx, 0,
- sizeof(hapd->comeback_pending_idx));
- }
-
- buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
- if (buf == NULL)
- return NULL;
-
- if (group)
- wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
-
- if (h2e) {
- /* Encapsulate Anti-clogging Token field in a container IE */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
- wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
- }
-
- if (comeback_token_hash(hapd, addr, &p_idx) < 0) {
- wpabuf_free(buf);
- return NULL;
- }
-
- token_idx = hapd->comeback_pending_idx[p_idx];
- if (!token_idx) {
- hapd->comeback_idx++;
- token_idx = hapd->comeback_idx;
- hapd->comeback_pending_idx[p_idx] = token_idx;
- }
- WPA_PUT_BE16(idx, token_idx);
- token = wpabuf_put(buf, SHA256_MAC_LEN);
- addrs[0] = addr;
- len[0] = ETH_ALEN;
- addrs[1] = idx;
- len[1] = sizeof(idx);
- if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
- 2, addrs, len, token) < 0) {
- wpabuf_free(buf);
- return NULL;
- }
- WPA_PUT_BE16(token, token_idx);
-
- return buf;
-}
-
#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
@@ -1220,31 +1124,32 @@
static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
{
- int sae_pwe = hapd->conf->sae_pwe;
+ enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
int id_in_use;
bool sae_pk = false;
id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
- if (id_in_use == 2 && sae_pwe != 3)
- sae_pwe = 1;
- else if (id_in_use == 1 && sae_pwe == 0)
- sae_pwe = 2;
+ if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK)
+ sae_pwe = SAE_PWE_BOTH;
#ifdef CONFIG_SAE_PK
sae_pk = hostapd_sae_pk_in_use(hapd->conf);
- if (sae_pwe == 0 && sae_pk)
- sae_pwe = 2;
+ if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk)
+ sae_pwe = SAE_PWE_BOTH;
#endif /* CONFIG_SAE_PK */
- if (sae_pwe == 0 &&
+ if (sae_pwe == SAE_PWE_HUNT_AND_PECK &&
(hapd->conf->wpa_key_mgmt &
(WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
- sae_pwe = 2;
+ sae_pwe = SAE_PWE_BOTH;
- return ((sae_pwe == 0 || sae_pwe == 3) &&
+ return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
+ sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
status_code == WLAN_STATUS_SUCCESS) ||
- (sae_pwe == 1 &&
+ (sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
(status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
(sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
- (sae_pwe == 2 &&
+ (sae_pwe == SAE_PWE_BOTH &&
(status_code == WLAN_STATUS_SUCCESS ||
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
(sae_pk && status_code == WLAN_STATUS_SAE_PK)));
@@ -1468,7 +1373,8 @@
mgmt->u.auth.variable, &token,
&token_len, groups, status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
- status_code == WLAN_STATUS_SAE_PK);
+ status_code == WLAN_STATUS_SAE_PK,
+ NULL);
if (resp == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1487,7 +1393,9 @@
}
if (token &&
- check_comeback_token(hapd, sta->addr, token, token_len)
+ check_comeback_token(hapd->comeback_key,
+ hapd->comeback_pending_idx, sta->addr,
+ token, token_len)
< 0) {
wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
"incorrect token from " MACSTR,
@@ -1515,8 +1423,14 @@
if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK)
h2e = 1;
- data = auth_build_token_req(hapd, sta->sae->group,
- sta->addr, h2e);
+ data = auth_build_token_req(
+ &hapd->last_comeback_key_update,
+ hapd->comeback_key,
+ hapd->comeback_idx,
+ hapd->comeback_pending_idx,
+ sizeof(hapd->comeback_pending_idx),
+ sta->sae->group,
+ sta->addr, h2e);
resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
if (hapd->conf->mesh & MESH_ENABLED)
sae_set_state(sta, SAE_NOTHING,
@@ -1560,8 +1474,9 @@
return;
}
- if (sae_check_confirm(sta->sae, var, var_len) < 0) {
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (sae_check_confirm(sta->sae, var, var_len,
+ NULL) < 0) {
+ resp = WLAN_STATUS_CHALLENGE_FAIL;
goto reply;
}
sta->sae->rc = peer_send_confirm;
@@ -2110,10 +2025,8 @@
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,
+ res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm,
wpabuf_put(data, 0),
wpabuf_tailroom(data));
if (res < 0) {
@@ -2390,243 +2303,15 @@
#ifdef CONFIG_PASN
-#ifdef CONFIG_SAE
-
-static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd,
- struct sta_info *sta,
- struct wpabuf *wd)
-{
- struct pasn_data *pasn = sta->pasn;
- const char *password;
- const u8 *data;
- size_t buf_len;
- u16 res, alg, seq, status;
- int groups[] = { pasn->group, 0 };
- struct sae_pt *pt = NULL;
- int ret;
-
- if (!wd)
- return -1;
-
- data = wpabuf_head_u8(wd);
- buf_len = wpabuf_len(wd);
-
- if (buf_len < 6) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
- buf_len);
- return -1;
- }
-
- alg = WPA_GET_LE16(data);
- seq = WPA_GET_LE16(data + 2);
- status = WPA_GET_LE16(data + 4);
-
- wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u",
- alg, seq, status);
-
- if (alg != WLAN_AUTH_SAE || seq != 1 ||
- status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
- wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit");
- return -1;
- }
-
- sae_clear_data(&pasn->sae);
- pasn->sae.state = SAE_NOTHING;
-
- ret = sae_set_group(&pasn->sae, pasn->group);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
- return -1;
- }
-
- password = sae_get_password(hapd, sta, NULL, NULL, &pt, NULL);
- if (!password || !pt) {
- wpa_printf(MSG_DEBUG, "PASN: No SAE PT found");
- return -1;
- }
-
- ret = sae_prepare_commit_pt(&pasn->sae, pt, hapd->own_addr, sta->addr,
- NULL, NULL);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
- return -1;
- }
-
- res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0,
- groups, 0);
- if (res != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit");
- return -1;
- }
-
- /* Process the commit message and derive the PMK */
- ret = sae_process_commit(&pasn->sae);
- if (ret) {
- wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
- return -1;
- }
-
- pasn->sae.state = SAE_COMMITTED;
-
- return 0;
-}
-
-
-static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd,
- struct sta_info *sta,
- struct wpabuf *wd)
-{
- struct pasn_data *pasn = sta->pasn;
- const u8 *data;
- size_t buf_len;
- u16 res, alg, seq, status;
-
- if (!wd)
- return -1;
-
- data = wpabuf_head_u8(wd);
- buf_len = wpabuf_len(wd);
-
- if (buf_len < 6) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
- buf_len);
- return -1;
- }
-
- alg = WPA_GET_LE16(data);
- seq = WPA_GET_LE16(data + 2);
- status = WPA_GET_LE16(data + 4);
-
- wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
- alg, seq, status);
-
- if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
- return -1;
- }
-
- res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6);
- if (res != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
- return -1;
- }
-
- pasn->sae.state = SAE_ACCEPTED;
-
- /*
- * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with
- * PASN/SAE should only be allowed with future PASN only. For now do not
- * restrict this only for PASN.
- */
- wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
- pasn->sae.pmk, pasn->sae.pmk_len,
- pasn->sae.pmkid, pasn->sae.akmp);
- return 0;
-}
-
-
-static struct wpabuf * pasn_get_sae_wd(struct hostapd_data *hapd,
- struct sta_info *sta)
-{
- struct pasn_data *pasn = sta->pasn;
- struct wpabuf *buf = NULL;
- u8 *len_ptr;
- size_t len;
-
- /* Need to add the entire Authentication frame body */
- buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN);
- if (!buf) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
- return NULL;
- }
-
- /* Need to add the entire authentication frame body for the commit */
- len_ptr = wpabuf_put(buf, 2);
- wpabuf_put_le16(buf, WLAN_AUTH_SAE);
- wpabuf_put_le16(buf, 1);
- wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
-
- /* Write the actual commit and update the length accordingly */
- sae_write_commit(&pasn->sae, buf, NULL, 0);
- len = wpabuf_len(buf);
- WPA_PUT_LE16(len_ptr, len - 2);
-
- /* Need to add the entire Authentication frame body for the confirm */
- len_ptr = wpabuf_put(buf, 2);
- wpabuf_put_le16(buf, WLAN_AUTH_SAE);
- wpabuf_put_le16(buf, 2);
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
- sae_write_confirm(&pasn->sae, buf);
- WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2);
-
- pasn->sae.state = SAE_CONFIRMED;
-
- return buf;
-}
-
-#endif /* CONFIG_SAE */
-
-
#ifdef CONFIG_FILS
-static struct wpabuf * pasn_get_fils_wd(struct hostapd_data *hapd,
- struct sta_info *sta)
-{
- struct pasn_data *pasn = sta->pasn;
- struct pasn_fils_data *fils = &pasn->fils;
- struct wpabuf *buf = NULL;
-
- if (!fils->erp_resp) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp");
- return NULL;
- }
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- return NULL;
-
- /* Add the authentication algorithm */
- wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
-
- /* Authentication Transaction seq# */
- wpabuf_put_le16(buf, 2);
-
- /* Status Code */
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
- /* Own RSNE */
- wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
-
- /* FILS Nonce */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
- wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
- wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN);
-
- /* FILS Session */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
- wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
- wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN);
-
- /* Wrapped Data */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp));
- wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
- wpabuf_put_buf(buf, fils->erp_resp);
-
- return buf;
-}
-
-
static void pasn_fils_auth_resp(struct hostapd_data *hapd,
struct sta_info *sta, u16 status,
struct wpabuf *erp_resp,
const u8 *msk, size_t msk_len)
{
struct pasn_data *pasn = sta->pasn;
- struct pasn_fils_data *fils = &pasn->fils;
+ struct pasn_fils *fils = &pasn->fils;
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
int ret;
@@ -2682,7 +2367,8 @@
pasn->secret = NULL;
fils->erp_resp = erp_resp;
- ret = handle_auth_pasn_resp(hapd, sta, NULL, WLAN_STATUS_SUCCESS);
+ ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
+ WLAN_STATUS_SUCCESS);
fils->erp_resp = NULL;
if (ret) {
@@ -2705,7 +2391,7 @@
return -1;
#else /* CONFIG_NO_RADIUS */
struct pasn_data *pasn = sta->pasn;
- struct pasn_fils_data *fils = &pasn->fils;
+ struct pasn_fils *fils = &pasn->fils;
struct ieee802_11_elems elems;
struct wpa_ie_data rsne_data;
struct wpabuf *fils_wd;
@@ -2756,7 +2442,7 @@
}
if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
- !elems.wrapped_data) {
+ !elems.wrapped_data || !elems.fils_session) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
return -1;
}
@@ -2764,7 +2450,7 @@
ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
&rsne_data);
if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE");
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
return -1;
}
@@ -2824,36 +2510,45 @@
#endif /* CONFIG_FILS */
-static struct wpabuf * pasn_get_wrapped_data(struct hostapd_data *hapd,
- struct sta_info *sta)
+static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+ int noack, unsigned int freq, unsigned int wait)
{
- switch (sta->pasn->akmp) {
- case WPA_KEY_MGMT_PASN:
- /* no wrapped data */
- return NULL;
- case WPA_KEY_MGMT_SAE:
-#ifdef CONFIG_SAE
- return pasn_get_sae_wd(hapd, sta);
-#else /* CONFIG_SAE */
- wpa_printf(MSG_ERROR,
- "PASN: SAE: Cannot derive wrapped data");
- return NULL;
-#endif /* CONFIG_SAE */
- case WPA_KEY_MGMT_FILS_SHA256:
- case WPA_KEY_MGMT_FILS_SHA384:
-#ifdef CONFIG_FILS
- return pasn_get_fils_wd(hapd, sta);
-#endif /* CONFIG_FILS */
- /* fall through */
- case WPA_KEY_MGMT_FT_PSK:
- case WPA_KEY_MGMT_FT_IEEE8021X:
- case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
- default:
- wpa_printf(MSG_ERROR,
- "PASN: TODO: Wrapped data for akmp=0x%x",
- sta->pasn->akmp);
- return NULL;
- }
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0);
+}
+
+
+static void hapd_initialize_pasn(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct pasn_data *pasn = sta->pasn;
+
+ pasn->cb_ctx = hapd;
+ pasn->send_mgmt = hapd_pasn_send_mlme;
+ pasn->pasn_groups = hapd->conf->pasn_groups;
+ pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+ pasn->rsn_pairwise = hapd->conf->rsn_pairwise;
+ pasn->derive_kdk = hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+#ifdef CONFIG_TESTING_OPTIONS
+ pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
+ if (hapd->conf->force_kdk_derivation)
+ pasn->derive_kdk = true;
+#endif /* CONFIG_TESTING_OPTIONS */
+ pasn->use_anti_clogging = use_anti_clogging(hapd);
+ pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt,
+ NULL);
+ pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
+ pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+ pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
+ pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth);
+
+ pasn->comeback_after = hapd->conf->pasn_comeback_after;
+ pasn->comeback_idx = hapd->comeback_idx;
+ pasn->comeback_key = hapd->comeback_key;
+ pasn->comeback_pending_idx = hapd->comeback_pending_idx;
+ os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN);
}
@@ -2889,582 +2584,16 @@
}
-static int
-pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *cached_pmk, size_t cached_pmk_len,
- struct wpa_pasn_params_data *pasn_data,
- struct wpabuf *wrapped_data,
- struct wpabuf *secret)
+static void hapd_pasn_update_params(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
{
- static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
- u8 pmk[PMK_LEN_MAX];
- u8 pmk_len;
- int ret;
-
- os_memset(pmk, 0, sizeof(pmk));
- pmk_len = 0;
-
- if (!cached_pmk || !cached_pmk_len)
- wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry");
-
- if (sta->pasn->akmp == WPA_KEY_MGMT_PASN) {
- wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
-
- pmk_len = WPA_PASN_PMK_LEN;
- os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk));
- } else if (cached_pmk && cached_pmk_len) {
- wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry");
-
- pmk_len = cached_pmk_len;
- os_memcpy(pmk, cached_pmk, cached_pmk_len);
- } else {
- switch (sta->pasn->akmp) {
-#ifdef CONFIG_SAE
- case WPA_KEY_MGMT_SAE:
- if (sta->pasn->sae.state == SAE_COMMITTED) {
- pmk_len = PMK_LEN;
- os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN);
- break;
- }
-#endif /* CONFIG_SAE */
- /* fall through */
- default:
- /* TODO: Derive PMK based on wrapped data */
- wpa_printf(MSG_DEBUG,
- "PASN: Missing PMK derivation");
- return -1;
- }
- }
-
- ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
- wpabuf_head(secret), wpabuf_len(secret),
- &sta->pasn->ptk, sta->pasn->akmp,
- sta->pasn->cipher, sta->pasn->kdk_len);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
- return -1;
- }
-
- if (sta->pasn->secure_ltf) {
- ret = wpa_ltf_keyseed(&sta->pasn->ptk, sta->pasn->akmp,
- sta->pasn->cipher);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed to derive LTF keyseed");
- return -1;
- }
- }
-
- wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
- return 0;
-}
-
-
-static void handle_auth_pasn_comeback(struct hostapd_data *hapd,
- struct sta_info *sta, u16 group)
-{
- struct wpabuf *buf, *comeback;
- int ret;
-
- wpa_printf(MSG_DEBUG,
- "PASN: Building comeback frame 2. Comeback after=%u",
- hapd->conf->pasn_comeback_after);
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- return;
-
- wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
- sta->addr, 2,
- WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY);
-
- /*
- * Do not include the group as a part of the token since it is not going
- * to be used.
- */
- comeback = auth_build_token_req(hapd, 0, sta->addr, 0);
- if (!comeback) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed sending auth with comeback");
- wpabuf_free(buf);
- return;
- }
-
- wpa_pasn_add_parameter_ie(buf, group,
- WPA_PASN_WRAPPED_DATA_NO,
- NULL, 0, comeback,
- hapd->conf->pasn_comeback_after);
- wpabuf_free(comeback);
-
- wpa_printf(MSG_DEBUG,
- "PASN: comeback: STA=" MACSTR, MAC2STR(sta->addr));
-
- ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
- NULL, 0, 0);
- if (ret)
- wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2");
-
- wpabuf_free(buf);
-}
-
-
-static int handle_auth_pasn_resp(struct hostapd_data *hapd,
- struct sta_info *sta,
- struct rsn_pmksa_cache_entry *pmksa,
- u16 status)
-{
- struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
- u8 mic[WPA_PASN_MAX_MIC_LEN];
- u8 mic_len;
- u8 *ptr;
- const u8 *frame, *data, *rsn_ie, *rsnxe_ie;
- u8 *data_buf = NULL;
- size_t rsn_ie_len, frame_len, data_len;
- int ret;
- const u8 *pmkid = NULL;
-
- wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status);
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- goto fail;
-
- wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
- sta->addr, 2, status);
-
- if (status != WLAN_STATUS_SUCCESS)
- goto done;
-
- if (pmksa) {
- pmkid = pmksa->pmkid;
-#ifdef CONFIG_SAE
- } else if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
- wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID");
- pmkid = sta->pasn->sae.pmkid;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
- } else if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
- sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
- wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID");
- pmkid = sta->pasn->fils.erp_pmkid;
-#endif /* CONFIG_FILS */
- }
-
- if (wpa_pasn_add_rsne(buf, pmkid,
- sta->pasn->akmp, sta->pasn->cipher) < 0)
- goto fail;
-
- /* No need to derive PMK if PMKSA is given */
- if (!pmksa)
- wrapped_data_buf = pasn_get_wrapped_data(hapd, sta);
- else
- sta->pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO;
-
- /* Get public key */
- pubkey = crypto_ecdh_get_pubkey(sta->pasn->ecdh, 0);
- pubkey = wpabuf_zeropad(pubkey,
- crypto_ecdh_prime_len(sta->pasn->ecdh));
- if (!pubkey) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
- goto fail;
- }
-
- wpa_pasn_add_parameter_ie(buf, sta->pasn->group,
- sta->pasn->wrapped_data_format,
- pubkey, true, NULL, 0);
-
- if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
- goto fail;
-
- wpabuf_free(wrapped_data_buf);
- wrapped_data_buf = NULL;
- wpabuf_free(pubkey);
- pubkey = NULL;
-
- /* Add RSNXE if needed */
- rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
- if (rsnxe_ie)
- wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
-
- /* Add the mic */
- mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
- wpabuf_put_u8(buf, WLAN_EID_MIC);
- wpabuf_put_u8(buf, mic_len);
- ptr = wpabuf_put(buf, mic_len);
-
- os_memset(ptr, 0, mic_len);
-
- frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
- frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
-
- rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &rsn_ie_len);
- if (!rsn_ie || !rsn_ie_len)
- goto fail;
-
- /*
- * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also
- * MDE, etc. Thus, do not use the returned length but instead use the
- * length specified in the IE header.
- */
- data_len = rsn_ie[1] + 2;
- if (rsnxe_ie) {
- data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2);
- if (!data_buf)
- goto fail;
-
- os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2);
- os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2);
- data_len += rsnxe_ie[1] + 2;
- data = data_buf;
- } else {
- data = rsn_ie;
- }
-
- ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
- hapd->own_addr, sta->addr, data, data_len,
- frame, frame_len, mic);
- os_free(data_buf);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation");
- goto fail;
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (hapd->conf->pasn_corrupt_mic) {
- wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC");
- mic[0] = ~mic[0];
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- os_memcpy(ptr, mic, mic_len);
-
-done:
- wpa_printf(MSG_DEBUG,
- "PASN: Building frame 2: success; resp STA=" MACSTR,
- MAC2STR(sta->addr));
-
- ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
- NULL, 0, 0);
- if (ret)
- wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
-
- wpabuf_free(buf);
- return ret;
-fail:
- wpabuf_free(wrapped_data_buf);
- wpabuf_free(pubkey);
- wpabuf_free(buf);
- return -1;
-}
-
-
-static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta,
- const struct ieee80211_mgmt *mgmt, size_t len)
-{
+ struct pasn_data *pasn = sta->pasn;
struct ieee802_11_elems elems;
struct wpa_ie_data rsn_data;
struct wpa_pasn_params_data pasn_params;
- struct rsn_pmksa_cache_entry *pmksa = NULL;
- const u8 *cached_pmk = NULL;
- size_t cached_pmk_len = 0;
-#ifdef CONFIG_IEEE80211R_AP
- u8 pmk_r1[PMK_LEN_MAX];
- size_t pmk_r1_len;
-#endif /* CONFIG_IEEE80211R_AP */
- struct wpabuf *wrapped_data = NULL, *secret = NULL;
- const int *groups = hapd->conf->pasn_groups;
- static const int default_groups[] = { 19, 0 };
- u16 status = WLAN_STATUS_SUCCESS;
- int ret, inc_y;
- bool derive_keys;
- u32 i;
- bool derive_kdk;
-
- if (!groups)
- groups = default_groups;
-
- if (ieee802_11_parse_elems(mgmt->u.auth.variable,
- len - offsetof(struct ieee80211_mgmt,
- u.auth.variable),
- &elems, 0) == ParseFailed) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed parsing Authentication frame");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
- &rsn_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
- status = WLAN_STATUS_INVALID_RSNIE;
- goto send_resp;
- }
-
- ret = wpa_pasn_validate_rsne(&rsn_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
- status = WLAN_STATUS_INVALID_RSNIE;
- goto send_resp;
- }
-
- if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) ||
- !(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) {
- wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
- status = WLAN_STATUS_INVALID_RSNIE;
- goto send_resp;
- }
-
- sta->pasn->akmp = rsn_data.key_mgmt;
- sta->pasn->cipher = rsn_data.pairwise_cipher;
-
- derive_kdk = (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) &&
- ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
- WLAN_RSNX_CAPAB_SECURE_LTF);
-#ifdef CONFIG_TESTING_OPTIONS
- if (!derive_kdk)
- derive_kdk = hapd->conf->force_kdk_derivation;
-#endif /* CONFIG_TESTING_OPTIONS */
- if (derive_kdk)
- sta->pasn->kdk_len = WPA_KDK_MAX_LEN;
- else
- sta->pasn->kdk_len = 0;
- wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", sta->pasn->kdk_len);
-
- if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) &&
- ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
- WLAN_RSNX_CAPAB_SECURE_LTF))
- sta->pasn->secure_ltf = true;
- else
- sta->pasn->secure_ltf = false;
-
- if (!elems.pasn_params || !elems.pasn_params_len) {
- wpa_printf(MSG_DEBUG,
- "PASN: No PASN Parameters element found");
- status = WLAN_STATUS_INVALID_PARAMETERS;
- goto send_resp;
- }
-
- ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
- elems.pasn_params_len + 3,
- false, &pasn_params);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed validation of PASN Parameters IE");
- status = WLAN_STATUS_INVALID_PARAMETERS;
- goto send_resp;
- }
-
- for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++)
- ;
-
- if (!pasn_params.group || groups[i] != pasn_params.group) {
- wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed",
- pasn_params.group);
- status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
- goto send_resp;
- }
-
- if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
- wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- if (pasn_params.comeback) {
- wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token");
-
- ret = check_comeback_token(hapd, sta->addr,
- pasn_params.comeback,
- pasn_params.comeback_len);
-
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
- } else if (use_anti_clogging(hapd)) {
- wpa_printf(MSG_DEBUG, "PASN: Respond with comeback");
- handle_auth_pasn_comeback(hapd, sta, pasn_params.group);
- ap_free_sta(hapd, sta);
- return;
- }
-
- sta->pasn->ecdh = crypto_ecdh_init(pasn_params.group);
- if (!sta->pasn->ecdh) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- sta->pasn->group = pasn_params.group;
-
- if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
- inc_y = 1;
- } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
- pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
- inc_y = 0;
- } else {
- wpa_printf(MSG_DEBUG,
- "PASN: Invalid first octet in pubkey=0x%x",
- pasn_params.pubkey[0]);
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, inc_y,
- pasn_params.pubkey + 1,
- pasn_params.pubkey_len - 1);
- if (!secret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- derive_keys = true;
- if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems,
- WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
- if (!wrapped_data) {
- wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
-#ifdef CONFIG_SAE
- if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
- ret = pasn_wd_handle_sae_commit(hapd, sta,
- wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed processing SAE commit");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
- }
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
- if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
- sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
- ret = pasn_wd_handle_fils(hapd, sta, wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed processing FILS wrapped data");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- wpa_printf(MSG_DEBUG,
- "PASN: FILS: Pending AS response");
-
- /*
- * With PASN/FILS, keys can be derived only after a
- * response from the AS is processed.
- */
- derive_keys = false;
- }
-#endif /* CONFIG_FILS */
- }
-
- sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format;
-
- ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
- ((const u8 *) mgmt) + IEEE80211_HDRLEN,
- len - IEEE80211_HDRLEN, sta->pasn->hash);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- if (!derive_keys) {
- wpa_printf(MSG_DEBUG, "PASN: Storing secret");
- sta->pasn->secret = secret;
- wpabuf_free(wrapped_data);
- return;
- }
-
- if (rsn_data.num_pmkid) {
- if (wpa_key_mgmt_ft(sta->pasn->akmp)) {
-#ifdef CONFIG_IEEE80211R_AP
- wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1");
-
- ret = wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
- rsn_data.pmkid,
- pmk_r1, &pmk_r1_len, NULL,
- NULL, NULL, NULL,
- NULL, NULL, NULL);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: FT: Failed getting PMK-R1");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
- cached_pmk = pmk_r1;
- cached_pmk_len = pmk_r1_len;
-#else /* CONFIG_IEEE80211R_AP */
- wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
-#endif /* CONFIG_IEEE80211R_AP */
- } else {
- wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry");
-
- pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
- rsn_data.pmkid);
- if (pmksa) {
- cached_pmk = pmksa->pmk;
- cached_pmk_len = pmksa->pmk_len;
- }
- }
- } else {
- wpa_printf(MSG_DEBUG, "PASN: No PMKID specified");
- }
-
- ret = pasn_derive_keys(hapd, sta, cached_pmk, cached_pmk_len,
- &pasn_params, wrapped_data, secret);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
- ((const u8 *) mgmt) + IEEE80211_HDRLEN,
- len - IEEE80211_HDRLEN, sta->pasn->hash);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
-
-send_resp:
- ret = handle_auth_pasn_resp(hapd, sta, pmksa, status);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to send response");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- } else {
- wpa_printf(MSG_DEBUG,
- "PASN: Success handling transaction == 1");
- }
-
- wpabuf_free(secret);
- wpabuf_free(wrapped_data);
-
- if (status != WLAN_STATUS_SUCCESS)
- ap_free_sta(hapd, sta);
-}
-
-
-static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta,
- const struct ieee80211_mgmt *mgmt, size_t len)
-{
- struct ieee802_11_elems elems;
- struct wpa_pasn_params_data pasn_params;
struct wpabuf *wrapped_data = NULL;
- u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
- u8 mic_len;
- int ret;
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
len - offsetof(struct ieee80211_mgmt,
@@ -3472,100 +2601,62 @@
&elems, 0) == ParseFailed) {
wpa_printf(MSG_DEBUG,
"PASN: Failed parsing Authentication frame");
- goto fail;
+ return;
}
- /* Check that the MIC IE exists. Save it and zero out the memory. */
- mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
- if (!elems.mic || elems.mic_len != mic_len) {
+ if (!elems.rsn_ie ||
+ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data)) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+ return;
+ }
+
+ if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
+ !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+ return;
+ }
+
+ pasn->akmp = rsn_data.key_mgmt;
+ pasn->cipher = rsn_data.pairwise_cipher;
+
+ if (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) {
+#ifdef CONFIG_IEEE80211R_AP
+ pasn->pmk_r1_len = 0;
+ wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
+ rsn_data.pmkid,
+ pasn->pmk_r1, &pasn->pmk_r1_len, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+#endif /* CONFIG_IEEE80211R_AP */
+ }
+#ifdef CONFIG_FILS
+ if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 &&
+ pasn->akmp != WPA_KEY_MGMT_FILS_SHA384)
+ return;
+ if (!elems.pasn_params ||
+ wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+ elems.pasn_params_len + 3,
+ false, &pasn_params)) {
wpa_printf(MSG_DEBUG,
- "PASN: Invalid MIC. Expecting len=%u", mic_len);
- goto fail;
- } else {
- os_memcpy(mic, elems.mic, mic_len);
- /* TODO: Clean this up.. Should not modify received frame
- * buffer. */
- os_memset((u8 *) elems.mic, 0, mic_len);
+ "PASN: Failed validation of PASN Parameters element");
+ return;
}
-
- if (!elems.pasn_params || !elems.pasn_params_len) {
- wpa_printf(MSG_DEBUG,
- "PASN: No PASN Parameters element found");
- goto fail;
- }
-
- ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
- elems.pasn_params_len + 3,
- false, &pasn_params);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed validation of PASN Parameters IE");
- goto fail;
- }
-
- if (pasn_params.pubkey || pasn_params.pubkey_len) {
- wpa_printf(MSG_DEBUG,
- "PASN: Public key should not be included");
- goto fail;
- }
-
- /* Verify the MIC */
- ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
- sta->addr, hapd->own_addr,
- sta->pasn->hash, mic_len * 2,
- (u8 *) &mgmt->u.auth,
- len - offsetof(struct ieee80211_mgmt, u.auth),
- out_mic);
-
- wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
- if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
- wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
- goto fail;
- }
-
if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems,
- WLAN_EID_EXTENSION,
+ wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
WLAN_EID_EXT_WRAPPED_DATA);
-
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
- goto fail;
+ return;
}
-
-#ifdef CONFIG_SAE
- if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
- ret = pasn_wd_handle_sae_confirm(hapd, sta,
- wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed processing SAE confirm");
- wpabuf_free(wrapped_data);
- goto fail;
- }
- }
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
- if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
- sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
- if (wrapped_data) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS: Ignore wrapped data");
- }
- }
-#endif /* CONFIG_FILS */
- wpabuf_free(wrapped_data);
+ if (pasn_wd_handle_fils(hapd, sta, wrapped_data))
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing FILS wrapped data");
+ else
+ pasn->fils_wd_valid = true;
}
-
- wpa_printf(MSG_INFO,
- "PASN: Success handling transaction == 3. Store PTK");
-
- ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
- sta->pasn->cipher, 43200, &sta->pasn->ptk, NULL, NULL);
- pasn_set_keys_from_cache(hapd, hapd->own_addr, sta->addr,
- sta->pasn->cipher, sta->pasn->akmp);
-fail:
- ap_free_sta(hapd, sta);
+ wpabuf_free(wrapped_data);
+#endif /* CONFIG_FILS */
}
@@ -3601,7 +2692,12 @@
return;
}
- handle_auth_pasn_1(hapd, sta, mgmt, len);
+ hapd_initialize_pasn(hapd, sta);
+
+ hapd_pasn_update_params(hapd, sta, mgmt, len);
+ if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
+ sta->addr, mgmt, len) < 0)
+ ap_free_sta(hapd, sta);
} else if (trans_seq == 3) {
if (!sta->pasn) {
wpa_printf(MSG_DEBUG,
@@ -3616,7 +2712,18 @@
return;
}
- handle_auth_pasn_3(hapd, sta, mgmt, len);
+ if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
+ sta->addr, mgmt, len) == 0) {
+ ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
+ sta->pasn->cipher, 43200,
+ &sta->pasn->ptk, NULL, NULL,
+ sta->pasn->akmp);
+
+ pasn_set_keys_from_cache(hapd, hapd->own_addr,
+ sta->addr, sta->pasn->cipher,
+ sta->pasn->akmp);
+ }
+ ap_free_sta(hapd, sta);
} else {
wpa_printf(MSG_DEBUG,
"PASN: Invalid transaction %u - ignore", trans_seq);
@@ -4036,6 +3143,23 @@
}
+static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
+{
+ size_t num_bss_nontx;
+ u8 max_bssid_ind = 0;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
+ return 0;
+
+ num_bss_nontx = hapd->iface->num_bss - 1;
+ while (num_bss_nontx > 0) {
+ max_bssid_ind++;
+ num_bss_nontx >>= 1;
+ }
+ return max_bssid_ind;
+}
+
+
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
{
int i, j = 32, aid;
@@ -4061,7 +3185,7 @@
}
if (j == 32)
return -1;
- aid = i * 32 + j + 1;
+ aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd));
if (aid > 2007)
return -1;
@@ -4605,6 +3729,15 @@
elems.he_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+
+ if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station does not support mandatory HE PHY - reject association");
+ return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ }
+
if (is_6ghz_op_class(hapd->iconf->op_class)) {
if (!(sta->flags & WLAN_STA_HE)) {
hostapd_logger(hapd, sta->addr,
@@ -4770,7 +3903,7 @@
return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
}
- if (hapd->conf->sae_pwe == 2 &&
+ if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
sta->auth_alg == WLAN_AUTH_SAE &&
sta->sae && !sta->sae->h2e &&
ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
@@ -5251,7 +4384,7 @@
}
#endif /* CONFIG_IEEE80211AX */
- p = hostapd_eid_ext_capab(hapd, p);
+ p = hostapd_eid_ext_capab(hapd, p, false);
p = hostapd_eid_bss_max_idle_period(hapd, p);
if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
@@ -6759,6 +5892,19 @@
return;
}
+#ifdef CONFIG_HS20
+ if (ok && len >= IEEE80211_HDRLEN + 2 &&
+ mgmt->u.action.category == WLAN_ACTION_WNM &&
+ mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ &&
+ sta->hs20_deauth_on_ack) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR
+ " on acknowledging the WNM-Notification",
+ MAC2STR(sta->addr));
+ ap_sta_session_timeout(hapd, sta, 0);
+ return;
+ }
+#endif /* CONFIG_HS20 */
+
if (len < 24 + 5 + sizeof(*report))
return;
report = (const struct rrm_measurement_report_element *)
@@ -7524,6 +6670,14 @@
reporting_hapd->conf->ssid.short_ssid)
bss_param |= RNR_BSS_PARAM_SAME_SSID;
+ if (iface->conf->mbssid != MBSSID_DISABLED &&
+ iface->num_bss > 1) {
+ bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+ if (i == 0)
+ bss_param |=
+ RNR_BSS_PARAM_TRANSMITTED_BSSID;
+ }
+
if (is_6ghz_op_class(hapd->iconf->op_class) &&
bss->conf->unsol_bcast_probe_resp_interval)
bss_param |=
@@ -7612,4 +6766,253 @@
return eid;
}
+
+static bool mbssid_known_bss(unsigned int i, const u8 *known_bss,
+ size_t known_bss_len)
+{
+ if (!known_bss || known_bss_len <= i / 8)
+ return false;
+ known_bss = &known_bss[i / 8];
+ return *known_bss & (u8) (BIT(i % 8));
+}
+
+
+static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+ u32 frame_type, size_t *bss_index,
+ const u8 *known_bss,
+ size_t known_bss_len)
+{
+ struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ size_t len = 3, i;
+
+ for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+ const u8 *auth, *rsn = NULL, *rsnx = NULL;
+ size_t nontx_profile_len, auth_len;
+ u8 ie_count = 0;
+
+ if (!bss || !bss->conf || !bss->started ||
+ mbssid_known_bss(i, known_bss, known_bss_len))
+ continue;
+
+ /*
+ * Sublement ID: 1 octet
+ * Length: 1 octet
+ * Nontransmitted capabilities: 4 octets
+ * SSID element: 2 + variable
+ * Multiple BSSID Index Element: 3 octets (+2 octets in beacons)
+ * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
+ */
+ nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
+
+ if (frame_type == WLAN_FC_STYPE_BEACON)
+ nontx_profile_len += 2;
+
+ auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+ if (auth) {
+ rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+ if (rsn)
+ nontx_profile_len += 2 + rsn[1];
+
+ rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+ if (rsnx)
+ nontx_profile_len += 2 + rsnx[1];
+ }
+ if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+ ie_count++;
+ if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+ ie_count++;
+ if (bss->conf->xrates_supported)
+ nontx_profile_len += 8;
+ else if (hapd->conf->xrates_supported)
+ ie_count++;
+ if (ie_count)
+ nontx_profile_len += 4 + ie_count;
+
+ if (len + nontx_profile_len > 255)
+ break;
+
+ len += nontx_profile_len;
+ }
+
+ *bss_index = i;
+ return len;
+}
+
+
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+ u8 *elem_count, const u8 *known_bss,
+ size_t known_bss_len)
+{
+ size_t len = 0, bss_index = 1;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_type != WLAN_FC_STYPE_BEACON &&
+ frame_type != WLAN_FC_STYPE_PROBE_RESP))
+ return 0;
+
+ if (frame_type == WLAN_FC_STYPE_BEACON) {
+ if (!elem_count) {
+ wpa_printf(MSG_INFO,
+ "MBSSID: Insufficient data for Beacon frames");
+ return 0;
+ }
+ *elem_count = 0;
+ }
+
+ while (bss_index < hapd->iface->num_bss) {
+ len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
+ &bss_index, known_bss,
+ known_bss_len);
+
+ if (frame_type == WLAN_FC_STYPE_BEACON)
+ *elem_count += 1;
+ }
+ return len;
+}
+
+
+static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ u32 frame_type, u8 max_bssid_indicator,
+ size_t *bss_index, u8 elem_count,
+ const u8 *known_bss, size_t known_bss_len)
+{
+ struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ size_t i;
+ u8 *eid_len_offset, *max_bssid_indicator_offset;
+
+ *eid++ = WLAN_EID_MULTIPLE_BSSID;
+ eid_len_offset = eid++;
+ max_bssid_indicator_offset = eid++;
+
+ for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+ struct hostapd_bss_config *conf;
+ u8 *eid_len_pos, *nontx_bss_start = eid;
+ const u8 *auth, *rsn = NULL, *rsnx = NULL;
+ u8 ie_count = 0, non_inherit_ie[3];
+ size_t auth_len = 0;
+ u16 capab_info;
+
+ if (!bss || !bss->conf || !bss->started ||
+ mbssid_known_bss(i, known_bss, known_bss_len))
+ continue;
+ conf = bss->conf;
+
+ *eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
+ eid_len_pos = eid++;
+
+ capab_info = hostapd_own_capab_info(bss);
+ *eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
+ *eid++ = sizeof(capab_info);
+ WPA_PUT_LE16(eid, capab_info);
+ eid += sizeof(capab_info);
+
+ *eid++ = WLAN_EID_SSID;
+ *eid++ = conf->ssid.ssid_len;
+ os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
+ eid += conf->ssid.ssid_len;
+
+ *eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
+ if (frame_type == WLAN_FC_STYPE_BEACON) {
+ *eid++ = 3;
+ *eid++ = i; /* BSSID Index */
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ (conf->dtim_period % elem_count))
+ conf->dtim_period = elem_count;
+ *eid++ = conf->dtim_period;
+ *eid++ = 0xFF; /* DTIM Count */
+ } else {
+ /* Probe Request frame does not include DTIM Period and
+ * DTIM Count fields. */
+ *eid++ = 1;
+ *eid++ = i; /* BSSID Index */
+ }
+
+ auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+ if (auth) {
+ rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+ if (rsn) {
+ os_memcpy(eid, rsn, 2 + rsn[1]);
+ eid += 2 + rsn[1];
+ }
+
+ rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+ if (rsnx) {
+ os_memcpy(eid, rsnx, 2 + rsnx[1]);
+ eid += 2 + rsnx[1];
+ }
+ }
+ if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+ non_inherit_ie[ie_count++] = WLAN_EID_RSN;
+ if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+ non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
+ if (hapd->conf->xrates_supported &&
+ !bss->conf->xrates_supported)
+ non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
+ if (ie_count) {
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 2 + ie_count;
+ *eid++ = WLAN_EID_EXT_NON_INHERITANCE;
+ *eid++ = ie_count;
+ os_memcpy(eid, non_inherit_ie, ie_count);
+ eid += ie_count;
+ }
+
+ *eid_len_pos = (eid - eid_len_pos) - 1;
+
+ if (((eid - eid_len_offset) - 1) > 255) {
+ eid = nontx_bss_start;
+ break;
+ }
+ }
+
+ *bss_index = i;
+ *max_bssid_indicator_offset = max_bssid_indicator;
+ if (*max_bssid_indicator_offset < 1)
+ *max_bssid_indicator_offset = 1;
+ *eid_len_offset = (eid - eid_len_offset) - 1;
+ return eid;
+}
+
+
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ unsigned int frame_stype, u8 elem_count,
+ u8 **elem_offset,
+ const u8 *known_bss, size_t known_bss_len)
+{
+ size_t bss_index = 1;
+ u8 elem_index = 0;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_stype != WLAN_FC_STYPE_BEACON &&
+ frame_stype != WLAN_FC_STYPE_PROBE_RESP))
+ return eid;
+
+ if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) {
+ wpa_printf(MSG_INFO,
+ "MBSSID: Insufficient data for Beacon frames");
+ return eid;
+ }
+
+ while (bss_index < hapd->iface->num_bss) {
+ if (frame_stype == WLAN_FC_STYPE_BEACON) {
+ if (elem_index == elem_count) {
+ wpa_printf(MSG_WARNING,
+ "MBSSID: Larger number of elements than there is room in the provided array");
+ break;
+ }
+
+ elem_offset[elem_index] = eid;
+ elem_index = elem_index + 1;
+ }
+ eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
+ hostapd_max_bssid_indicator(hapd),
+ &bss_index, elem_count,
+ known_bss, known_bss_len);
+ }
+
+ return eid;
+}
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index fa1f47b..5f443fc 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -45,7 +45,8 @@
#endif /* NEED_AP_MLME */
u16 hostapd_own_capab_info(struct hostapd_data *hapd);
void ap_ht2040_timeout(void *eloop_data, void *user_data);
-u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+ bool mbssid_complete);
u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
@@ -214,5 +215,12 @@
enum ieee80211_op_mode opmode,
const u8 *he_capab, size_t he_capab_len,
const u8 *eht_capab, size_t eht_capab_len);
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+ u8 *elem_count, const u8 *known_bss,
+ size_t known_bss_len);
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ unsigned int frame_stype, u8 elem_count,
+ u8 **elem_offset,
+ const u8 *known_bss, size_t known_bss_len);
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index ec36a9e..caaadce 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -42,11 +42,29 @@
static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
- const u8 *he_phy_cap,
+ u8 he_oper_chwidth, const u8 *he_phy_cap,
const u8 *eht_phy_cap)
{
u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
bool band24, band5, band6;
+ u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+
+ switch (he_oper_chwidth) {
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ he_phy_cap_chwidth |=
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_160MHZ:
+ he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_80MHZ:
+ case CONF_OPER_CHWIDTH_USE_HT:
+ he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ break;
+ }
+
+ he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
band24 = mode == HOSTAPD_MODE_IEEE80211B ||
mode == HOSTAPD_MODE_IEEE80211G ||
@@ -56,19 +74,18 @@
band6 = is_6ghz_op_class(opclass);
if (band24 &&
- (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
- HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
+ (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
if (band5 &&
- (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ (he_phy_cap_chwidth &
(HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
if (band5 &&
- (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ (he_phy_cap_chwidth &
(HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
@@ -98,6 +115,7 @@
return 0;
len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
eht_cap->phy_cap);
len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
@@ -152,6 +170,7 @@
mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
eht_cap->phy_cap);
if (mcs_nss_len) {
@@ -178,17 +197,24 @@
struct ieee80211_eht_operation *oper;
u8 *pos = eid, seg0 = 0, seg1 = 0;
enum oper_chan_width chwidth;
+ size_t elen = 1 + 4 + 3;
if (!hapd->iface->current_mode)
return eid;
*pos++ = WLAN_EID_EXTENSION;
- *pos++ = 5;
+ *pos++ = 1 + elen;
*pos++ = WLAN_EID_EXT_EHT_OPERATION;
oper = (struct ieee80211_eht_operation *) pos;
oper->oper_params = EHT_OPER_INFO_PRESENT;
+ /* TODO: Fill in appropriate EHT-MCS max Nss information */
+ oper->basic_eht_mcs_nss_set[0] = 0x11;
+ oper->basic_eht_mcs_nss_set[1] = 0x00;
+ oper->basic_eht_mcs_nss_set[2] = 0x00;
+ oper->basic_eht_mcs_nss_set[3] = 0x00;
+
if (is_6ghz_op_class(conf->op_class))
chwidth = op_class_to_ch_width(conf->op_class);
else
@@ -227,7 +253,7 @@
oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
oper->oper_info.ccfs1 = seg1;
- return pos + 4;
+ return pos + elen;
}
@@ -275,6 +301,7 @@
sta_mcs = capab->optional;
if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
mode->eht_capab[opmode].phy_cap) ==
EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
@@ -300,7 +327,7 @@
static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
- u8 opclass,
+ u8 opclass, u8 he_oper_chwidth,
const u8 *he_cap, const u8 *eht_cap,
size_t len)
{
@@ -317,8 +344,8 @@
if (len < cap_len)
return true;
- cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_phy_cap,
- cap->phy_cap);
+ cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
+ he_phy_cap, cap->phy_cap);
if (len < cap_len)
return true;
@@ -342,6 +369,7 @@
!he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
!eht_capab ||
ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
he_capab, eht_capab,
eht_capab_len) ||
!check_valid_eht_mcs(hapd, eht_capab, opmode)) {
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index eaeaec5..31dfb62 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -340,7 +340,8 @@
}
-static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx,
+ bool mbssid_complete)
{
*pos = 0x00;
@@ -364,6 +365,8 @@
*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
if (hapd->conf->bss_transition)
*pos |= 0x08; /* Bit 19 - BSS Transition */
+ if (hapd->iconf->mbssid)
+ *pos |= 0x40; /* Bit 22 - Multiple BSSID */
break;
case 3: /* Bits 24-31 */
#ifdef CONFIG_WNM_AP
@@ -435,6 +438,11 @@
(hapd->iface->drv_flags &
WPA_DRIVER_FLAGS_BEACON_PROTECTION))
*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED)
+ *pos |= 0x08; /* Bit 83 - Enhanced multiple BSSID */
+ if (mbssid_complete)
+ *pos |= 0x01; /* Bit 80 - Complete List of NonTxBSSID
+ * Profiles */
break;
case 11: /* Bits 88-95 */
#ifdef CONFIG_SAE_PK
@@ -448,7 +456,8 @@
}
-u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+ bool mbssid_complete)
{
u8 *pos = eid;
u8 len = EXT_CAPA_MAX_LEN, i;
@@ -459,7 +468,7 @@
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = len;
for (i = 0; i < len; i++, pos++) {
- hostapd_ext_capab_byte(hapd, pos, i);
+ hostapd_ext_capab_byte(hapd, pos, i, mbssid_complete);
if (i < hapd->iface->extended_capa_len) {
*pos &= ~hapd->iface->extended_capa_mask[i];
@@ -470,6 +479,13 @@
*pos &= ~hapd->conf->ext_capa_mask[i];
*pos |= hapd->conf->ext_capa[i];
}
+
+ /* Clear bits 83 and 22 if EMA and MBSSID are not enabled
+ * otherwise association fails with some clients */
+ if (i == 10 && hapd->iconf->mbssid < ENHANCED_MBSSID_ENABLED)
+ *pos &= ~0x08;
+ if (i == 2 && !hapd->iconf->mbssid)
+ *pos &= ~0x40;
}
while (len > 0 && eid[1 + len] == 0) {
@@ -1062,10 +1078,11 @@
#endif /* CONFIG_SAE_PK */
if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
- (hapd->conf->sae_pwe == 1 || hapd->conf->sae_pwe == 2 ||
+ (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ hapd->conf->sae_pwe == SAE_PWE_BOTH ||
hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk ||
wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt)) &&
- hapd->conf->sae_pwe != 3) {
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (sae_pk)
@@ -1078,7 +1095,7 @@
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
- capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
flen = (capab & 0xff00) ? 2 : 1;
if (len < 2 + flen || !capab)
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index d90792c..46a47d0 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1709,23 +1709,35 @@
static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
- struct sta_info *sta, u8 *pos,
+ struct sta_info *sta, const u8 *pos,
size_t len)
{
+ size_t url_len;
+ unsigned int timeout;
+
if (len < 3)
return; /* Malformed information */
+ url_len = len - 3;
sta->hs20_deauth_requested = 1;
+ sta->hs20_deauth_on_ack = url_len == 0;
wpa_printf(MSG_DEBUG,
- "HS 2.0: Deauthentication request - Code %u Re-auth Delay %u",
- *pos, WPA_GET_LE16(pos + 1));
+ "HS 2.0: Deauthentication request - Code %u Re-auth Delay %u URL length %zu",
+ *pos, WPA_GET_LE16(pos + 1), url_len);
wpabuf_free(sta->hs20_deauth_req);
sta->hs20_deauth_req = wpabuf_alloc(len + 1);
if (sta->hs20_deauth_req) {
wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
- wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
- wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+ wpabuf_put_u8(sta->hs20_deauth_req, url_len);
+ wpabuf_put_data(sta->hs20_deauth_req, pos + 3, url_len);
}
- ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+ timeout = hapd->conf->hs20_deauth_req_timeout;
+ /* If there is no URL, no need to provide time to fetch it. Use a short
+ * timeout here to allow maximum time for completing 4-way handshake and
+ * WNM-Notification delivery. Acknowledgement of the frame will result
+ * in cutting this wait further. */
+ if (!url_len && timeout > 2)
+ timeout = 2;
+ ap_sta_session_timeout(hapd, sta, timeout);
}
@@ -1813,6 +1825,7 @@
buf = NULL;
sta->remediation = 0;
sta->hs20_deauth_requested = 0;
+ sta->hs20_deauth_on_ack = 0;
for (;;) {
if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index b67b852..32d291d 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -40,6 +40,7 @@
{
os_free(entry->vlan_desc);
os_free(entry->identity);
+ os_free(entry->dpp_pkhash);
wpabuf_free(entry->cui);
#ifndef CONFIG_NO_RADIUS
radius_free_class(&entry->radius_class);
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 2ef2174..e3cee4a 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -23,6 +23,8 @@
int akmp; /* WPA_KEY_MGMT_* */
u8 spa[ETH_ALEN];
+ u8 *dpp_pkhash; /* SHA256_MAC_LEN octet hash value of DPP Connector
+ * public key */
u8 *identity;
size_t identity_len;
struct wpabuf *cui;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index c541926..63f514c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1260,6 +1260,13 @@
}
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return wpa_auth_get_dpp_pkhash(sta->wpa_sm);
+}
+
+
void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
int authorized)
{
@@ -1298,10 +1305,13 @@
sta->addr, authorized, dev_addr);
if (authorized) {
+ const u8 *dpp_pkhash;
const char *keyid;
+ char dpp_pkhash_buf[100];
char keyid_buf[100];
char ip_addr[100];
+ dpp_pkhash_buf[0] = '\0';
keyid_buf[0] = '\0';
ip_addr[0] = '\0';
#ifdef CONFIG_P2P
@@ -1319,14 +1329,27 @@
" keyid=%s", keyid);
}
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s",
- buf, ip_addr, keyid_buf);
+ dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+ if (dpp_pkhash) {
+ const char *prefix = " dpp_pkhash=";
+ size_t plen = os_strlen(prefix);
+
+ os_strlcpy(dpp_pkhash_buf, prefix,
+ sizeof(dpp_pkhash_buf));
+ wpa_snprintf_hex(&dpp_pkhash_buf[plen],
+ sizeof(dpp_pkhash_buf) - plen,
+ dpp_pkhash, SHA256_MAC_LEN);
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+ buf, ip_addr, keyid_buf, dpp_pkhash_buf);
if (hapd->msg_ctx_parent &&
hapd->msg_ctx_parent != hapd->msg_ctx)
wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_CONNECTED "%s%s%s",
- buf, ip_addr, keyid_buf);
+ AP_STA_CONNECTED "%s%s%s%s",
+ buf, ip_addr, keyid_buf,
+ dpp_pkhash_buf);
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index d2a8344..b59b758 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -16,6 +16,7 @@
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "crypto/sha384.h"
+#include "pasn/pasn_common.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
@@ -68,44 +69,6 @@
enum frame_encryption encrypted;
};
-enum pasn_fils_state {
- PASN_FILS_STATE_NONE = 0,
- PASN_FILS_STATE_PENDING_AS,
- PASN_FILS_STATE_COMPLETE
-};
-
-struct pasn_fils_data {
- u8 state;
- u8 nonce[FILS_NONCE_LEN];
- u8 anonce[FILS_NONCE_LEN];
- u8 session[FILS_SESSION_LEN];
- u8 erp_pmkid[PMKID_LEN];
-
- struct wpabuf *erp_resp;
-};
-
-struct pasn_data {
- int akmp;
- int cipher;
- u16 group;
- bool secure_ltf;
- u8 trans_seq;
- u8 wrapped_data_format;
- size_t kdk_len;
-
- u8 hash[SHA384_MAC_LEN];
- struct wpa_ptk ptk;
- struct crypto_ecdh *ecdh;
-
- struct wpabuf *secret;
-#ifdef CONFIG_SAE
- struct sae_data sae;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
- struct pasn_fils_data fils;
-#endif /* CONFIG_FILS */
-};
-
struct sta_info {
struct sta_info *next; /* next entry in sta list */
struct sta_info *hnext; /* next entry in hash table list */
@@ -155,6 +118,7 @@
unsigned int qos_map_enabled:1;
unsigned int remediation:1;
unsigned int hs20_deauth_requested:1;
+ unsigned int hs20_deauth_on_ack:1;
unsigned int session_timeout_set:1;
unsigned int radius_das_match:1;
unsigned int ecsa_supported:1;
@@ -390,6 +354,8 @@
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
struct sta_info *sta);
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+ struct sta_info *sta);
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 64a1800..7aff64f 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -23,6 +23,7 @@
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
+#include "crypto/sha512.h"
#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "drivers/driver.h"
@@ -1652,22 +1653,25 @@
if (pad_len)
*pos++ = 0xdd;
- wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+ wpa_hexdump_key(MSG_DEBUG,
+ "Plaintext EAPOL-Key Key Data (+ padding)",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
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 %zu)",
- sm->PTK.kek_len);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: AES-WRAP using KEK",
+ sm->PTK.kek, 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);
bin_clear_free(buf, key_data_len);
return;
}
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Encrypted Key Data from AES-WRAP",
+ key_data, key_data_len);
WPA_PUT_BE16(key_mic + mic_len, key_data_len);
-#ifndef CONFIG_NO_RC4
+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_FIPS)
} else if (sm->PTK.kek_len == 16) {
u8 ek[32];
@@ -1681,7 +1685,7 @@
os_memcpy(key_data, buf, key_data_len);
rc4_skip(ek, 32, 256, key_data, key_data_len);
WPA_PUT_BE16(key_mic + mic_len, key_data_len);
-#endif /* CONFIG_NO_RC4 */
+#endif /* !(CONFIG_NO_RC4 || CONFIG_FIPS) */
} else {
os_free(hdr);
bin_clear_free(buf, key_data_len);
@@ -1716,6 +1720,7 @@
}
wpa_auth_set_eapol(wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1);
+ wpa_hexdump(MSG_DEBUG, "Send EAPOL-Key msg", hdr, len);
wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
sm->pairwise_set);
os_free(hdr);
@@ -1735,10 +1740,25 @@
if (!sm)
return;
+ ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /* When delay_eapol_tx is true, delay the EAPOL-Key transmission by
+ * sending it only on the last attempt after all timeouts for the prior
+ * skipped attemps. */
+ if (wpa_auth->conf.delay_eapol_tx &&
+ ctr != wpa_auth->conf.wpa_pairwise_update_count) {
+ wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+ "DELAY-EAPOL-TX-%d", ctr);
+ goto skip_tx;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
keyidx, encr, 0);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_tx:
+#endif /* CONFIG_TESTING_OPTIONS */
- ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
if (ctr == 1 && wpa_auth->conf.tx_status)
timeout_ms = pairwise ? eapol_key_timeout_first :
eapol_key_timeout_first_group;
@@ -2192,13 +2212,20 @@
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;
+#ifdef CONFIG_SAE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (psk_len == SHA512_MAC_LEN || psk_len == SHA384_MAC_LEN ||
+ psk_len == SHA256_MAC_LEN))
+ sm->xxkey_len = psk_len;
+#endif /* CONFIG_SAE */
+ os_memcpy(sm->xxkey, psk, sm->xxkey_len);
#endif /* CONFIG_IEEE80211R_AP */
}
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sm) && sm->pmksa) {
- wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
+ wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache (len=%zu)",
+ sm->pmksa->pmk_len);
os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
#ifdef CONFIG_IEEE80211R_AP
@@ -2460,7 +2487,6 @@
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);
if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
conf->ssid, conf->ssid_len,
@@ -2468,7 +2494,7 @@
conf->r0_key_holder,
conf->r0_key_holder_len,
sm->addr, pmk_r0, pmk_r0_name,
- use_sha384) < 0)
+ sm->wpa_key_mgmt) < 0)
return -1;
wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
@@ -2476,7 +2502,7 @@
res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
sm->addr, sm->pmk_r1_name,
- use_sha384);
+ fils_ft_len);
forced_memzero(pmk_r0, PMK_LEN_MAX);
if (res < 0)
return -1;
@@ -3694,9 +3720,8 @@
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,
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+ sm->xxkey_len,
conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
@@ -4814,11 +4839,13 @@
"wpa=%d\n"
"AKMSuiteSelector=" RSN_SUITE "\n"
"hostapdWPAPTKState=%d\n"
- "hostapdWPAPTKGroupState=%d\n",
+ "hostapdWPAPTKGroupState=%d\n"
+ "hostapdMFPR=%d\n",
sm->wpa,
RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
sm->wpa_ptk_state,
- sm->wpa_ptk_group_state);
+ sm->wpa_ptk_group_state,
+ sm->mfpr);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -4855,6 +4882,14 @@
}
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm)
+{
+ if (!sm || !sm->pmksa)
+ return NULL;
+ return sm->pmksa->dpp_pkhash;
+}
+
+
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
{
if (!sm)
@@ -5017,6 +5052,29 @@
}
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp, const u8 *dpp_pkhash)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ if (wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
+ entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+ NULL, 0, wpa_auth->addr, addr, session_timeout,
+ NULL, akmp);
+ if (!entry)
+ return -1;
+
+ if (dpp_pkhash)
+ entry->dpp_pkhash = os_memdup(dpp_pkhash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr)
{
@@ -5102,6 +5160,15 @@
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth)
+{
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return NULL;
+ return wpa_auth->pmksa;
+}
+
+
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 *pmkid)
@@ -5462,13 +5529,14 @@
#ifdef CONFIG_IEEE80211R_AP
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
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,
+ return wpa_write_ftie(conf, sm->wpa_key_mgmt, sm->xxkey_len,
+ conf->r0_key_holder, conf->r0_key_holder_len,
NULL, NULL, buf, len, NULL, 0, 0);
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -5682,9 +5750,8 @@
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,
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+ sm->xxkey_len,
conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index a18f7cb..3b32fe3 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -242,6 +242,7 @@
int ft_rsnxe_used;
unsigned int skip_send_eapol:1;
unsigned int enable_eapol_large_timeout:1;
+ bool delay_eapol_tx;
#endif /* CONFIG_TESTING_OPTIONS */
unsigned int oci_freq_override_eapol_m3;
unsigned int oci_freq_override_eapol_g1;
@@ -257,7 +258,7 @@
unsigned int fils_cache_id_set:1;
u8 fils_cache_id[FILS_CACHE_ID_LEN];
#endif /* CONFIG_FILS */
- int sae_pwe;
+ enum sae_pwe sae_pwe;
bool sae_pk;
unsigned int secure_ltf:1;
@@ -412,6 +413,7 @@
int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
+const u8 * wpa_auth_get_dpp_pkhash(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);
@@ -437,6 +439,9 @@
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);
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp, const u8 *dpp_pkhash);
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,
@@ -450,6 +455,8 @@
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 *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth);
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 *pmkid);
@@ -537,7 +544,8 @@
int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
int ap_seg1_idx, int *bandwidth, int *seg1_idx);
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
u8 *buf, size_t len);
void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
u8 *fils_anonce, u8 *fils_snonce,
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 1b1324b..88d63bb 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -20,6 +20,7 @@
#include "crypto/aes_siv.h"
#include "crypto/aes_wrap.h"
#include "crypto/sha384.h"
+#include "crypto/sha512.h"
#include "crypto/random.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -805,15 +806,29 @@
}
-int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
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, int rsnxe_used)
{
u8 *pos = buf, *ielen;
- size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
- sizeof(struct rsn_ftie);
+ size_t hdrlen;
+ u16 mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA256_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie);
+ else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie_sha384);
+ else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie_sha512);
+ else if (wpa_key_mgmt_sha384(key_mgmt))
+ hdrlen = sizeof(struct rsn_ftie_sha384);
+ else
+ hdrlen = sizeof(struct rsn_ftie);
if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
subelem_len)
@@ -822,12 +837,27 @@
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ielen = pos++;
- if (use_sha384) {
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN) {
+ struct rsn_ftie_sha512 *hdr = (struct rsn_ftie_sha512 *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ } else if ((key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(key_mgmt)) {
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, !!rsnxe_used);
+ mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
if (anonce)
os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
if (snonce)
@@ -837,7 +867,8 @@
os_memset(hdr, 0, sizeof(*hdr));
pos += sizeof(*hdr);
- WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
+ mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
if (anonce)
os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
if (snonce)
@@ -2081,9 +2112,7 @@
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
{
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
- size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
- SHA384_MAC_LEN : PMK_LEN;
- size_t pmk_r1_len = pmk_r0_len;
+ size_t pmk_r0_len, pmk_r1_len;
u8 pmk_r1[PMK_LEN_MAX];
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
@@ -2101,6 +2130,17 @@
const u8 *mpmk;
size_t mpmk_len;
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (sm->xxkey_len == SHA256_MAC_LEN ||
+ sm->xxkey_len == SHA384_MAC_LEN ||
+ sm->xxkey_len == SHA512_MAC_LEN))
+ pmk_r0_len = sm->xxkey_len;
+ else if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+ pmk_r0_len = SHA384_MAC_LEN;
+ else
+ pmk_r0_len = PMK_LEN;
+ pmk_r1_len = pmk_r0_len;
+
if (sm->xxkey_len > 0) {
mpmk = sm->xxkey;
mpmk_len = sm->xxkey_len;
@@ -2127,7 +2167,7 @@
if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
r0kh, r0kh_len, sm->addr,
pmk_r0, pmk_r0_name,
- wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
+ sm->wpa_key_mgmt) < 0)
return -1;
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,
@@ -2521,12 +2561,11 @@
u8 *anonce, *snonce;
const u8 *kck;
size_t kck_len;
- int use_sha384;
+ size_t key_len;
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))
@@ -2697,7 +2736,8 @@
snonce = NULL;
}
rsnxe_used = (auth_alg == WLAN_AUTH_FT) &&
- (conf->sae_pwe == 1 || conf->sae_pwe == 2);
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH);
#ifdef CONFIG_TESTING_OPTIONS
if (sm->wpa_auth->conf.ft_rsnxe_used) {
rsnxe_used = sm->wpa_auth->conf.ft_rsnxe_used == 1;
@@ -2705,7 +2745,20 @@
rsnxe_used);
}
#endif /* CONFIG_TESTING_OPTIONS */
- res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
+ key_len = sm->xxkey_len;
+ if (!key_len)
+ key_len = sm->pmk_r1_len;
+ if (!key_len && sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->wpa_auth->cb->get_psk) {
+ size_t psk_len;
+
+ if (sm->wpa_auth->cb->get_psk(sm->wpa_auth->cb_ctx,
+ sm->addr, sm->p2p_dev_addr,
+ NULL, &psk_len, NULL))
+ key_len = psk_len;
+ }
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt, key_len,
+ r0kh_id, r0kh_id_len,
anonce, snonce, pos, end - pos,
subelem, subelem_len, rsnxe_used);
os_free(subelem);
@@ -2715,7 +2768,16 @@
ftie_len = res;
pos += res;
- if (use_sha384) {
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN) {
+ struct rsn_ftie_sha512 *_ftie =
+ (struct rsn_ftie_sha512 *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ } else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
struct rsn_ftie_sha384 *_ftie =
(struct rsn_ftie_sha384 *) (ftie + 2);
@@ -2731,8 +2793,8 @@
*elem_count = 3; /* Information element count */
ric_start = pos;
- if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
- && parse.ric) {
+ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse,
+ sm->wpa_key_mgmt) == 0 && parse.ric) {
pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
parse.ric_len);
if (auth_alg == WLAN_AUTH_FT)
@@ -2772,7 +2834,8 @@
kck_len = sm->PTK.kck_len;
}
if (auth_alg == WLAN_AUTH_FT &&
- wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
+ wpa_ft_mic(sm->wpa_key_mgmt, 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,
@@ -2913,7 +2976,8 @@
if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
r0kh_len, sm->addr,
- pmk_r0, pmk_r0_name, 0) < 0 ||
+ pmk_r0, pmk_r0_name,
+ WPA_KEY_MGMT_FT_PSK) < 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,
@@ -3013,7 +3077,8 @@
const u8 **identity, size_t *identity_len,
const u8 **radius_cui,
size_t *radius_cui_len,
- int *out_session_timeout)
+ int *out_session_timeout,
+ size_t *pmk_r1_len)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
const struct wpa_ft_pmk_r0_sa *r0;
@@ -3072,6 +3137,8 @@
*out_session_timeout = session_timeout;
+ *pmk_r1_len = r0->pmk_r0_len;
+
return 0;
}
@@ -3092,8 +3159,7 @@
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, kdk_len;
+ size_t pmk_r1_len, kdk_len, len;
*resp_ies = NULL;
*resp_ies_len = 0;
@@ -3104,12 +3170,10 @@
wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
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) ||
@@ -3120,26 +3184,9 @@
return WLAN_STATUS_INVALID_MDIE;
}
- 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);
- } 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.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r0kh_id == NULL) {
@@ -3162,49 +3209,73 @@
wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
parse.rsn_pmkid, WPA_PMK_NAME_LEN);
- 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;
if (conf->ft_psk_generate_local &&
wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder,
+ sm->addr, pmk_r1_name, PMK_LEN) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
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;
+ pmk_r1_len = PMK_LEN;
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) {
+ goto pmk_r1_derived;
+ }
+
+ /* Need to test all possible hash algorithms for FT-SAE-EXT-KEY since
+ * the key length is not yet known. For other AKMs, only the length
+ * identified by the AKM is used. */
+ for (len = SHA256_MAC_LEN; len <= SHA512_MAC_LEN; len += 16) {
+ if (parse.key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ ((wpa_key_mgmt_sha384(parse.key_mgmt) &&
+ len != SHA384_MAC_LEN) ||
+ (!wpa_key_mgmt_sha384(parse.key_mgmt) &&
+ len != SHA256_MAC_LEN)))
+ continue;
+ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder,
+ sm->addr, pmk_r1_name, len) < 0)
+ continue;
+
+ 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: Generated PMK-R1 based on local PMK-R0");
+ "FT: Found PMKR1Name (using SHA%zu) from local cache",
+ pmk_r1_len * 8);
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 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_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, &pmk_r1_len) == 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 either unknown or blocked R0KH-ID or NAK from R0KH");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ return -1; /* Status pending */
+
pmk_r1_derived:
wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
sm->pmk_r1_name_valid = 1;
@@ -3218,6 +3289,40 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
+ /* Now that we know the correct PMK-R1 length and as such, the length
+ * of the MIC field, fetch the SNonce. */
+ if (pmk_r1_len == SHA512_MAC_LEN) {
+ const struct rsn_ftie_sha512 *ftie;
+
+ ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else if (pmk_r1_len == SHA384_MAC_LEN) {
+ const struct rsn_ftie_sha384 *ftie;
+
+ ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else {
+ const struct rsn_ftie *ftie;
+
+ ftie = (const 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);
+ }
+
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
sm->SNonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
@@ -3232,14 +3337,14 @@
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,
+ &sm->PTK, ptk_name, parse.key_mgmt,
pairwise, kdk_len) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
#ifdef CONFIG_PASN
if (sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
- wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, pairwise)) {
+ wpa_ltf_keyseed(&sm->PTK, parse.key_mgmt, pairwise)) {
wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -3282,7 +3387,8 @@
goto fail;
pos += ret;
- ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
+ ret = wpa_write_ftie(conf, parse.key_mgmt, pmk_r1_len,
+ parse.r0kh_id, parse.r0kh_id_len,
sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0,
0);
if (ret < 0)
@@ -3349,25 +3455,20 @@
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
- size_t mic_len = 16;
+ size_t mic_len;
unsigned int count;
const u8 *kck;
size_t kck_len;
- int use_sha384;
- const u8 *anonce, *snonce, *fte_mic;
- u8 fte_elem_count;
- int rsnxe_used;
struct wpa_auth_config *conf;
if (sm == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
conf = &sm->wpa_auth->conf;
- 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, use_sha384) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -3397,55 +3498,42 @@
return WLAN_STATUS_INVALID_MDIE;
}
- if (use_sha384) {
- struct rsn_ftie_sha384 *ftie;
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA512_MAC_LEN)
+ mic_len = 32;
+ else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+ mic_len = 24;
+ else
+ mic_len = 16;
- 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;
- rsnxe_used = ftie->mic_control[0] & 0x01;
- 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;
- rsnxe_used = ftie->mic_control[0] & 0x01;
- fte_elem_count = ftie->mic_control[1];
- fte_mic = ftie->mic;
+ if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+ parse.fte_mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+ parse.fte_mic_len, mic_len);
+ return WLAN_STATUS_INVALID_FTIE;
}
- if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- snonce, WPA_NONCE_LEN);
+ parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->SNonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
- if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- anonce, WPA_NONCE_LEN);
+ parse.fte_anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->ANonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
-
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
return WLAN_STATUS_INVALID_FTIE;
@@ -3492,10 +3580,10 @@
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (parse.rsnxe)
count++;
- if (fte_elem_count != count) {
+ if (parse.fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- fte_elem_count, count);
+ parse.fte_elem_count, count);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -3506,7 +3594,8 @@
kck = sm->PTK.kck;
kck_len = sm->PTK.kck_len;
}
- if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
+ if (wpa_ft_mic(sm->wpa_key_mgmt, 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,
@@ -3518,12 +3607,12 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+ if (os_memcmp_const(mic, parse.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",
- fte_mic, mic_len);
+ parse.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);
@@ -3537,7 +3626,9 @@
return WLAN_STATUS_INVALID_FTIE;
}
- if (rsnxe_used && (conf->sae_pwe == 1 || conf->sae_pwe == 2) &&
+ if (parse.fte_rsnxe_used &&
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH) &&
!parse.rsnxe) {
wpa_printf(MSG_INFO,
"FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
@@ -4105,7 +4196,8 @@
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))
+ (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN ||
+ f_pmk_r1_len == SHA512_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);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index a510952..9f6699b 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -117,6 +117,7 @@
#ifdef CONFIG_TESTING_OPTIONS
wconf->corrupt_gtk_rekey_mic_probability =
iconf->corrupt_gtk_rekey_mic_probability;
+ wconf->delay_eapol_tx = iconf->delay_eapol_tx;
if (conf->own_ie_override &&
wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
@@ -198,10 +199,10 @@
#endif /* CONFIG_FILS */
wconf->sae_pwe = conf->sae_pwe;
sae_pw_id = hostapd_sae_pw_id_in_use(conf);
- if (sae_pw_id == 2 && wconf->sae_pwe != 3)
- wconf->sae_pwe = 1;
- else if (sae_pw_id == 1 && wconf->sae_pwe == 0)
- wconf->sae_pwe = 2;
+ if (sae_pw_id == 2 && wconf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ wconf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ else if (sae_pw_id == 1 && wconf->sae_pwe == SAE_PWE_HUNT_AND_PECK)
+ wconf->sae_pwe = SAE_PWE_BOTH;
#ifdef CONFIG_SAE_PK
wconf->sae_pk = hostapd_sae_pk_in_use(conf);
#endif /* CONFIG_SAE_PK */
@@ -937,7 +938,7 @@
struct hostapd_data *hapd = ctx;
ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
- ptk, NULL, NULL);
+ ptk, NULL, NULL, 0);
}
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 5bd699c..7ed3f2b 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -86,6 +86,7 @@
unsigned int pending_deinit:1;
unsigned int started:1;
unsigned int mgmt_frame_prot:1;
+ unsigned int mfpr:1;
unsigned int rx_eapol_key_secure:1;
unsigned int update_snonce:1;
unsigned int alt_snonce_valid:1;
@@ -298,7 +299,7 @@
#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, int use_sha384,
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 1c8affa..43ccec9 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -405,7 +405,8 @@
size_t flen;
if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
- (conf->sae_pwe == 1 || conf->sae_pwe == 2 || conf->sae_pk ||
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
@@ -419,7 +420,7 @@
if (conf->secure_rtt)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (conf->prot_range_neg)
- capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
@@ -883,6 +884,7 @@
sm->mgmt_frame_prot = 0;
else
sm->mgmt_frame_prot = 1;
+ sm->mfpr = !!(data.capabilities & WPA_CAPABILITY_MFPR);
if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
wpa_printf(MSG_DEBUG,
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 8aba713..a95ae36 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -428,7 +428,7 @@
}
if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
- NULL, 0) != 0 ||
+ NULL, 0, NULL) != 0 ||
sae_process_commit(&sae) < 0)
goto fail;
diff --git a/src/common/defs.h b/src/common/defs.h
index 3e658cb..c0c6dbe 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -137,7 +137,8 @@
static inline int wpa_key_mgmt_sha256(int akm)
{
- return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+ return !!(akm & (WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_PSK_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
WPA_KEY_MGMT_SAE |
WPA_KEY_MGMT_FT_SAE |
@@ -520,4 +521,12 @@
#define MAX_NUM_MLD_LINKS 15
+enum sae_pwe {
+ SAE_PWE_HUNT_AND_PECK = 0,
+ SAE_PWE_HASH_TO_ELEMENT = 1,
+ SAE_PWE_BOTH = 2,
+ SAE_PWE_FORCE_HUNT_AND_PECK = 3,
+ SAE_PWE_NOT_SET = 4,
+};
+
#endif /* DEFS_H */
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 559bdcd..d2fc00f 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -4146,7 +4146,7 @@
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)
+ os_time_t *expiry, u8 *peer_key_hash)
{
struct json_token *root = NULL, *netkey, *token;
struct json_token *own_root = NULL;
@@ -4269,6 +4269,9 @@
}
#endif /* CONFIG_DPP3 */
+ if (peer_key_hash)
+ dpp_get_pubkey_hash(intro->peer_key, peer_key_hash);
+
ret = DPP_STATUS_OK;
fail:
if (ret != DPP_STATUS_OK)
@@ -5039,6 +5042,24 @@
}
+void dpp_notify_auth_success(struct dpp_authentication *auth, int initiator)
+{
+ u8 hash[SHA256_MAC_LEN];
+ char hex[SHA256_MAC_LEN * 2 + 1];
+
+ if (auth->peer_protocol_key) {
+ dpp_get_pubkey_hash(auth->peer_protocol_key, hash);
+ wpa_snprintf_hex(hex, sizeof(hex), hash, sizeof(hash));
+ } else {
+ hex[0] = '\0';
+ }
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_AUTH_SUCCESS "init=%d pkhash=%s own=%d peer=%d",
+ initiator, hex, auth->own_bi ? (int) auth->own_bi->id : -1,
+ auth->peer_bi ? (int) auth->peer_bi->id : -1);
+}
+
+
#ifdef CONFIG_DPP2
struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 3094be8..86f8478 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -674,7 +674,7 @@
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);
+ os_time_t *expiry, u8 *peer_key_hash);
void dpp_peer_intro_deinit(struct dpp_introduction *intro);
int dpp_get_connector_version(const char *connector);
struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
@@ -825,6 +825,7 @@
struct dpp_global * dpp_global_init(struct dpp_global_config *config);
void dpp_global_clear(struct dpp_global *dpp);
void dpp_global_deinit(struct dpp_global *dpp);
+void dpp_notify_auth_success(struct dpp_authentication *auth, int initiator);
/* dpp_reconfig.c */
@@ -857,6 +858,7 @@
size_t pp_key_len);
int dpp_update_reconfig_id(struct dpp_reconfig_id *id);
void dpp_free_reconfig_id(struct dpp_reconfig_id *id);
+int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash);
#endif /* CONFIG_DPP */
#endif /* DPP_H */
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index 09d4d8c..f17f95a 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -246,6 +246,27 @@
}
+int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
+{
+ struct wpabuf *uncomp;
+ const u8 *addr[1];
+ size_t len[1];
+ int res;
+
+ if (!key)
+ return -1;
+
+ uncomp = crypto_ec_key_get_pubkey_point(key, 1);
+ if (!uncomp)
+ return -1;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ return res;
+}
+
+
struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
{
struct crypto_ec_key *key;
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
index ff18a99..d226a8a 100644
--- a/src/common/dpp_tcp.c
+++ b/src/common/dpp_tcp.c
@@ -371,8 +371,7 @@
return;
wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
- wpa_msg(conn->msg_ctx, MSG_INFO,
- DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+ dpp_notify_auth_success(auth, initiator);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
wpa_printf(MSG_INFO,
@@ -628,6 +627,17 @@
if (!ctrl)
return -1;
+ if (type == DPP_PA_PRESENCE_ANNOUNCEMENT ||
+ type == DPP_PA_RECONFIG_ANNOUNCEMENT) {
+ conn = dpp_relay_match_ctrl(ctrl, src, freq, type);
+ if (conn &&
+ (!conn->auth || conn->auth->waiting_auth_resp)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Use existing TCP connection to Controller since no Auth Resp seen on it yet");
+ return dpp_relay_tx(conn, hdr, buf, len);
+ }
+ }
+
wpa_printf(MSG_DEBUG,
"DPP: Authentication Request for a configured Controller");
conn = dpp_relay_new_conn(ctrl, src, freq);
@@ -945,12 +955,6 @@
struct dpp_authentication *auth;
struct dpp_global *dpp = conn->ctrl->global;
- if (conn->auth) {
- wpa_printf(MSG_DEBUG,
- "DPP: Ignore Presence Announcement during ongoing Authentication");
- return -1;
- }
-
wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
@@ -969,6 +973,12 @@
return -1;
}
+ if (conn->auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Presence Announcement during ongoing Authentication");
+ return 0;
+ }
+
auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL,
DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
if (!auth)
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index d6fd792..3c3667f 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -199,11 +199,76 @@
}
+static int ieee802_11_parse_mle(const u8 *pos, size_t elen, size_t **total_len,
+ struct ieee802_11_elems *elems,
+ int show_errors)
+{
+ u8 mle_type = pos[0] & MULTI_LINK_CONTROL_TYPE_MASK;
+
+ switch (mle_type) {
+ case MULTI_LINK_CONTROL_TYPE_BASIC:
+ elems->basic_mle = pos;
+ elems->basic_mle_len = elen;
+ *total_len = &elems->basic_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_PROBE_REQ:
+ elems->probe_req_mle = pos;
+ elems->probe_req_mle_len = elen;
+ *total_len = &elems->probe_req_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_RECONF:
+ elems->reconf_mle = pos;
+ elems->reconf_mle_len = elen;
+ *total_len = &elems->reconf_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_TDLS:
+ elems->tdls_mle = pos;
+ elems->tdls_mle_len = elen;
+ *total_len = &elems->tdls_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS:
+ elems->prior_access_mle = pos;
+ elems->prior_access_mle_len = elen;
+ *total_len = &elems->prior_access_mle_len;
+ break;
+ default:
+ if (show_errors) {
+ wpa_printf(MSG_MSGDUMP,
+ "Unknown Multi-Link element type %u",
+ mle_type);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static size_t ieee802_11_fragments_length(struct ieee802_11_elems *elems,
+ const u8 *start, size_t len)
+{
+ const struct element *elem;
+ size_t frags_len = 0;
+
+ for_each_element(elem, start, len) {
+ if (elem->id != WLAN_EID_FRAGMENT)
+ break;
+
+ frags_len += elem->datalen + 2;
+ elems->num_frag_elems++;
+ }
+
+ return frags_len;
+}
+
+
static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
struct ieee802_11_elems *elems,
+ const u8 *start, size_t len,
int show_errors)
{
u8 ext_id;
+ size_t *total_len = NULL;
if (elen < 1) {
if (show_errors) {
@@ -216,8 +281,6 @@
ext_id = *pos++;
elen--;
- elems->frag_ies.last_eid_ext = 0;
-
switch (ext_id) {
case WLAN_EID_EXT_ASSOC_DELAY_INFO:
if (elen != 1)
@@ -244,6 +307,7 @@
break;
elems->fils_hlp = pos;
elems->fils_hlp_len = elen;
+ total_len = &elems->fils_hlp_len;
break;
case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
if (elen < 1)
@@ -260,6 +324,7 @@
case WLAN_EID_EXT_WRAPPED_DATA:
elems->wrapped_data = pos;
elems->wrapped_data_len = elen;
+ total_len = &elems->wrapped_data_len;
break;
case WLAN_EID_EXT_FILS_PUBLIC_KEY:
if (elen < 1)
@@ -323,6 +388,17 @@
elems->eht_operation = pos;
elems->eht_operation_len = elen;
break;
+ case WLAN_EID_EXT_MULTI_LINK:
+ if (elen < 2)
+ break;
+ if (ieee802_11_parse_mle(pos, elen, &total_len, elems,
+ show_errors))
+ return -1;
+ break;
+ case WLAN_EID_EXT_KNOWN_BSSID:
+ elems->mbssid_known_bss = pos;
+ elems->mbssid_known_bss_len = elen;
+ break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
@@ -332,39 +408,14 @@
return -1;
}
- if (elen == 254)
- elems->frag_ies.last_eid_ext = ext_id;
+ if (elen == 254 && total_len)
+ *total_len += ieee802_11_fragments_length(
+ elems, pos + elen, (start + len) - (pos + elen));
return 0;
}
-static void ieee802_11_parse_fragment(struct frag_ies_info *frag_ies,
- const u8 *pos, u8 elen)
-{
- if (frag_ies->n_frags >= MAX_NUM_FRAG_IES_SUPPORTED) {
- wpa_printf(MSG_MSGDUMP, "Too many element fragments - skip");
- return;
- }
-
- /*
- * Note: while EID == 0 is a valid ID (SSID IE), it should not be
- * fragmented.
- */
- if (!frag_ies->last_eid) {
- wpa_printf(MSG_MSGDUMP,
- "Fragment without a valid last element - skip");
- return;
- }
-
- frag_ies->frags[frag_ies->n_frags].ie = pos;
- frag_ies->frags[frag_ies->n_frags].ie_len = elen;
- frag_ies->frags[frag_ies->n_frags].eid = frag_ies->last_eid;
- frag_ies->frags[frag_ies->n_frags].eid_ext = frag_ies->last_eid_ext;
- frag_ies->n_frags++;
-}
-
-
/**
* ieee802_11_parse_elems - Parse information elements in management frames
* @start: Pointer to the start of IEs
@@ -389,6 +440,12 @@
u8 id = elem->id, elen = elem->datalen;
const u8 *pos = elem->data;
+ if (id == WLAN_EID_FRAGMENT && elems->num_frag_elems > 0) {
+ elems->num_frag_elems--;
+ continue;
+ }
+ elems->num_frag_elems = 0;
+
switch (id) {
case WLAN_EID_SSID:
if (elen > SSID_MAX_LEN) {
@@ -592,11 +649,13 @@
elems->s1g_capab = pos;
break;
case WLAN_EID_FRAGMENT:
- ieee802_11_parse_fragment(&elems->frag_ies, pos, elen);
+ wpa_printf(MSG_MSGDUMP,
+ "Fragment without a valid last element - skip");
+
break;
case WLAN_EID_EXTENSION:
- if (ieee802_11_parse_extension(pos, elen, elems,
- show_errors))
+ if (ieee802_11_parse_extension(pos, elen, elems, start,
+ len, show_errors))
unknown++;
break;
default:
@@ -608,12 +667,6 @@
id, elen);
break;
}
-
- if (id != WLAN_EID_FRAGMENT && elen == 255)
- elems->frag_ies.last_eid = id;
-
- if (id == WLAN_EID_EXTENSION && !elems->frag_ies.last_eid_ext)
- elems->frag_ies.last_eid = 0;
}
if (!for_each_element_completed(elem, start, len)) {
@@ -1273,8 +1326,9 @@
if (chan < 25 || chan > 29)
return -1;
return 56160 + 2160 * (chan - 24);
+ default:
+ return -1;
}
- return -1;
}
@@ -1323,8 +1377,9 @@
if (chan != 25)
return -1;
return 56160 + 2160 * (chan - 24);
+ default:
+ return -1;
}
- return -1;
}
@@ -1379,8 +1434,9 @@
if (chan != 25)
return -1;
return 56160 + 2160 * (chan - 24);
+ default:
+ return -1;
}
- return -1;
}
@@ -1405,8 +1461,9 @@
if (chan < 149 || chan > 165)
return -1;
return 5000 + 5 * chan;
+ default:
+ return -1;
}
- return -1;
}
@@ -1492,8 +1549,9 @@
if (chan < 25 || chan > 29)
return -1;
return 56160 + 2160 * (chan - 24);
+ default:
+ return -1;
}
- return -1;
}
/**
@@ -1833,6 +1891,9 @@
S2S(DENIED_HE_NOT_SUPPORTED)
S2S(SAE_HASH_TO_ELEMENT)
S2S(SAE_PK)
+ S2S(INVALID_PUBLIC_KEY)
+ S2S(PASN_BASE_AKMP_FAILED)
+ S2S(OCI_MISMATCH)
}
return "UNKNOWN";
#undef S2S
@@ -1922,11 +1983,14 @@
/*
* IEEE Std 802.11ax-2021, Table E-4 actually talks about channel center
- * frequency index 42, 58, 106, 122, 138, 155, 171 with channel spacing
- * of 80 MHz, but currently use the following definition for simplicity
+ * frequency index for operation classes 128, 129, 130, 132, 133, 134,
+ * and 135, but currently use the lowest 20 MHz channel for simplicity
* (these center frequencies are not actual channels, which makes
- * wpas_p2p_verify_channel() fail). wpas_p2p_verify_80mhz() should take
- * care of removing invalid channels.
+ * wpas_p2p_verify_channel() fail).
+ * Specially for the operation class 136, it is also defined to use the
+ * channel center frequency index value, but it happens to be a 20 MHz
+ * channel and the channel number in the channel set would match the
+ * value in for the frequency center.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP },
@@ -2624,9 +2688,9 @@
return 6480;
case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
return 8640;
+ default:
+ return 20;
}
-
- return 20;
}
@@ -2688,41 +2752,47 @@
return CONF_OPER_CHWIDTH_6480MHZ;
case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
return CONF_OPER_CHWIDTH_8640MHZ;
+ default:
+ return CONF_OPER_CHWIDTH_USE_HT;
}
- return CONF_OPER_CHWIDTH_USE_HT;
}
-struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems,
- u8 eid, u8 eid_ext,
- const u8 *data, u8 len)
-{
- struct frag_ies_info *frag_ies = &elems->frag_ies;
- struct wpabuf *buf;
- unsigned int i;
- if (!elems || !data || !len)
+struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len,
+ bool ext_elem)
+{
+ struct wpabuf *buf;
+ const u8 *pos, *end = data + len;
+ size_t min_defrag_len = ext_elem ? 255 : 256;
+
+ if (!data || !len)
return NULL;
- buf = wpabuf_alloc_copy(data, len);
+ if (len < min_defrag_len)
+ return wpabuf_alloc_copy(data, len);
+
+ buf = wpabuf_alloc_copy(data, min_defrag_len - 1);
if (!buf)
return NULL;
- for (i = 0; i < frag_ies->n_frags; i++) {
+ pos = &data[min_defrag_len - 1];
+ len -= min_defrag_len - 1;
+ while (len > 2 && pos[0] == WLAN_EID_FRAGMENT && pos[1]) {
int ret;
+ size_t elen = 2 + pos[1];
- if (frag_ies->frags[i].eid != eid ||
- frag_ies->frags[i].eid_ext != eid_ext)
- continue;
-
- ret = wpabuf_resize(&buf, frag_ies->frags[i].ie_len);
+ if (elen > (size_t) (end - pos) || elen > len)
+ break;
+ ret = wpabuf_resize(&buf, pos[1]);
if (ret < 0) {
wpabuf_free(buf);
return NULL;
}
/* Copy only the fragment data (without the EID and length) */
- wpabuf_put_data(buf, frag_ies->frags[i].ie,
- frag_ies->frags[i].ie_len);
+ wpabuf_put_data(buf, &pos[2], pos[1]);
+ pos += elen;
+ len -= elen;
}
return buf;
@@ -2733,7 +2803,7 @@
u8 eid, u8 eid_ext)
{
const u8 *data;
- u8 len;
+ size_t len;
/*
* TODO: Defragmentation mechanism can be supported for all IEs. For now
@@ -2763,7 +2833,7 @@
return NULL;
}
- return ieee802_11_defrag_data(elems, eid, eid_ext, data, len);
+ return ieee802_11_defrag_data(data, len, true);
}
/* Parse HT capabilities to get maximum number of supported spatial streams */
@@ -3125,3 +3195,40 @@
return &buf[mld_addr_pos];
}
+
+
+struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type)
+{
+ const u8 *data;
+ size_t len;
+
+ switch (type) {
+ case MULTI_LINK_CONTROL_TYPE_BASIC:
+ data = elems->basic_mle;
+ len = elems->basic_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_PROBE_REQ:
+ data = elems->probe_req_mle;
+ len = elems->probe_req_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_RECONF:
+ data = elems->reconf_mle;
+ len = elems->reconf_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_TDLS:
+ data = elems->tdls_mle;
+ len = elems->tdls_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS:
+ data = elems->prior_access_mle;
+ len = elems->prior_access_mle_len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Defragmentation not supported for Multi-Link element type=%u",
+ type);
+ return NULL;
+ }
+
+ return ieee802_11_defrag_data(data, len, true);
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index ff31e8d..9a6915d 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -21,7 +21,6 @@
struct hostapd_hw_modes;
#define MAX_NOF_MB_IES_SUPPORTED 5
-#define MAX_NUM_FRAG_IES_SUPPORTED 3
struct mb_ies_info {
struct {
@@ -31,21 +30,6 @@
u8 nof_ies;
};
-struct frag_ies_info {
- struct {
- u8 eid;
- u8 eid_ext;
- const u8 *ie;
- u8 ie_len;
- } frags[MAX_NUM_FRAG_IES_SUPPORTED];
-
- u8 n_frags;
-
- /* the last parsed element ID and element extension ID */
- u8 last_eid;
- u8 last_eid_ext;
-};
-
/* Parsed Information Elements */
struct ieee802_11_elems {
const u8 *ssid;
@@ -119,6 +103,12 @@
const u8 *pasn_params;
const u8 *eht_capabilities;
const u8 *eht_operation;
+ const u8 *basic_mle;
+ const u8 *probe_req_mle;
+ const u8 *reconf_mle;
+ const u8 *tdls_mle;
+ const u8 *prior_access_mle;
+ const u8 *mbssid_known_bss;
u8 ssid_len;
u8 supp_rates_len;
@@ -157,10 +147,10 @@
u8 dils_len;
u8 fils_req_params_len;
u8 fils_key_confirm_len;
- u8 fils_hlp_len;
+ size_t fils_hlp_len;
u8 fils_ip_addr_assign_len;
u8 key_delivery_len;
- u8 wrapped_data_len;
+ size_t wrapped_data_len;
u8 fils_pk_len;
u8 owe_dh_len;
u8 power_capab_len;
@@ -175,9 +165,20 @@
u8 pasn_params_len;
u8 eht_capabilities_len;
u8 eht_operation_len;
+ size_t basic_mle_len;
+ size_t probe_req_mle_len;
+ size_t reconf_mle_len;
+ size_t tdls_mle_len;
+ size_t prior_access_mle_len;
+ u8 mbssid_known_bss_len;
struct mb_ies_info mb_ies;
- struct frag_ies_info frag_ies;
+
+ /*
+ * The number of fragment elements to be skipped after a known
+ * fragmented element.
+ */
+ unsigned int num_frag_elems;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -338,11 +339,11 @@
int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
struct ieee80211_edmg_config requested);
-struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems,
- u8 eid, u8 eid_ext,
- const u8 *data, u8 len);
+struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len,
+ bool ext_elem);
struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems,
u8 eid, u8 eid_ext);
+struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type);
const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type);
const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 5c423eb..e5e4e83 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -209,6 +209,9 @@
#define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124
#define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
#define WLAN_STATUS_SAE_PK 127
+#define WLAN_STATUS_INVALID_PUBLIC_KEY 136
+#define WLAN_STATUS_PASN_BASE_AKMP_FAILED 137
+#define WLAN_STATUS_OCI_MISMATCH 138
/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
#define WLAN_REASON_UNSPECIFIED 1
@@ -481,6 +484,9 @@
#define WLAN_EID_EXT_SPATIAL_REUSE 39
#define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42
#define WLAN_EID_EXT_OCV_OCI 54
+#define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
+#define WLAN_EID_EXT_NON_INHERITANCE 56
+#define WLAN_EID_EXT_KNOWN_BSSID 57
#define WLAN_EID_EXT_SHORT_SSID_LIST 58
#define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
#define WLAN_EID_EXT_EDMG_CAPABILITIES 61
@@ -586,7 +592,11 @@
#define WLAN_RSNX_CAPAB_SAE_PK 6
#define WLAN_RSNX_CAPAB_SECURE_LTF 8
#define WLAN_RSNX_CAPAB_SECURE_RTT 9
-#define WLAN_RSNX_CAPAB_PROT_RANGE_NEG 10
+#define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
+#define WLAN_RSNX_CAPAB_URNM_MFPR 15
+
+/* Multiple BSSID element subelements */
+#define WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0
/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
#define WLAN_ACTION_SPECTRUM_MGMT 0
@@ -1289,9 +1299,12 @@
#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11))
/* B36..B39 - Reserved */
+#define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122
+#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
+#define BSS_MEMBERSHIP_SELECTOR_EPD 124
+#define BSS_MEMBERSHIP_SELECTOR_GLK 125
#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
-#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
/* VHT Defines */
#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0))
@@ -2444,13 +2457,16 @@
#define RNR_BSS_PARAM_CO_LOCATED BIT(6)
#define RNR_20_MHZ_PSD_MAX_TXPOWER 255 /* dBm */
-/* IEEE P802.11be/D1.5, 9.4.2.311 - EHT Operation element */
+/* IEEE P802.11be/D2.3, 9.4.2.311 - EHT Operation element */
#define EHT_OPERATION_IE_MIN_LEN 1
/* Figure 9-1002b: EHT Operation Parameters field subfields */
#define EHT_OPER_INFO_PRESENT BIT(0)
#define EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT BIT(1)
+#define EHT_OPER_DEFAULT_PE_DURATION BIT(2)
+#define EHT_OPER_GROUP_ADDR_BU_INDICATION_LIMIT BIT(3)
+#define EHT_OPER_GROUP_ADDR_BU_INDICATION_EXPONENT (BIT(4) | BIT(5))
/* Control subfield: Channel Width subfield; see Table 9-401b */
#define EHT_OPER_CHANNEL_WIDTH_MASK 0x7
@@ -2473,6 +2489,7 @@
/* Figure 9-1002a: EHT Operation element format */
struct ieee80211_eht_operation {
u8 oper_params; /* EHT Operation Parameters: EHT_OPER_* bits */
+ u8 basic_eht_mcs_nss_set[4];
struct ieee80211_eht_oper_info oper_info; /* 0 or 3 or 5 octets */
} STRUCT_PACKED;
@@ -2550,10 +2567,13 @@
u8 optional[EHT_MCS_NSS_CAPAB_LEN + EHT_PPE_THRESH_CAPAB_LEN];
} STRUCT_PACKED;
+#define IEEE80211_EHT_CAPAB_MIN_LEN (2 + 9)
+
/* IEEE P802.11be/D2.1, 9.4.2.312 - Multi-Link element */
/* Figure 9-1002f: Multi-Link Control field */
#define MULTI_LINK_CONTROL_TYPE_MASK 0x07
+#define MULTI_LINK_CONTROL_LEN 2
/* Table 9-401c: Mult-Link element Type subfield encoding */
#define MULTI_LINK_CONTROL_TYPE_BASIC 0
@@ -2562,14 +2582,125 @@
#define MULTI_LINK_CONTROL_TYPE_TDLS 3
#define MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS 4
-/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */
-#define BASIC_MULTI_LINK_CTRL0_PRES_LINK_ID 0x10
-#define BASIC_MULTI_LINK_CTRL0_PRES_BSS_PARAM_CH_COUNT 0x20
-#define BASIC_MULTI_LINK_CTRL0_PRES_MSD_INFO 0x40
-#define BASIC_MULTI_LINK_CTRL0_PRES_EML_CAPA 0x80
+/*
+ * IEEE P802.11be/D2.2, Table 9-401c: Optional subelement IDs for Link Info
+ * field of the Multi-Link element
+ */
+#define MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE 0
+#define MULTI_LINK_SUB_ELEM_ID_VENDOR 221
+#define MULTI_LINK_SUB_ELEM_ID_FRAGMENT 254
-#define BASIC_MULTI_LINK_CTRL1_PRES_MLD_CAPA 0x01
-#define BASIC_MULTI_LINK_CTRL1_PRES_AP_MLD_ID 0x02
+/* IEEE P802.11be/D2.2, 9.4.2.312.2 - Basic Multi-Link element */
+
+/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */
+#define BASIC_MULTI_LINK_CTRL_PRES_LINK_ID 0x0010
+#define BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT 0x0020
+#define BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO 0x0040
+#define BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA 0x0080
+#define BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA 0x0100
+#define BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID 0x0200
+
+/*
+ * STA Control field definitions of Per-STA Profile subelement in Basic
+ * Multi-Link element as described in Figure 9-1002n: STA Control field format.
+ */
+#define BASIC_MLE_STA_CTRL_LINK_ID_MASK 0x000F
+#define BASIC_MLE_STA_CTRL_COMPLETE_PROFILE 0x0010
+#define BASIC_MLE_STA_CTRL_PRES_STA_MAC 0x0020
+#define BASIC_MLE_STA_CTRL_PRES_BEACON_INT 0x0040
+#define BASIC_MLE_STA_CTRL_PRES_TSF_OFFSET 0x0080
+#define BASIC_MLE_STA_CTRL_PRES_DTIM_INFO 0x0100
+#define BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR 0x0200
+#define BASIC_MLE_STA_CTRL_NSTR_BITMAP 0x0400
+#define BASIC_MLE_STA_CTRL_PRES_BSS_PARAM_COUNT 0x0800
+
+#define BASIC_MLE_STA_PROF_STA_MAC_IDX 3
+
+/* IEEE P802.11be/D2.2, 9.4.2.312.2.3 - Common Info field of the Basic
+ * Multi-Link element */
+struct eht_ml_basic_common_info {
+ u8 len;
+ u8 mld_addr[ETH_ALEN];
+
+ /*
+ * Followed by optional fields based on the multi link basic presence
+ * bitmap
+ *
+ * Link ID Info: 1 octet
+ * BSS Parameters Change Count: 1 octet
+ * Medium Synchronization Delay Information: 2 octets
+ * EML Capabilities: 2 octets
+ * MLD Capabilities and Operations: 2 octets
+ * AP MLD ID: 1 octet
+ */
+ u8 variable[];
+} STRUCT_PACKED;
+
+#define EHT_ML_LINK_ID_MSK 0x0f
+
+#define EHT_ML_MEDIUM_SYNC_DELAY_DURATION 0x00ff
+#define EHT_ML_MEDIUM_SYNC_DELAY_OFDM_ED_TH 0x0f00
+#define EHT_ML_MEDIUM_SYNC_DELAY_MAX_TXOP 0xf000
+
+#define EHT_ML_EML_CAPA_EMLSR_SUPP 0x0001
+#define EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK 0x000e
+#define EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK 0x0070
+#define EHT_ML_EML_CAPA_EMLMR_SUPP 0x0080
+#define EHT_ML_EML_CAPA_EMLMR_DELAY_MASK 0x0700
+#define EHT_ML_EML_CAPA_TRANSITION_TIMEOUT_MASK 0x7800
+
+#define EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK 0x000f
+#define EHT_ML_MLD_CAPA_SRS_SUPP 0x0010
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL 0x0020
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ONE 0x0040
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK 0x0060
+#define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK 0x0f80
+#define EHT_ML_MLD_CAPA_AAR_SUPP 0x1000
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.2.4 - Per-STA Profile subelement format */
+struct ieee80211_eht_per_sta_profile {
+ le16 sta_control;
+
+ /* Followed by STA Info and STA Profile fields */
+ u8 variable[];
+} STRUCT_PACKED;
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.3 - Probe Request Multi-Link element */
+
+#define EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID 0x0001
+
+struct eht_ml_probe_req_common_info {
+ u8 len;
+
+ /*
+ * Followed by optional fields based on the multi link basic presence
+ * bitmap
+ *
+ * AP MLD ID: 1 octet
+ */
+ u8 variable[];
+} STRUCT_PACKED;
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
+
+#define EHT_ML_PRES_BM_RECONFIGURE_MLD_ADDRESS 0x0001
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.1 - Multi-Link element / General */
+
+struct ieee80211_eht_ml {
+ le16 ml_control;
+
+ /* Followed by Common Info and Link Info fields */
+ u8 variable[];
+} STRUCT_PACKED;
+
+/* Table 9-401c - Optional subelement IDs for Link Info field of the
+ * Multi-Link element */
+enum ieee80211_eht_ml_sub_elem {
+ EHT_ML_SUB_ELEM_PER_STA_PROFILE = 0,
+ EHT_ML_SUB_ELEM_VENDOR = 221,
+ EHT_ML_SUB_ELEM_FRAGMENT = 254,
+};
/* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
#define EDMG_BSS_OPERATING_CHANNELS_OFFSET 6
@@ -2717,4 +2848,15 @@
#define IS_CROSS_AKM_ROAM_KEY_MGMT(key_mgmt) \
((key_mgmt & WPA_KEY_MGMT_CROSS_AKM_ROAM) == WPA_KEY_MGMT_CROSS_AKM_ROAM)
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
+struct ieee80211_neighbor_ap_info {
+ u8 tbtt_info_hdr;
+ u8 tbtt_info_len;
+ u8 op_class;
+ u8 channel;
+
+ /* Followed by the rest of the TBTT Information field contents */
+ u8 data[0];
+} STRUCT_PACKED;
+
#endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ptksa_cache.c b/src/common/ptksa_cache.c
index aacc425..3b5c0b8 100644
--- a/src/common/ptksa_cache.c
+++ b/src/common/ptksa_cache.c
@@ -264,6 +264,7 @@
* @ptk: The PTK
* @life_time_expiry_cb: Callback for alternative expiration handling
* @ctx: Context pointer to save into e->ctx for the callback
+ * @akmp: The key management mechanism that was used to derive the PTK
* Returns: Pointer to the added PTKSA cache entry or %NULL on error
*
* This function creates a PTKSA entry and adds it to the PTKSA cache.
@@ -277,7 +278,7 @@
const struct wpa_ptk *ptk,
void (*life_time_expiry_cb)
(struct ptksa_cache_entry *e),
- void *ctx)
+ void *ctx, u32 akmp)
{
struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
struct os_reltime now;
@@ -302,6 +303,7 @@
entry->cipher = cipher;
entry->cb = life_time_expiry_cb;
entry->ctx = ctx;
+ entry->akmp = akmp;
if (own_addr)
os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
diff --git a/src/common/ptksa_cache.h b/src/common/ptksa_cache.h
index a643a26..6182215 100644
--- a/src/common/ptksa_cache.h
+++ b/src/common/ptksa_cache.h
@@ -26,6 +26,7 @@
u8 own_addr[ETH_ALEN];
void (*cb)(struct ptksa_cache_entry *e);
void *ctx;
+ u32 akmp;
};
#ifdef CONFIG_PTKSA_CACHE
@@ -44,7 +45,7 @@
const struct wpa_ptk *ptk,
void (*cb)
(struct ptksa_cache_entry *e),
- void *ctx);
+ void *ctx, u32 akmp);
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher);
#else /* CONFIG_PTKSA_CACHE */
@@ -73,7 +74,7 @@
static inline struct ptksa_cache_entry *
ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
- void (*cb)(struct ptksa_cache_entry *e), void *ctx)
+ void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp)
{
return NULL;
}
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 215ba91..f972061 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -852,6 +852,48 @@
*
* The attributes used with this command are defined in
* enum qca_wlan_vendor_attr_sr.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT: Subcommand used to
+ * notify application layer about the primary netdev of an MLO connection.
+ * In some implementations, MLO has multiple netdevs out of which one
+ * netdev is designated as primary to provide a unified interface to the
+ * bridge. In those implementations this event is sent on every MLO peer
+ * connection.
+ *
+ * The attributes used with this event are defined in
+ * enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT: This vendor command is used by the
+ * driver to notify different AFC events to userspace. The attributes used
+ * with this command are defined in enum qca_wlan_vendor_attr_afc_event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE: This vendor command is used by
+ * userspace to deliver AFC response data to driver. The attributes used
+ * with this command are defined in enum qca_wlan_vendor_attr_afc_response.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP: Subcommand to configure AP interface to
+ * operate in doze mode.
+ *
+ * Userspace uses this command to configure the AP interface to enter or
+ * exit from doze mode. The driver sends this event after it enters or
+ * exits the doze mode with the updated AP doze mode settings.
+ *
+ * The attributes used with this subcommand are defined in
+ * enum qca_wlan_vendor_attr_dozed_ap.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE: This vendor subcommand is used
+ * to get the status of local packet capture of monitor mode. The monitor
+ * mode can be started using QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE
+ * subcommand.
+ *
+ * The attributes used with this command are defined in enum
+ * qca_wlan_vendor_attr_get_monitor_mode.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS: This vendor command is used to
+ * get roam information from the driver to user space. It provides the
+ * latest several instances of roam information cached in the driver.
+ * The command is only used for STA mode. The attributes used with this
+ * command are defined in enum qca_wlan_vendor_attr_roam_cached_stats.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1056,6 +1098,12 @@
QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG = 218,
QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY = 219,
QCA_NL80211_VENDOR_SUBCMD_SR = 220,
+ QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT = 221,
+ QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT = 222,
+ QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE = 223,
+ QCA_NL80211_VENDOR_SUBCMD_DOZED_AP = 224,
+ QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE = 225,
+ QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS = 226,
};
/* Compatibility defines for previously used subcmd names.
@@ -2656,6 +2704,14 @@
* Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
* disabled, as BK is of the lowest priority and an upgrade to it does
* not result in any changes for the frames.
+ *
+ * If only UDP frames of BE or BK access category needs to be upgraded
+ * without changing the access category of VO or VI UDP frames, refer to
+ * attribute QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE_FOR_BE_BK.
+ *
+ * This attribute is not recommended to be used as it blindly forces all
+ * UDP packets to a higher access category which could impact the
+ * traffic pattern of all apps using UDP and can cause unknown behavior.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
@@ -2781,6 +2837,74 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_DBAM = 83,
+ /* 8-bit unsigned value. This attribute takes the QoS/access category
+ * value represented by the enum qca_wlan_ac_type and expects the driver
+ * to upgrade the UDP frames of BE or BK access category to this access
+ * category. This attribute will not modify UDP frames of VO or VI
+ * access category. The value of QCA_WLAN_AC_ALL is invalid for this
+ * attribute.
+ *
+ * This will override the DSCP value configured in the frame with the
+ * intention to only upgrade the access category. That said, it is not
+ * intended to downgrade the access category for the frames.
+ * Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
+ * disabled, as BK is of the lowest priority and an upgrade to it does
+ * not result in any changes for the frames.
+ *
+ * This attribute behavior is similar to
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE with the difference that
+ * only UDP frames of BE or BK access category are upgraded and not
+ * UDP frames of VI or VO access category.
+ *
+ * This attribute is not recommended to be used as it blindly forces all
+ * UDP packets of BE or BK access category to a higher access category
+ * which could impact the traffic pattern of all apps using UDP and can
+ * cause unknown behavior.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE_FOR_BE_BK = 84,
+
+ /* 8-bit unsigned value to configure the driver to enable/disable the
+ * periodic sounding for Tx beamformer functionality. The default
+ * behavior uses algorithm to do sounding based on packet stats.
+ *
+ * 0 - Default behavior.
+ * 1 - Enable the periodic sounding for Tx beamformer.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_BEAMFORMER_PERIODIC_SOUNDING = 85,
+
+ /* 8-bit unsigned value, whenever wifi calling (wfc) begins or ends,
+ * userspace sends this information to the driver/firmware to configure
+ * wfc state. The driver/firmware uses this information to
+ * optimize power savings, rate adaption, roaming, etc.
+ *
+ * 1 - wfc is on.
+ * 0 - wfc is off.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_WFC_STATE = 86,
+
+ /* 8-bit unsigned value to configure the driver to enable/disable the
+ * EHT EML capability in management frame EHT capabilities.
+ * 1 - Enable, 0 - Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_EML_CAPABILITY = 87,
+
+ /* 8-bit unsigned value to configure the driver with EHT MLO max
+ * simultaneous links to be used for MLO connection.
+ * The range of the value is 0 to 14.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MAX_SIMULTANEOUS_LINKS = 88,
+
+ /* 8-bit unsigned value to configure the driver with EHT MLO maximum
+ * number of links to be used for MLO connection.
+ * The range of the value is 1 to 16.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MAX_NUM_LINKS = 89,
+
+ /* 8-bit unsigned value to configure the driver with EHT MLO mode.
+ * Uses enum qca_wlan_eht_mlo_mode values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MODE = 90,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -5058,6 +5182,42 @@
* If the current RX link speed is above the threshold, roaming is not
* needed. If this attribute is not configured, or if it is set to 0, the
* driver will not consider the RX link speed in the roaming decision.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_HO_DELAY_FOR_RX: u16 value in milliseconds.
+ * Optional parameter. This configuration delays hand-off by the
+ * specified duration to receive pending RX frames from the current BSS.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_NO_REUSE_PARTIAL_SCAN_FREQ: Unsigned 8-bit
+ * value.
+ * During the roam scan, if there are no desired APs found in the partial
+ * frequency list, an immediate full scan on all the supported frequencies
+ * is initiated as a fallback. This flag controls the frequency list
+ * creation for the full scan on the following lines.
+ * 1 - Full scan to exclude the frequencies that were already scanned by
+ * the previous partial scan.
+ * 0 - Full scan to include all the supported frequencies irrespective of
+ * the ones part of the earlier partial scan.
+ * If this flag is not specified, a full scan shall include all the
+ * supported frequencies irrespective of the ones part of an earlier
+ * partial scan.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_6GHZ_ONLY_ON_PRIOR_DISCOVERY: Unsigned 8-bit
+ * value.
+ * During the roam scan, if there are no desired APs found in the partial
+ * frequency list, an immediate full scan on all the supported frequencies
+ * is initiated as a fallback. This full scan would add the 2.4/5/6 GHz
+ * frequencies, including all PSC frequencies by default. This attribute
+ * controls the inclusion of the 6 GHz PSC frequencies for the full scan
+ * as following.
+ * 1 - Full scan to include the supported 6 GHz PSC frequencies only on the
+ * prior discovery of any 6 GHz frequency support in the environment.
+ * This discovery can happen through a prior RNR, 11k neighbor
+ * request, 11v BTM request, host scan, etc.
+ * 0 - Default behavior. Full scan to include all the supported 6 GHz
+ * PSC frequencies regardless of whether 6 GHz BSSs have been
+ * discovered.
+ * The default behavior if this flag is not specified is to include all
+ * the supported 6 GHz PSC frequencies in the roam full scan.
*/
enum qca_vendor_attr_roam_control {
QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
@@ -5084,6 +5244,9 @@
QCA_ATTR_ROAM_CONTROL_SCAN_6G_PSC_DWELL_TIME = 22,
QCA_ATTR_ROAM_CONTROL_SCAN_6G_NON_PSC_DWELL_TIME = 23,
QCA_ATTR_ROAM_CONTROL_LINKSPEED_THRESHOLD = 24,
+ QCA_ATTR_ROAM_CONTROL_HO_DELAY_FOR_RX = 25,
+ QCA_ATTR_ROAM_CONTROL_FULL_SCAN_NO_REUSE_PARTIAL_SCAN_FREQ = 26,
+ QCA_ATTR_ROAM_CONTROL_FULL_SCAN_6GHZ_ONLY_ON_PRIOR_DISCOVERY = 27,
/* keep last */
QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
@@ -5987,6 +6150,22 @@
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
/*
+ * Segment 0 in MHz (u32).
+ *
+ * For 20/40/80 MHz bandwidth, this indicates the channel center
+ * frequency index for the 20/40/80 MHz operating channel.
+ * For 160 MHz bandwidth, this indicates the channel center
+ * frequency of the primary 80 MHz channel.
+ * For 320 MHz bandwidth, indicates the channel center frequency
+ * of the primary 160 MHz channel.
+ *
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
+ * is also maintained.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0 = 12,
+ /* Legacy alias for the Segment 0 attribute.
+ *
* VHT segment 0 in MHz (u32) and the attribute is mandatory.
* Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
@@ -6004,9 +6183,25 @@
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*/
- QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 = 12,
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 =
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0,
/*
+ * Segment 1 in MHz (u32).
+ *
+ * For 20/40/80 MHz bandwidth, this is set to 0.
+ * For 160 MHz bandwidth, indicates the channel center frequency of the
+ * 160 MHz channel.
+ * For 320 MHz bandwidth, indicates the channel center frequency of the
+ * 320 MHz channel.
+ *
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
+ * is also maintained.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1 = 13,
+ /* Legacy alias for the Segment 1 attribute.
+ *
* VHT segment 1 in MHz (u32) and the attribute is mandatory.
* Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
@@ -6024,7 +6219,8 @@
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*/
- QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 = 13,
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 =
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1,
/*
* 16-bit attribute of bits indicating the AP power modes supported by
@@ -6041,6 +6237,33 @@
* qca_wlan_vendor_external_acs_event_chan_power_info_attr.
*/
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR = 15,
+ /*
+ * This indicates the overlapping 320 MHz center frequency in MHz
+ * (u32), if the given primary channel supports more than one
+ * 320 MHz channel bonding.
+ *
+ * Example:
+ * For 6 GHz, channel frequency 6115 MHz (channel number 33) segment 0
+ * center frequency (primary 160 MHz) is 6185 MHz and there can be two
+ * possible segment 2 frequencies for this (320 MHz center
+ * frequencies):
+ *
+ * 1) Center frequency 6105 MHz (channel 31): 320 MHz channel bonding
+ * from frequency 5945 MHz - 6265 MHz
+ * 2) Center frequency 6265 MHz (channel 63): 320 MHz channel bonding
+ * from frequency 6105 MHz - 6425 MHz
+ *
+ * In this case,
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0 will
+ * return 6185 MHz.
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1 will
+ * return 6105 MHz.
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_OVERLAP_SEG_1
+ * will return 6265 MHz.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_OVERLAP_SEG_1
+ = 16,
+
/* keep last */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
@@ -6293,6 +6516,11 @@
* VHT seg1 channel frequency in MHz
* Note: If user-space application has no support of the 6 GHz band, this
* attribute is optional.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_PUNCTURE_BITMAP: Required (u16)
+ * Puncture Bitmap for selected primary channel. Optional if no support
+ * for EHT (IEEE 802.11be). Encoding for this attribute follows the
+ * convention used in the Disabled Subchannel Bitmap field of the EHT Operation
+ * element.
*/
enum qca_wlan_vendor_attr_external_acs_channels {
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
@@ -6328,6 +6556,7 @@
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY = 11,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 = 12,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 = 13,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_PUNCTURE_BITMAP = 14,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
@@ -7110,6 +7339,28 @@
QCA_WLAN_HANG_BUS_FAILURE = 26,
/* tasklet/credit latency found */
QCA_WLAN_HANG_TASKLET_CREDIT_LATENCY_DETECT = 27,
+ /* MSDU buffers received in REO error ring, exceeding certain
+ * threshold
+ */
+ QCA_WLAN_HANG_RX_MSDU_BUF_RCVD_IN_ERR_RING = 28,
+ /* Vdev SM is out of sync and connect req received
+ * when already connected
+ */
+ QCA_WLAN_HANG_VDEV_SM_OUT_OF_SYNC = 29,
+ /* Stats request timeout */
+ QCA_WLAN_HANG_STATS_REQ_TIMEOUT = 30,
+ /* Leak in TX descriptor for a packet */
+ QCA_WLAN_HANG_TX_DESC_LEAK = 31,
+ /* Scheduler watchdog timeout */
+ QCA_WLAN_HANG_SCHED_TIMEOUT = 32,
+ /* Failed to send self peer deletion cmd to firmware */
+ QCA_WLAN_HANG_SELF_PEER_DEL_FAIL = 33,
+ /* Received del self sta without del bss */
+ QCA_WLAN_HANG_DEL_SELF_STA_FAIL = 34,
+ /* Recovery needed when sending flush completion to userspace */
+ QCA_WLAN_HANG_FLUSH_LOGS = 35,
+ /* Host wakeup because of page fault */
+ QCA_WLAN_HANG_HOST_WAKEUP_REASON_PAGE_FAULT = 36,
};
/**
@@ -8240,6 +8491,43 @@
};
/**
+ * enum eht_mcs_config - EHT MCS support configuration
+ *
+ * Configures the EHT Tx/Rx MCS map in EHT Capability element.
+ * These values are used in the driver to configure the EHT MCS map to advertise
+ * Tx/Rx MCS map in EHT capability and these values are applied for all the
+ * streams supported by the device.
+ * @EHT_MCS0_7: EHT MCS 0 to 7 support
+ * @EHT_MCS0_9: EHT MCS 0 to 9 support
+ * @EHT_MCS0_11: EHT MCS 0 to 11 support
+ * @EHT_MCS0_13: EHT MCS 0 to 13 support
+ */
+enum eht_mcs_config {
+ EHT_MCS0_7 = 0,
+ EHT_MCS0_9 = 1,
+ EHT_MCS0_11 = 2,
+ EHT_MCS0_13 = 3,
+};
+
+/**
+ * enum qca_wlan_eht_mlo_mode: EHT MLO mode configuration.
+ * @QCA_WLAN_EHT_MODE_INVALID: Invalid.
+ * @QCA_WLAN_EHT_MLSR: Multi-link single radio mode
+ * @QCA_WLAN_EHT_EMLSR: Enhanced multi-link single radio mode.
+ * @QCA_WLAN_EHT_NON_STR_MLMR: Non simultaneous transmit and receive
+ * multi-link multi radio mode.
+ * @QCA_WLAN_EHT_STR_MLMR: Simultaneous transmit and receive
+ * multi-link multi radio mode.
+ */
+enum qca_wlan_eht_mlo_mode {
+ QCA_WLAN_EHT_MODE_INVALID = 0,
+ QCA_WLAN_EHT_MLSR = 1,
+ QCA_WLAN_EHT_EMLSR = 2,
+ QCA_WLAN_EHT_NON_STR_MLMR = 3,
+ QCA_WLAN_EHT_STR_MLMR = 4,
+};
+
+/**
* enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for
* HE operating mode control transmit request. These attributes are
* sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and
@@ -8831,6 +9119,64 @@
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BEAMFORMER_PERIODIC_SOUNDING = 59,
+ /* 8-bit unsigned value to configure beamformee SS EHT capability
+ * to indicate the maximum number of spatial streams that the STA
+ * can receive in an EHT sounding NDP for <= 80 MHz bandwidth.
+ * The range of the value is 3 to 7.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_80MHZ = 60,
+
+ /* 8-bit unsigned value to configure beamformee SS EHT capability
+ * to indicate the maximum number of spatial streams that the STA
+ * can receive in an EHT sounding NDP for 160 MHz bandwidth.
+ * The range of the value is 3 to 7.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_160MHZ = 61,
+
+ /* 8-bit unsigned value to configure beamformee SS EHT capability
+ * to indicate the maximum number of spatial streams that the STA
+ * can receive in an EHT sounding NDP for 320 MHz bandwidth.
+ * The range of the value is 3 to 7.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_320MHZ = 62,
+
+ /* 8-bit unsigned value to configure the driver to exclude station
+ * profile in Probe Request frame Multi-Link element.
+ * 0 - Default behavior, sends the Probe Request frame with station
+ * profile data included in the Multi-Link element.
+ * 1 - Exclude station profile in Probe Request frame Multi-Link
+ * element.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EXCLUDE_STA_PROF_IN_PROBE_REQ = 63,
+
+ /* 8-bit unsigned value to configure EHT testbed defaults.
+ * This attribute is used to configure the testbed device.
+ * 1 - Set the device EHT capabilities to testbed defaults.
+ * 0 - Reset the device EHT capabilities to supported config.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_EHT_TESTBED_DEFAULTS = 64,
+
+ /* 8-bit unsigned value to indicate the EHT MCS support.
+ * Uses enum eht_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_EHT_MCS = 65,
+
+ /* 8-bit unsigned value to configure EHT TB Sounding Feedback
+ * Rate Limit capability.
+ * This attribute is used to configure the testbed device.
+ * 0 - Indicates no maximum supported data rate limitation.
+ * 1 - Indicates the maximum supported data rate is the lower of
+ * the 1500 Mb/s and the maximum supported data rate.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_TB_SOUNDING_FB_RL = 66,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -11797,11 +12143,15 @@
*
* @QCA_WLAN_CONCURRENT_AP_POLICY_LOSSLESS_AUDIO_STREAMING: Select interface
* concurrencies to meet lossless audio streaming requirements.
+ *
+ * @QCA_WLAN_CONCURRENT_AP_POLICY_XR: Select interface concurrencies to meet
+ * XR (eXtended Reality) requirements.
*/
enum qca_wlan_concurrent_ap_policy_config {
QCA_WLAN_CONCURRENT_AP_POLICY_UNSPECIFIED = 0,
QCA_WLAN_CONCURRENT_AP_POLICY_GAMING_AUDIO = 1,
QCA_WLAN_CONCURRENT_AP_POLICY_LOSSLESS_AUDIO_STREAMING = 2,
+ QCA_WLAN_CONCURRENT_AP_POLICY_XR = 3,
};
/**
@@ -12069,6 +12419,394 @@
};
/**
+ * enum qca_wlan_roam_stats_invoke_reason - Roam invoke reason. These values
+ * are used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_USER_TRIGGER_INVOKE_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_UNDEFINED: Default value when target
+ * invoke roam.
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_NUD_FAILURE: Neighbor unreachable
+ * detection failed when the roam trigger.
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_USER_SPACE: Invoke from user space.
+ */
+
+enum qca_wlan_roam_stats_invoke_reason {
+ QCA_WLAN_ROAM_STATS_INVOKE_REASON_UNDEFINED = 0,
+ QCA_WLAN_ROAM_STATS_INVOKE_REASON_NUD_FAILURE = 1,
+ QCA_WLAN_ROAM_STATS_INVOKE_REASON_USER_SPACE = 2,
+};
+
+/**
+ * enum qca_wlan_roam_stats_tx_failures_reason - Roam TX failures reason. These
+ * values are used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_UNSPECIFIED: Default value when
+ * roam by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_XRETRY: Excessive retry when roam
+ * trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_INACTIVITY: Station inactivity when
+ * roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_IBSS_DISCONNECT: IBSS disconnect when
+ * roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_TDLS_DISCONNECT: TDLS peer has
+ * disappeared, and all TX is failing when roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_SA_QUERY_TIMEOUT: SA query process
+ * timeout when roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_ROAMING_EVENT: Directly connected
+ * peer has roamed to a repeater.
+ */
+enum qca_wlan_roam_stats_tx_failures_reason {
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_UNSPECIFIED = 0,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_XRETRY = 1,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_INACTIVITY = 2,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_IBSS_DISCONNECT = 3,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_TDLS_DISCONNECT = 4,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_SA_QUERY_TIMEOUT = 5,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_ROAMING_EVENT = 6,
+};
+
+/**
+ * enum qca_wlan_roam_stats_abort_reason - Roam abort reason. These values
+ * are used by the attribute %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ABORT_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_ABORT_UNSPECIFIED: Target did not specify the
+ * detailed reason for roam scan being aborted.
+ * @QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_DATA_RSSI_HIGH: Roam scan is not
+ * started due to high data RSSI during LOW-RSSI roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_LINK_SPEED_GOOD: Roam scan is not
+ * started due to good link speed during LOW-RSSI roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_BG_DATA_RSSI_HIGH: Roam scan is not started
+ * due to high data RSSI during background roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_BG_RSSI_ABOVE_THRESHOLD: Roam scan is not
+ * started due to high beacon RSSI during background roaming
+ */
+enum qca_wlan_roam_stats_abort_reason {
+ QCA_WLAN_ROAM_STATS_ABORT_UNSPECIFIED = 0,
+ QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_DATA_RSSI_HIGH = 1,
+ QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_LINK_SPEED_GOOD = 2,
+ QCA_WLAN_ROAM_STATS_ABORT_BG_DATA_RSSI_HIGH = 3,
+ QCA_WLAN_ROAM_STATS_ABORT_BG_RSSI_ABOVE_THRESHOLD = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_scan_chan_info - Attributes used inside
+ * the %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHAN_INFO nested attribute.
+ */
+enum qca_wlan_vendor_attr_roam_stats_scan_chan_info {
+ /* 32-bit unsigned value to indicate center frequency of the primary
+ * channel in MHz for each roam scan channel.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHANNEL_FREQ = 1,
+ /* 8-bit unsigned value to indicate channel scan type for each
+ * roam scan channel. 0-passive, 1-active.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_DWELL_TYPE = 2,
+ /* 32-bit unsigned value to indicate maximum scan time in milliseconds
+ * for each roam scan channel.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_MAX_DWELL_TIME = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_FRAME_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_roam_stats_frame_subtype - Roam frame subtypes. These values
+ * are used by the attribute %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_SUBTYPE.
+ *
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH: Pre-authentication frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC: Reassociation frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1: EAPOL-Key M1 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2: EAPOL-Key M2 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3: EAPOL-Key M3 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4: EAPOL-Key M4 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1: EAPOL-Key GTK M1 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2: EAPOL-Key GTK M2 frame
+ */
+enum qca_wlan_roam_stats_frame_subtype {
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH = 1,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC = 2,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1 = 3,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2 = 4,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3 = 5,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4 = 6,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1 = 7,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2 = 8,
+};
+
+/**
+ * enum roam_frame_status - Specifies the valid values the vendor roam frame
+ * attribute QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_STATUS can take.
+ *
+ * @QCA_WLAN_ROAM_FRAME_STATUS_SUCCESS: It indicates the roam frame was
+ * sent or received successfully.
+ * @QCA_WLAN_ROAM_FRAME_STATUS_FAIL: It indicates the roam frame sending or
+ * receiving failed.
+ */
+enum qca_wlan_roam_stats_frame_status {
+ QCA_WLAN_ROAM_STATS_FRAME_STATUS_SUCCESS = 0,
+ QCA_WLAN_ROAM_STATS_FRAME_STATUS_FAIL = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_frame_info - Attributes used within the
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO nested attribute.
+ */
+enum qca_wlan_vendor_attr_roam_stats_frame_info {
+ /* 8-bit unsigned value to indicate the frame subtype during
+ * roaming, one of the values in qca_wlan_roam_stats_frame_subtype.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_SUBTYPE = 1,
+ /* 8-bit unsigned value to indicate the frame is successful or failed
+ * during roaming, one of the values in
+ * qca_wlan_roam_stats_frame_status.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_STATUS = 2,
+ /* 64-bit unsigned value to indicate the timestamp for frame of
+ * preauthentication/reassociation/EAPOL-M1/EAPOL-M2/EAPOL-M3/EAPOL-M4
+ * when sent or received during roaming, timestamp in milliseconds
+ * from system boot.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_TIMESTAMP = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_info - Used by the attribute
+ * QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INFO.
+ */
+enum qca_wlan_vendor_attr_roam_stats_info {
+ /* 64-bit unsigned value to indicate the timestamp when roam was
+ * triggered by the firmware, timestamp in milliseconds from system
+ * boot.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_TRIGGER_TIMESTAMP = 1,
+ /* 32-bit unsigned value to indicate the roam trigger reason for the
+ * last roaming attempted by the firmware. This can be queried either
+ * in a connected state or disconnected state. The values of this
+ * attribute represent the roam trigger reason codes, which
+ * are defined in enum qca_roam_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TRIGGER_REASON = 2,
+ /* 8-bit unsigned value to indicate percentage of packets for which
+ * the RX rate is lower than the RX rate threshold in total RX packets,
+ * used for roaming trigger by per.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PER_RXRATE_THRESHOLD_PERCENT = 3,
+ /* 8-bit unsigned value to indicate percentage of packets for which
+ * the TX rate is lower than TX rate threshold in total TX packets,
+ * used for roaming trigger by per.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PER_TXRATE_THRESHOLD_PERCENT = 4,
+ /* 32-bit unsigned value to indicate final beacon miss count for
+ * trigger reason of beacon miss.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FINAL_BMISS_CNT = 5,
+ /* 32-bit unsigned value to indicate consecutive beacon miss count
+ * for trigger reason of beacon miss.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONSECUTIVE_BMISS_CNT = 6,
+ /* 8-bit unsigned value to indicate QOS-NULL TX status for trigger
+ * reason of beacon miss, 0 - success, 1 - fail.
+ * If QOS-NULL TX status is successful, beacon miss final count and
+ * consecutive beacon miss count will be reset to zero, and roam will
+ * not be triggered. If QOS-NULL TX status is failed, beacon miss final
+ * count and consecutive beacon miss count continue to calculate until
+ * roaming trigger by beacon miss.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BMISS_QOS_NULL_SUCCESS = 7,
+ /* 8-bit unsigned value to indicate connected AP RSSI in dBm
+ * for trigger reason of poor RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_CURRENT_RSSI = 8,
+ /* 8-bit unsigned value to indicate RSSI threshold value in dBm
+ * for trigger reason of poor RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_ROAM_RSSI_THRESHOLD = 9,
+ /* 8-bit unsigned value to indicate RX link speed status
+ * for trigger reason of poor RSSI, 0 - good link speed,
+ * 1 - bad link speed.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_RX_LINKSPEED_STATUS = 10,
+ /* 8-bit unsigned value to indicate connected AP RSSI in dBm
+ * for trigger reason of better RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BETTER_RSSI_CURRENT_RSSI = 11,
+ /* 8-bit unsigned value to indicate RSSI threshold value in dBm
+ * for trigger reason of better RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BETTER_RSSI_HIGH_RSSI_THRESHOLD = 12,
+ /* 32-bit unsigned value to indicate RX throughput in bytes per second
+ * for trigger reason of congestion.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_RX_TPUT = 13,
+ /* 32-bit unsigned value to indicate TX throughput in bytes per second
+ * for trigger reason of congestion.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_TX_TPUT = 14,
+ /* 8-bit unsigned value to indicate roamable AP count
+ * for trigger reason of congestion.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_ROAMABLE_CNT = 15,
+ /* 8-bit unsigned value to indicate invoke reason, one of the values
+ * defined in qca_wlan_roam_stats_invoke_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_USER_TRIGGER_INVOKE_REASON = 16,
+ /* 8-bit unsigned value to indicate request mode for trigger reason
+ * of BTM, values are defined in IEEE Std 802.11-2020, 9.6.13.9.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_REQUEST_MODE = 17,
+ /* 32-bit unsigned value to indicate disassociate time in milliseconds
+ * for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_DISASSOC_IMMINENT_TIME = 18,
+ /* 32-bit unsigned value to indicate preferred candidate list valid
+ * interval in milliseconds for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_VALID_INTERNAL = 19,
+ /* 8-bit unsigned value to indicate the number of preferred
+ * candidates for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_CANDIDATE_LIST_CNT = 20,
+ /* 8-bit unsigned value to indicate response status for trigger
+ * reason of BTM, values are defined in IEEE Std 802.11-2020,
+ * Table 9-428 (BTM status code definitions).
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_RESPONSE_STATUS_CODE = 21,
+ /* 32-bit unsigned value to indicate BSS termination timeout value
+ * in milliseconds for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_BSS_TERMINATION_TIMEOUT = 22,
+ /* 32-bit unsigned value to indicate MBO associate retry timeout
+ * value in milliseconds for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_MBO_ASSOC_RETRY_TIMEOUT = 23,
+ /* 8-bit unsigned value to indicate dialog token number
+ * for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_REQ_DIALOG_TOKEN = 24,
+ /* 8-bit unsigned value to indicate percentage of connected AP
+ * channel congestion utilization for trigger reason of BSS load.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BSS_CU_LOAD = 25,
+ /* 8-bit unsigned value to indicate disconnection type
+ * for trigger reason of disconnection. 1 - Deauthentication,
+ * 2 - Disassociation.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DISCONNECTION_TYPE = 26,
+ /* 16-bit unsigned value to indicate deauthentication or disassociation
+ * reason for trigger reason of disconnection, values are defined
+ * in IEEE Std 802.11-2020, Table 9-49 (Reason codes).
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DISCONNECTION_REASON = 27,
+ /* 32-bit unsigned value to indicate milliseconds of roam scan
+ * periodicity when needing to roam to find a better AP for trigger
+ * reason of periodic timer.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PERIODIC_TIMER_MS = 28,
+ /* 8-bit unsigned value to indicate connected AP RSSI in dBm for
+ * trigger reason of background scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_CURRENT_RSSI = 29,
+ /* 8-bit unsigned value to indicate data RSSI in dBm for trigger reason
+ * of background scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_DATA_RSSI = 30,
+ /* 8-bit unsigned value to indicate data RSSI threshold in dBm
+ * for trigger reason of background scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_DATA_RSSI_THRESH = 31,
+ /* 32-bit unsigned value to indicate consecutive TX failure threshold
+ * for trigger reason of TX failures.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_THRESHOLD = 32,
+ /* 8-bit unsigned value to indicate TX failure reason for trigger
+ * reason of TX failures, one of the values defined in
+ * qca_wlan_roam_stats_tx_failures_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_REASON = 33,
+ /* 8-bit unsigned value to indicate detail abort reason. One of the
+ * values in enum qca_wlan_roam_stats_abort_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ABORT_REASON = 34,
+ /* 8-bit unsigned value to indicate data RSSI in dBm when aborting the
+ * roam scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RSSI = 35,
+ /* 8-bit unsigned value to indicate data RSSI threshold in dBm when
+ * aborting the roam scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RSSI_THRESHOLD = 36,
+ /* 8-bit unsigned value to indicate data RSSI threshold in RX link
+ * speed status when aborting the roam scan.
+ * 0 - good link speed, 1 - bad link speed
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RX_LINKSPEED_STATUS = 37,
+ /* 8-bit unsigned value to indicate roaming scan type.
+ * 0 - Partial roam scan, 1 - Full roam scan
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_TYPE = 38,
+ /* 8-bit unsigned value to indicate roaming result, used in STA mode
+ * only.
+ * 0-Roaming is successful, 1-Roaming is failed
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_STATUS = 39,
+ /* 8-bit unsigned value to indicate the roam fail reason for the
+ * last failed roaming attempt by the firmware. Different roam failure
+ * reason codes are specified in enum qca_vendor_roam_fail_reasons.
+ * This can be queried either in connected state or disconnected state.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FAIL_REASON = 40,
+ /* Nested attribute. Indicate roam scan info for each channel, the
+ * attributes defined in enum
+ * qca_wlan_vendor_attr_roam_stats_scan_chan_info are used inside
+ * this attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHAN_INFO = 41,
+ /* 32-bit unsigned value to indicate total scan time during roam scan
+ * all channels, time in milliseconds.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TOTAL_SCAN_TIME = 42,
+ /* Nested attribute. This attribute shall be used by the driver to
+ * send roam information of each subtype. The attributes defined in
+ * enum qca_wlan_vendor_attr_roam_stats_frame_info are used inside
+ * this attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO = 43,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_cached_stats - Vendor subcmd attributes to
+ * report cached roam info from the driver to user space, enum values are used
+ * for netlink attributes sent with the
+ * %QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS sub command.
+ */
+enum qca_wlan_vendor_attr_roam_cached_stats {
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INVALID = 0,
+ /* Nested attribute, this attribute contains nested array roam info
+ * statistics defined in enum qca_wlan_vendor_attr_roam_stats_info.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INFO = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_supported_radio_cfg - Attributes for
* radio configurations present in each radio combination.
*
@@ -13190,6 +13928,17 @@
* QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER attribute with the
* filter mask specified by the
* QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_SERVICE_CLASS_ID: Optional u16
+ * attribute.
+ * Represents the service class id of the configured SCS rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DST_MAC_ADDR: Optional 6 bytes
+ * MAC address.
+ * Represents the destination MAC address in the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_NETDEV_IF_INDEX: Optional u32 attribute
+ * Represents the netdevice interface index in the rule.
*/
enum qca_wlan_vendor_attr_scs_rule_config {
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_INVALID = 0,
@@ -13211,6 +13960,9 @@
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER = 16,
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK = 17,
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_VALUE = 18,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_SERVICE_CLASS_ID = 19,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DST_MAC_ADDR = 20,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_NETDEV_IF_INDEX = 21,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_AFTER_LAST,
@@ -13561,4 +14313,531 @@
QCA_WLAN_VENDOR_ATTR_SR_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event - Defines the attributes
+ * used in the QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT subcommand.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MACADDR: 6 byte MAC address
+ * used by the peer on the link that corresponds to the link used for sending
+ * the event notification.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_MAC_ADDR: 6 byte
+ * MLD MAC address of the peer.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_PRIM_IFINDEX: u32 attribute,
+ * used to pass ifindex of the primary netdev.
+ */
+enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event {
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MACADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_MAC_ADDR = 2,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_PRIM_IFINDEX = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MAX =
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_freq_psd_info: This enum is used with
+ * nested attributes QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST to update the frequency range
+ * and PSD information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START: Required and type is
+ * u32. This attribute is used to indicate the start of the queried frequency
+ * range in MHz.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END: Required and type is u32.
+ * This attribute is used to indicate the end of the queried frequency range
+ * in MHz.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD: Required and type is u32.
+ * This attribute will contain the PSD information for a single range as
+ * specified by the QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START and
+ * QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END attributes.
+ *
+ * The PSD power info (dBm/MHz) from user space should be multiplied
+ * by a factor of 100 when sending to the driver to preserve granularity
+ * up to 2 decimal places.
+ * Example:
+ * PSD power value: 10.21 dBm/MHz
+ * Value to be updated in QCA_WLAN_VENDOR_ATTR_AFC_PSD_INFO: 1021.
+ *
+ * Note: QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD attribute will be used only
+ * with nested attribute QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO and with
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE.
+ *
+ * The following set of attributes will be used to exchange frequency and
+ * corresponding PSD information for AFC between the user space and the driver.
+ */
+enum qca_wlan_vendor_attr_afc_freq_psd_info {
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END = 2,
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD = 3,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info: This enum is used with
+ * nested attribute QCA_WLAN_VENDOR_ATTR_AFC_CHAN_LIST_INFO to update the
+ * channel list and corresponding EIRP information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM: Required and type is u8.
+ * This attribute is used to indicate queried channel from
+ * the operating class indicated in QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP: Optional and type is u32.
+ * This attribute is used to configure the EIRP power info corresponding
+ * to the channel number indicated in QCA_WLAN_VENDOR_ATTR_AFC_CHAN_NUM.
+ * The EIRP power info(dBm) from user space should be multiplied
+ * by a factor of 100 when sending to Driver to preserve granularity up to
+ * 2 decimal places.
+ * Example:
+ * EIRP power value: 34.23 dBm
+ * Value to be updated in QCA_WLAN_VENDOR_ATTR_AFC_EIRP_INFO: 3423.
+ *
+ * Note: QCA_WLAN_VENDOR_ATTR_AFC_EIRP_INFO attribute will only be used with
+ * nested attribute QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * with QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE:
+ *
+ * The following set of attributes will be used to exchange Channel and
+ * corresponding EIRP information for AFC between the user space and Driver.
+ */
+enum qca_wlan_vendor_attr_afc_chan_eirp_info {
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP = 2,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_opclass_info: This enum is used with nested
+ * attributes QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_REQ_OPCLASS_CHAN_INFO to update the operating class,
+ * channel, and EIRP related information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS: Required and type is u8.
+ * This attribute is used to indicate the operating class, as listed under
+ * IEEE Std 802.11-2020 Annex E Table E-4, for the queried channel list.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST: Array of nested attributes
+ * for updating the channel number and EIRP power information.
+ * It uses the attributes defined in
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info.
+ *
+ * Operating class information packing format for
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_INFO when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE_EXPIRY.
+ *
+ * m - Total number of operating classes.
+ * n, j - Number of queried channels for the corresponding operating class.
+ *
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ * .....
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[n - 1]
+ * ....
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[m]
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[m]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ * ....
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[j - 1]
+ *
+ * Operating class information packing format for
+ * QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_INFO when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE.
+ *
+ * m - Total number of operating classes.
+ * n, j - Number of channels for the corresponding operating class.
+ *
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[0]
+ * .....
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[n - 1]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[n - 1]
+ * ....
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[m]
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[m]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[0]
+ * ....
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[j - 1]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[j - 1]
+ *
+ * The following set of attributes will be used to exchange operating class
+ * information for AFC between the user space and the driver.
+ */
+enum qca_wlan_vendor_attr_afc_opclass_info {
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST = 2,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_event_type: Defines values for AFC event type.
+ * Attribute used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY: AFC expiry event sent from the
+ * driver to userspace in order to query the new AFC power values.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE: Power update
+ * complete event will be sent from the driver to userspace to indicate
+ * processing of the AFC response.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_PAYLOAD_RESET: AFC payload reset event
+ * will be sent from the driver to userspace to indicate last received
+ * AFC response data has been cleared on the AP due to invalid data
+ * in the QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE.
+ *
+ * The following enum defines the different event types that will be
+ * used by the driver to help trigger corresponding AFC functionality in user
+ * space.
+ */
+enum qca_wlan_vendor_afc_event_type {
+ QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY = 0,
+ QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE = 1,
+ QCA_WLAN_VENDOR_AFC_EVENT_TYPE_PAYLOAD_RESET = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_ap_deployment_type: Defines values for AP
+ * deployment type.
+ * Attribute used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_UNKNOWN: Unknown AP deployment.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_INDOOR: Indoor AP deployment.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_OUTDOOR: Outdoor AP deployment.
+ *
+ * The following enum defines different deployment modes that the AP might
+ * come up in. This information will be essential to retrieve deployment-type
+ * specific SP power values for AFC operation.
+ */
+enum qca_wlan_vendor_afc_ap_deployment_type {
+ QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_UNKNOWN = 0,
+ QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_INDOOR = 1,
+ QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_OUTDOOR = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_evt_status_code: Defines values AP will use to
+ * indicate AFC response status.
+ * Enum used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_SUCCESS: Success
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_TIMEOUT: Indicates AFC indication
+ * command was not received within the expected time of the AFC expiry event
+ * being triggered.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_PARSING_ERROR: Indicates AFC data
+ * parsing error by the driver.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_LOCAL_ERROR: Indicates any other local
+ * error.
+ *
+ * The following enum defines the status codes that the driver will use to
+ * indicate whether the AFC data is valid or not.
+ */
+enum qca_wlan_vendor_afc_evt_status_code {
+ QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_SUCCESS = 0,
+ QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_TIMEOUT = 1,
+ QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_PARSING_ERROR = 2,
+ QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_LOCAL_ERROR = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_event: Defines attributes to be used with
+ * vendor event QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT. These attributes will
+ * support sending only a single request to the user space at a time.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE: Required u8 attribute.
+ * Used with event to notify the type of AFC event received.
+ * Valid values are defined in enum qca_wlan_vendor_afc_event_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT: u8 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY,
+ * otherwise unused.
+ *
+ * This attribute is used to indicate the AP deployment type in the AFC request.
+ * Valid values are defined in enum qca_wlan_vendor_afc_ap_deployment_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID: Required u32 attribute.
+ * Unique request identifier generated by the AFC client for every
+ * AFC expiry event trigger. See also QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID.
+ * The user space application is responsible for ensuring no duplicate values
+ * are in-flight with the server, e.g., by delaying a request, should the same
+ * value be received from different radios in parallel.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFC_WFA_VERSION: u32 attribute. Optional.
+ * It is used when the QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY, otherwise unused.
+ *
+ * This attribute indicates the AFC spec version information. This will
+ * indicate the AFC version AFC client must use to query the AFC data.
+ * Bits 15:0 - Minor version
+ * Bits 31:16 - Major version
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MIN_DES_POWER: u16 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY,
+ * otherwise unused.
+ * This attribute indicates the minimum desired power (in dBm) for
+ * the queried spectrum.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE: u8 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * Valid values are defined in enum qca_wlan_vendor_afc_evt_status_code.
+ * This attribute is used to indicate if there were any errors parsing the
+ * AFC response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_SERVER_RESP_CODE: s32 attribute. Required
+ * when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the AFC response code. The AFC response codes are
+ * in the following categories:
+ * -1: General Failure.
+ * 0: Success.
+ * 100 - 199: General errors related to protocol.
+ * 300 - 399: Error events specific to message exchange
+ * for the Available Spectrum Inquiry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_DATE: u32 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the date until which the current response is
+ * valid for in UTC format.
+ * Date format: bits 7:0 - DD (Day 1-31)
+ * bits 15:8 - MM (Month 1-12)
+ * bits 31:16 - YYYY (Year)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_TIME: u32 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the time until which the current response is
+ * valid for in UTC format.
+ * Time format: bits 7:0 - SS (Seconds 0-59)
+ * bits 15:8 - MM (Minutes 0-59)
+ * bits 23:16 - HH (Hours 0-23)
+ * bits 31:24 - Reserved
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST: Array of nested attributes
+ * for updating the list of frequency ranges to be queried.
+ * Required when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY or
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ * It uses the attributes defined in
+ * enum qca_wlan_vendor_attr_afc_freq_psd_info.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST: Array of nested attributes
+ * for updating the list of operating classes and corresponding channels to be
+ * queried.
+ * Required when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY or
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ * It uses the attributes defined in enum qca_wlan_vendor_attr_afc_opclass_info.
+ */
+enum qca_wlan_vendor_attr_afc_event {
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT = 2,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFC_WFA_VERSION = 4,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MIN_DES_POWER = 5,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE = 6,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_SERVER_RESP_CODE = 7,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_DATE = 8,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_TIME = 9,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST = 10,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST = 11,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_response: Defines attributes to be used
+ * with vendor command QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE. These attributes
+ * will support sending only a single AFC response to the driver at a time.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA: Type is NLA_STRING. Required attribute.
+ * This attribute will be used to send a single Spectrum Inquiry response object
+ * from the 'availableSpectrumInquiryResponses' array object from the response
+ * JSON.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE: Required u32 attribute.
+ *
+ * This attribute indicates the period (in seconds) for which the response
+ * data received is valid for.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID: Required u32 attribute.
+ *
+ * This attribute indicates the request ID for which the corresponding
+ * response is being sent for. See also QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE: Required u32 attribute.
+ *
+ * This attribute indicates the date until which the current response is
+ * valid for in UTC format.
+ * Date format: bits 7:0 - DD (Day 1-31)
+ * bits 15:8 - MM (Month 1-12)
+ * bits 31:16 - YYYY (Year)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME: Required u32 attribute.
+ *
+ * This attribute indicates the time until which the current response is
+ * valid for in UTC format.
+ * Time format: bits 7:0 - SS (Seconds 0-59)
+ * bits 15:8 - MM (Minutes 0-59)
+ * bits 23:16 - HH (Hours 0-23)
+ * bits 31:24 - Reserved
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE: Required s32 attribute.
+ *
+ * This attribute indicates the AFC response code. The AFC response codes are
+ * in the following categories:
+ * -1: General Failure.
+ * 0: Success.
+ * 100 - 199: General errors related to protocol.
+ * 300 - 399: Error events specific to message exchange
+ * for the Available Spectrum Inquiry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO: Array of nested attributes
+ * for PSD info of all the queried frequency ranges. It uses the attributes
+ * defined in enum qca_wlan_vendor_attr_afc_freq_psd_info. Required attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO: Array of nested
+ * attributes for EIRP info of all queried operating class/channels. It uses
+ * the attributes defined in enum qca_wlan_vendor_attr_afc_opclass_info and
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info. Required attribute.
+ *
+ */
+enum qca_wlan_vendor_attr_afc_response {
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE = 2,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE = 4,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME = 5,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE = 6,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO = 7,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO = 8,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_dozed_ap_state - Doze states for AP interface
+ *
+ * @QCA_WLAN_DOZED_AP_DISABLE: Disable doze state on the AP interface.
+ *
+ * @QCA_WLAN_DOZED_AP_ENABLE: Enable doze state on the AP interface. AP starts
+ * beaconing at higher beacon interval with Rx disabled.
+ */
+enum qca_wlan_dozed_ap_state {
+ QCA_WLAN_DOZED_AP_DISABLE = 0,
+ QCA_WLAN_DOZED_AP_ENABLE = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_dozed_ap - Used by the vendor command
+ * @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP to configure or receive dozed AP mode
+ * configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE: u8 attribute.
+ * Configures the doze state for an AP interface. Possible values are defined
+ * in enum qca_wlan_dozed_ap_state. @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP event
+ * gets triggered asynchronously to provide updated AP interface configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE: Unsigned 64-bit cookie provided by
+ * the driver in the response to specific @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP
+ * command, which is used later to maintain synchronization between commands
+ * and asynchronous events.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_NEXT_TSF: u64 attribute.
+ * Used in event to indicate the next TBTT TSF timer value after applying the
+ * doze mode configuration. Next TBTT TSF is the time at which the AP sends
+ * the first beacon after entering or exiting dozed mode.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_BI_MULTIPLIER: u16 attribute.
+ * Used with event to inform the periodicity of beacon transmission that would
+ * be skipped at all TBTTs in between.
+ */
+enum qca_wlan_vendor_attr_dozed_ap {
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE = 1,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE = 2,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_NEXT_TSF = 3,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_BI_MULTIPLIER = 4,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_MAX =
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_monitor_mode_status - Represents the status codes
+ * used with QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE.
+ * @QCA_WLAN_VENDOR_MONITOR_MODE_NO_CAPTURE_RUNNING: Used to indicate no
+ * capture running status.
+ * @QCA_WLAN_VENDOR_MONITOR_MODE_CAPTURE_RUNNING: Used to indicate
+ * capture running status.
+ **/
+
+enum qca_wlan_vendor_monitor_mode_status {
+ QCA_WLAN_VENDOR_MONITOR_MODE_NO_CAPTURE_RUNNING = 0,
+ QCA_WLAN_VENDOR_MONITOR_MODE_CAPTURE_RUNNING = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_monitor_mode - Used by the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE to report
+ * information regarding the local packet capture over the monitor mode.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_STATUS: u32 attribute. This attribute
+ * represents the status of the start capture commands. The values used with
+ * this attribute are defined in enum qca_wlan_vendor_monitor_mode_status. This
+ * is returned by the driver in the response to the command.
+ */
+
+enum qca_wlan_vendor_attr_get_monitor_mode {
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_STATUS = 1,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_MAX =
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 33c7e5f..d4a196f 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -603,9 +603,9 @@
case 30:
*z = 7;
return 0;
+ default:
+ return -1;
}
-
- return -1;
}
@@ -1606,7 +1606,9 @@
* (commit-scalar + peer-commit-scalar) mod r part as a bit string by
* zero padding it from left to the length of the order (in full
* octets). */
- crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
+ if (crypto_bignum_to_bin(tmp, val, sizeof(val),
+ sae->tmp->order_len) < 0)
+ goto fail;
wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
#ifdef CONFIG_SAE_PK
@@ -1967,8 +1969,10 @@
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);
- if (sae->tmp->peer_commit_element_ecc == NULL)
+ if (!sae->tmp->peer_commit_element_ecc) {
+ wpa_printf(MSG_DEBUG, "SAE: Peer element is not a valid point");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
sae->tmp->peer_commit_element_ecc)) {
@@ -2153,7 +2157,7 @@
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups,
- int h2e)
+ int h2e, int *ie_offset)
{
const u8 *pos = data, *end = data + len;
u16 res;
@@ -2179,6 +2183,9 @@
if (res != WLAN_STATUS_SUCCESS)
return res;
+ if (ie_offset)
+ *ie_offset = pos - data;
+
/* Optional Password Identifier element */
res = sae_parse_password_identifier(sae, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
@@ -2372,7 +2379,8 @@
}
-int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len,
+ int *ie_offset)
{
u8 verifier[SAE_MAX_HASH_LEN];
size_t hash_len;
@@ -2428,6 +2436,10 @@
return -1;
#endif /* CONFIG_SAE_PK */
+ /* 2 bytes are for send-confirm, then the hash, followed by IEs */
+ if (ie_offset)
+ *ie_offset = 2 + hash_len;
+
return 0;
}
diff --git a/src/common/sae.h b/src/common/sae.h
index 6a6b0c8..c446da3 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -136,9 +136,10 @@
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,
- int h2e);
+ int h2e, int *ie_offset);
int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
-int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len,
+ int *ie_offset);
u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
const char * sae_state_txt(enum sae_state state);
size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index da707e6..15ebcab 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -882,7 +882,7 @@
#ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+int wpa_ft_mic(int key_mgmt, const u8 *kck, size_t kck_len, const u8 *sta_addr,
const u8 *ap_addr, u8 transaction_seqnum,
const u8 *mdie, size_t mdie_len,
const u8 *ftie, size_t ftie_len,
@@ -894,8 +894,9 @@
const u8 *addr[10];
size_t len[10];
size_t i, num_elem = 0;
- u8 zero_mic[24];
+ u8 zero_mic[32];
size_t mic_len, fte_fixed_len;
+ int res;
if (kck_len == 16) {
mic_len = 16;
@@ -903,6 +904,10 @@
} else if (kck_len == 24) {
mic_len = 24;
#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+ } else if (kck_len == 32) {
+ mic_len = 32;
+#endif /* CONFIG_SHA512 */
} else {
wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
(unsigned int) kck_len);
@@ -967,6 +972,17 @@
for (i = 0; i < num_elem; i++)
wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (kck_len == 32) {
+ u8 hash[SHA512_MAC_LEN];
+
+ if (hmac_sha512_vector(kck, kck_len, num_elem, addr, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 32);
+ res = 0;
+ }
+#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA384
if (kck_len == 24) {
u8 hash[SHA384_MAC_LEN];
@@ -974,26 +990,34 @@
if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash))
return -1;
os_memcpy(mic, hash, 24);
+ res = 0;
}
#endif /* CONFIG_SHA384 */
- if (kck_len == 16 &&
- omac1_aes_128_vector(kck, num_elem, addr, len, mic))
- return -1;
+ if (kck_len == 16 && key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ u8 hash[SHA256_MAC_LEN];
- return 0;
+ if (hmac_sha256_vector(kck, kck_len, num_elem, addr, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 16);
+ res = 0;
+ }
+ if (kck_len == 16 && key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ omac1_aes_128_vector(kck, num_elem, addr, len, mic) == 0)
+ res = 0;
+
+ return res;
}
static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
- struct wpa_ft_ies *parse, int use_sha384)
+ struct wpa_ft_ies *parse, const u8 *opt)
{
const u8 *end, *pos;
parse->ftie = ie;
parse->ftie_len = ie_len;
- pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) :
- sizeof(struct rsn_ftie));
+ pos = opt;
end = ie + ie_len;
wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
@@ -1004,7 +1028,7 @@
len = *pos++;
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "FT: Truncated subelement");
- break;
+ return -1;
}
switch (id) {
@@ -1016,8 +1040,11 @@
return -1;
}
parse->r1kh_id = pos;
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
+ parse->r1kh_id, FT_R1KH_ID_LEN);
break;
case FTIE_SUBELEM_GTK:
+ wpa_printf(MSG_DEBUG, "FT: GTK");
parse->gtk = pos;
parse->gtk_len = len;
break;
@@ -1030,8 +1057,11 @@
}
parse->r0kh_id = pos;
parse->r0kh_id_len = len;
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
+ parse->r0kh_id, parse->r0kh_id_len);
break;
case FTIE_SUBELEM_IGTK:
+ wpa_printf(MSG_DEBUG, "FT: IGTK");
parse->igtk = pos;
parse->igtk_len = len;
break;
@@ -1039,9 +1069,12 @@
case FTIE_SUBELEM_OCI:
parse->oci = pos;
parse->oci_len = len;
+ wpa_hexdump(MSG_DEBUG, "FT: OCI",
+ parse->oci, parse->oci_len);
break;
#endif /* CONFIG_OCV */
case FTIE_SUBELEM_BIGTK:
+ wpa_printf(MSG_DEBUG, "FT: BIGTK");
parse->bigtk = pos;
parse->bigtk_len = len;
break;
@@ -1057,20 +1090,73 @@
}
-int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse, int use_sha384)
+static int wpa_ft_parse_fte(int key_mgmt, const u8 *ie, size_t len,
+ struct wpa_ft_ies *parse)
+{
+ size_t mic_len;
+ u8 mic_len_info;
+ const u8 *pos = ie;
+ const u8 *end = pos + len;
+
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", pos, 2);
+ parse->fte_rsnxe_used = pos[0] & FTE_MIC_CTRL_RSNXE_USED;
+ mic_len_info = (pos[0] & FTE_MIC_CTRL_MIC_LEN_MASK) >>
+ FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ parse->fte_elem_count = pos[1];
+ pos += 2;
+
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ switch (mic_len_info) {
+ case FTE_MIC_LEN_16:
+ mic_len = 16;
+ break;
+ case FTE_MIC_LEN_24:
+ mic_len = 24;
+ break;
+ case FTE_MIC_LEN_32:
+ mic_len = 32;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "FT: Unknown MIC Length subfield value %u",
+ mic_len_info);
+ return -1;
+ }
+ } else {
+ mic_len = wpa_key_mgmt_sha384(key_mgmt) ? 24 : 16;
+ }
+ if (mic_len > (size_t) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "FT: No room for %zu octet MIC in FTE",
+ mic_len);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", pos, mic_len);
+ parse->fte_mic = pos;
+ parse->fte_mic_len = mic_len;
+ pos += mic_len;
+
+ if (2 * WPA_NONCE_LEN > end - pos)
+ return -1;
+ parse->fte_anonce = pos;
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
+ parse->fte_anonce, WPA_NONCE_LEN);
+ pos += WPA_NONCE_LEN;
+ parse->fte_snonce = pos;
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
+ parse->fte_snonce, WPA_NONCE_LEN);
+ pos += WPA_NONCE_LEN;
+
+ return wpa_ft_parse_ftie(ie, len, parse, pos);
+}
+
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
+ int key_mgmt)
{
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)
@@ -1104,11 +1190,8 @@
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;
- }
+ if (!key_mgmt)
+ key_mgmt = parse->key_mgmt;
break;
case WLAN_EID_RSNX:
wpa_hexdump(MSG_DEBUG, "FT: RSNXE", pos, len);
@@ -1126,47 +1209,19 @@
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;
+ /* The first two octets (MIC Control field) is in the
+ * same offset for all cases, but the second field (MIC)
+ * has variable length with three different values.
+ * In particular the FT-SAE-EXT-KEY is inconvinient to
+ * parse, so try to handle this in pieces instead of
+ * using the struct rsn_ftie* definitions. */
- 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));
- parse->fte_anonce = ftie_sha384->anonce;
- wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
- ftie_sha384->anonce,
- WPA_NONCE_LEN);
- parse->fte_snonce = ftie_sha384->snonce;
- 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))
+ if (len < 2)
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));
- parse->fte_anonce = ftie->anonce;
- wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
- ftie->anonce, WPA_NONCE_LEN);
- parse->fte_snonce = ftie->snonce;
- 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) < 0)
+ prot_ie_count = pos[1]; /* Element Count field in
+ * MIC Control */
+
+ if (wpa_ft_parse_fte(key_mgmt, pos, len, parse) < 0)
return -1;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
@@ -1249,7 +1304,7 @@
* PASN frame. SHA-256 is used as the hash algorithm, except for the ciphers
* 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used.
*/
-static bool pasn_use_sha384(int akmp, int cipher)
+bool pasn_use_sha384(int akmp, int cipher)
{
return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 ||
cipher == WPA_CIPHER_GCMP_256)) ||
@@ -1988,30 +2043,40 @@
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 key_mgmt)
{
u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
FT_R0KH_ID_MAX_LEN + ETH_ALEN];
- u8 *pos, r0_key_data[64], hash[48];
+ u8 *pos, r0_key_data[64 + 16], hash[64];
const u8 *addr[2];
size_t len[2];
- size_t q = use_sha384 ? 48 : 32;
- size_t r0_key_data_len = q + 16;
+ size_t q, r0_key_data_len;
+ int res;
+
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (xxkey_len == SHA256_MAC_LEN || xxkey_len == SHA384_MAC_LEN ||
+ xxkey_len == SHA512_MAC_LEN))
+ q = xxkey_len;
+ else if (wpa_key_mgmt_sha384(key_mgmt))
+ q = SHA384_MAC_LEN;
+ else
+ q = SHA256_MAC_LEN;
+ r0_key_data_len = q + 16;
/*
- * R0-Key-Data = KDF-384(XXKey, "FT-R0",
+ * R0-Key-Data = KDF-Hash-Length(XXKey, "FT-R0",
* SSIDlength || SSID || MDID || R0KHlength ||
* R0KH-ID || S0KH-ID)
* XXKey is either the second 256 bits of MSK or PSK; or the first
- * 384 bits of MSK for FT-EAP-SHA384.
+ * 384 bits of MSK for FT-EAP-SHA384; or PMK from SAE.
* 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
+ * Q = 384 for FT-EAP-SHA384; the length of the digest generated by H()
+ * for FT-SAE-EXT-KEY; or otherwise, 256
*/
if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
return -1;
- wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s",
- use_sha384 ? "SHA384" : "SHA256");
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-SHA%zu", q * 8);
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);
@@ -2029,30 +2094,43 @@
os_memcpy(pos, s0kh_id, ETH_ALEN);
pos += ETH_ALEN;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (q == SHA512_MAC_LEN) {
+ if (xxkey_len != SHA512_MAC_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected XXKey length %d (expected %d)",
+ (int) xxkey_len, SHA512_MAC_LEN);
+ return -1;
+ }
+ res = sha512_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len);
+ }
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (use_sha384) {
+ if (q == SHA384_MAC_LEN) {
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;
+ res = sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len);
}
#endif /* CONFIG_SHA384 */
- if (!use_sha384) {
+ if (q == SHA256_MAC_LEN) {
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;
+ res = sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len);
}
+ if (res < 0)
+ return res;
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);
@@ -2065,12 +2143,23 @@
addr[1] = &r0_key_data[q];
len[1] = 16;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (q == SHA512_MAC_LEN)
+ res = sha512_vector(2, addr, len, hash);
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (use_sha384 && sha384_vector(2, addr, len, hash) < 0)
- return -1;
+ if (q == SHA384_MAC_LEN)
+ res = sha384_vector(2, addr, len, hash);
#endif /* CONFIG_SHA384 */
- if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
- return -1;
+ if (q == SHA256_MAC_LEN)
+ res = sha256_vector(2, addr, len, hash);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to derive PMKR0Name (PMK-R0 len %zu)",
+ q);
+ return res;
+ }
os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
forced_memzero(r0_key_data, sizeof(r0_key_data));
@@ -2084,11 +2173,14 @@
* IEEE Std 802.11r-2008 - 8.5.1.5.4
*/
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)
+ const u8 *s1kh_id, u8 *pmk_r1_name,
+ size_t pmk_r1_len)
{
- u8 hash[48];
+ u8 hash[64];
const u8 *addr[4];
size_t len[4];
+ int res;
+ const char *title;
/*
* PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name ||
@@ -2103,14 +2195,31 @@
addr[3] = s1kh_id;
len[3] = ETH_ALEN;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (pmk_r1_len == SHA512_MAC_LEN) {
+ title = "FT: PMKR1Name (using SHA512)";
+ res = sha512_vector(4, addr, len, hash);
+ }
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (use_sha384 && sha384_vector(4, addr, len, hash) < 0)
- return -1;
+ if (pmk_r1_len == SHA384_MAC_LEN) {
+ title = "FT: PMKR1Name (using SHA384)";
+ res = sha384_vector(4, addr, len, hash);
+ }
#endif /* CONFIG_SHA384 */
- if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
- return -1;
+ if (pmk_r1_len == SHA256_MAC_LEN) {
+ title = "FT: PMKR1Name (using SHA256)";
+ res = sha256_vector(4, addr, len, hash);
+ }
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to derive PMKR1Name (PMK-R1 len %zu)",
+ pmk_r1_len);
+ return res;
+ }
os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, title, pmk_r1_name, WPA_PMK_NAME_LEN);
return 0;
}
@@ -2127,10 +2236,11 @@
{
u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
u8 *pos;
+ int res;
- /* 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");
+ /* PMK-R1 = KDF-Hash(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-SHA%zu",
+ pmk_r0_len * 8);
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));
@@ -2140,26 +2250,28 @@
os_memcpy(pos, s1kh_id, ETH_ALEN);
pos += ETH_ALEN;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (pmk_r0_len == SHA512_MAC_LEN)
+ res = sha512_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len);
+#endif /* CONFIG_SHA512 */
#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;
+ if (pmk_r0_len == SHA384_MAC_LEN)
+ res = sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len);
#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;
+ if (pmk_r0_len == SHA256_MAC_LEN)
+ res = sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to derive PMK-R1");
+ return res;
}
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len);
return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id,
- pmk_r1_name,
- pmk_r0_len == SHA384_MAC_LEN);
+ pmk_r1_name, pmk_r0_len);
}
@@ -2182,7 +2294,8 @@
u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
WPA_KDK_MAX_LEN];
size_t ptk_len, offset;
- int use_sha384 = wpa_key_mgmt_sha384(akmp);
+ size_t key_len;
+ int res;
if (kdk_len > WPA_KDK_MAX_LEN) {
wpa_printf(MSG_ERROR,
@@ -2191,12 +2304,20 @@
return -1;
}
+ if (akmp == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (pmk_r1_len == SHA256_MAC_LEN || pmk_r1_len == SHA384_MAC_LEN ||
+ pmk_r1_len == SHA512_MAC_LEN))
+ key_len = pmk_r1_len;
+ else if (wpa_key_mgmt_sha384(akmp))
+ key_len = SHA384_MAC_LEN;
+ else
+ key_len = SHA256_MAC_LEN;
+
/*
* 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_printf(MSG_DEBUG, "FT: Derive PTK using KDF-SHA%zu", key_len * 8);
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);
@@ -2212,39 +2333,52 @@
os_memcpy(pos, sta_addr, ETH_ALEN);
pos += ETH_ALEN;
- ptk->kck_len = wpa_kck_len(akmp, PMK_LEN);
+ ptk->kck_len = wpa_kck_len(akmp, key_len);
ptk->kck2_len = wpa_kck2_len(akmp);
- ptk->kek_len = wpa_kek_len(akmp, PMK_LEN);
+ ptk->kek_len = wpa_kek_len(akmp, key_len);
ptk->kek2_len = wpa_kek2_len(akmp);
ptk->tk_len = wpa_cipher_key_len(cipher);
ptk->kdk_len = kdk_len;
ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len +
ptk->kck2_len + ptk->kek2_len + ptk->kdk_len;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (key_len == SHA512_MAC_LEN) {
+ if (pmk_r1_len != SHA512_MAC_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected PMK-R1 length %d (expected %d)",
+ (int) pmk_r1_len, SHA512_MAC_LEN);
+ return -1;
+ }
+ res = sha512_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len);
+ }
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (use_sha384) {
+ if (key_len == SHA384_MAC_LEN) {
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;
+ res = sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len);
}
#endif /* CONFIG_SHA384 */
- if (!use_sha384) {
+ if (key_len == SHA256_MAC_LEN) {
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;
+ res = sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len);
}
+ if (res < 0)
+ return -1;
wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len);
/*
@@ -2313,7 +2447,7 @@
* @akmp: Negotiated key management protocol
*
* 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
+ * AKM: 00-0F-AC:3, 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()
@@ -2736,9 +2870,9 @@
return 16;
case WPA_CIPHER_TKIP:
return 32;
+ default:
+ return 0;
}
-
- return 0;
}
@@ -2751,9 +2885,9 @@
case WPA_CIPHER_GCMP:
case WPA_CIPHER_TKIP:
return 6;
+ default:
+ return 0;
}
-
- return 0;
}
@@ -2778,8 +2912,9 @@
return WPA_ALG_BIP_GMAC_256;
case WPA_CIPHER_BIP_CMAC_256:
return WPA_ALG_BIP_CMAC_256;
+ default:
+ return WPA_ALG_NONE;
}
- return WPA_ALG_NONE;
}
@@ -3951,4 +4086,27 @@
wpabuf_put_u8(buf, capab);
}
+
+/*
+ * wpa_pasn_add_extra_ies - Add protocol specific IEs in Authentication
+ * frame for PASN.
+ *
+ * @buf: Buffer in which the elements will be added
+ * @extra_ies: Protocol specific elements to add
+ * @len: Length of the elements
+ * Returns: 0 on success, -1 on failure
+ */
+
+int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len)
+{
+ if (!len || !extra_ies || !buf)
+ return 0;
+
+ if (wpabuf_tailroom(buf) < sizeof(len))
+ return -1;
+
+ wpabuf_put_data(buf, extra_ies, len);
+ return 0;
+}
+
#endif /* CONFIG_PASN */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 71b423c..05b1a8a 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -186,6 +186,18 @@
#define FT_R1KH_ID_LEN 6
#define WPA_PMK_NAME_LEN 16
+/* FTE - MIC Control - RSNXE Used */
+#define FTE_MIC_CTRL_RSNXE_USED BIT(0)
+#define FTE_MIC_CTRL_MIC_LEN_MASK (BIT(1) | BIT(2) | BIT(3))
+#define FTE_MIC_CTRL_MIC_LEN_SHIFT 1
+
+/* FTE - MIC Length subfield values */
+enum ft_mic_len_subfield {
+ FTE_MIC_LEN_16 = 0,
+ FTE_MIC_LEN_24 = 1,
+ FTE_MIC_LEN_32 = 2,
+};
+
/* IEEE 802.11, 8.5.2 EAPOL-Key frames */
#define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2)))
@@ -401,6 +413,14 @@
/* followed by optional parameters */
} STRUCT_PACKED;
+struct rsn_ftie_sha512 {
+ u8 mic_control[2];
+ u8 mic[32];
+ 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
@@ -455,7 +475,7 @@
size_t *key_auth_len);
#ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+int wpa_ft_mic(int key_mgmt, const u8 *kck, size_t kck_len, const u8 *sta_addr,
const u8 *ap_addr, u8 transaction_seqnum,
const u8 *mdie, size_t mdie_len,
const u8 *ftie, size_t ftie_len,
@@ -467,9 +487,10 @@
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 key_mgmt);
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);
+ const u8 *s1kh_id, u8 *pmk_r1_name,
+ size_t pmk_r1_len);
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,
@@ -544,6 +565,10 @@
size_t r0kh_id_len;
const u8 *fte_anonce;
const u8 *fte_snonce;
+ bool fte_rsnxe_used;
+ unsigned int fte_elem_count;
+ const u8 *fte_mic;
+ size_t fte_mic_len;
const u8 *rsn;
size_t rsn_len;
u16 rsn_capab;
@@ -599,7 +624,7 @@
#define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
- int use_sha384);
+ int key_mgmt);
struct wpa_eapol_ie_parse {
const u8 *wpa_ie;
@@ -697,6 +722,7 @@
int wpa_use_aes_key_wrap(int akmp);
int fils_domain_name_hash(const char *domain, u8 *hash);
+bool pasn_use_sha384(int akmp, int cipher);
int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
const u8 *spa, const u8 *bssid,
const u8 *dhss, size_t dhss_len,
@@ -735,5 +761,6 @@
struct wpa_pasn_params_data *pasn_params);
void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab);
+int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len);
#endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index ba54da5..4ab2a1b 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -466,6 +466,8 @@
#define WPA_BSS_MASK_UPDATE_IDX BIT(22)
#define WPA_BSS_MASK_BEACON_IE BIT(23)
#define WPA_BSS_MASK_FILS_INDICATION BIT(24)
+#define WPA_BSS_MASK_RNR BIT(25)
+#define WPA_BSS_MASK_ML BIT(26)
/* VENDOR_ELEM_* frame id values */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 91df607..ff0869c 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1068,7 +1068,8 @@
/**
* crypto_ec_key_get_subject_public_key - Get SubjectPublicKeyInfo ASN.1 for an EC key
* @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
- * Returns: Buffer with DER encoding of ASN.1 SubjectPublicKeyInfo or %NULL on failure
+ * Returns: Buffer with DER encoding of ASN.1 SubjectPublicKeyInfo using
+ * compressed point format, or %NULL on failure
*/
struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key);
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index f058e06..22f6ab4 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -182,7 +182,6 @@
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-static OSSL_PROVIDER *openssl_default_provider = NULL;
static OSSL_PROVIDER *openssl_legacy_provider = NULL;
#endif /* OpenSSL version >= 3.0 */
@@ -192,9 +191,7 @@
if (openssl_legacy_provider)
return;
- openssl_legacy_provider = OSSL_PROVIDER_load(NULL, "legacy");
- if (openssl_legacy_provider && !openssl_default_provider)
- openssl_default_provider = OSSL_PROVIDER_load(NULL, "default");
+ openssl_legacy_provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1);
#endif /* OpenSSL version >= 3.0 */
}
@@ -206,10 +203,6 @@
OSSL_PROVIDER_unload(openssl_legacy_provider);
openssl_legacy_provider = NULL;
}
- if (openssl_default_provider) {
- OSSL_PROVIDER_unload(openssl_default_provider);
- openssl_default_provider = NULL;
- }
#endif /* OpenSSL version >= 3.0 */
}
@@ -320,12 +313,12 @@
#ifndef CONFIG_FIPS
+
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
openssl_load_legacy_provider();
return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
}
-#endif /* CONFIG_FIPS */
int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
@@ -404,11 +397,11 @@
#endif /* CONFIG_NO_RC4 */
-#ifndef CONFIG_FIPS
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
}
+
#endif /* CONFIG_FIPS */
@@ -454,9 +447,9 @@
return EVP_aes_192_ecb();
case 32:
return EVP_aes_256_ecb();
+ default:
+ return NULL;
}
-
- return NULL;
}
@@ -2156,9 +2149,7 @@
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;
+ return BN_rshift((BIGNUM *) r, (const BIGNUM *) a, n) == 1 ? 0 : -1;
}
@@ -2458,14 +2449,16 @@
EC_POINT_get_affine_coordinates(e->group, (EC_POINT *) point,
x_bn, y_bn, e->bnctx)) {
if (x) {
- crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
- x, len, len);
+ ret = crypto_bignum_to_bin(
+ (struct crypto_bignum *) x_bn, x, len, len);
}
- if (y) {
- crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
- y, len, len);
+ if (ret >= 0 && y) {
+ ret = crypto_bignum_to_bin(
+ (struct crypto_bignum *) y_bn, y, len, len);
}
- ret = 0;
+
+ if (ret > 0)
+ ret = 0;
}
BN_clear_free(x_bn);
@@ -4090,10 +4083,12 @@
case NID_brainpoolP512r1:
return 30;
#endif /* NID_brainpoolP512r1 */
+ default:
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Unsupported curve (nid=%d) in EC key",
+ nid);
+ return -1;
}
- wpa_printf(MSG_ERROR, "OpenSSL: Unsupported curve (nid=%d) in EC key",
- nid);
- return -1;
}
@@ -4854,10 +4849,8 @@
pos += suite_id_len;
os_memcpy(pos, label, label_len);
pos += label_len;
- if (info && info_len) {
+ if (info && info_len)
os_memcpy(pos, info, info_len);
- pos += info_len;
- }
pos = out;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
@@ -5020,8 +5013,8 @@
EVP_PKEY_CTX *pctx = NULL;
struct crypto_ec_key *sk_e;
int res = -1;
- u8 dhss[HPKE_MAX_SHARED_SECRET_LEN + 16];
- size_t dhss_len;
+ u8 *dhss = NULL;
+ size_t dhss_len = 0;
struct wpabuf *enc_buf = NULL, *pk_rm = NULL;
/* skE, pkE = GenerateKeyPair() */
@@ -5038,10 +5031,13 @@
if (!pctx ||
EVP_PKEY_derive_init(pctx) != 1 ||
EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_r) != 1 ||
+ EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 ||
+ !(dhss = os_malloc(dhss_len)) ||
EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
- wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ wpa_printf(MSG_INFO,
+ "OpenSSL: hpke_encap: EVP_PKEY_derive failed (dhss_len=%zu): %s",
+ dhss_len, ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
@@ -5063,7 +5059,7 @@
wpabuf_head(pk_rm),
wpabuf_len(pk_rm), shared_secret);
fail:
- forced_memzero(dhss, sizeof(dhss));
+ bin_clear_free(dhss, dhss_len);
crypto_ec_key_deinit(sk_e);
EVP_PKEY_CTX_free(pctx);
wpabuf_free(enc_buf);
@@ -5184,8 +5180,8 @@
size_t len;
int res = -1;
struct crypto_ec_key *pk_e = NULL;
- u8 dhss[HPKE_MAX_SHARED_SECRET_LEN + 16];
- size_t dhss_len;
+ u8 *dhss = NULL;
+ size_t dhss_len = 0;
/* pkE = DeserializePublicKey(enc) */
if (enc_ct_len < ctx->n_pk)
@@ -5198,15 +5194,17 @@
if (!pk_e)
return -1; /* invalid public key point */
/* dh = DH(skR, pkE) */
- dhss_len = sizeof(dhss);
pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_r, NULL);
if (!pctx ||
EVP_PKEY_derive_init(pctx) != 1 ||
EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_e) != 1 ||
+ EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 ||
+ !(dhss = os_malloc(dhss_len)) ||
EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
- wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ wpa_printf(MSG_INFO,
+ "OpenSSL: hpke_decap: EVP_PKEY_derive failed (dhss_len=%zu): %s",
+ dhss_len, ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
@@ -5221,7 +5219,7 @@
wpabuf_head(pk_rm),
wpabuf_len(pk_rm), shared_secret);
fail:
- forced_memzero(dhss, sizeof(dhss));
+ bin_clear_free(dhss, dhss_len);
crypto_ec_key_deinit(pk_e);
EVP_PKEY_CTX_free(pctx);
wpabuf_free(pk_rm);
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
index 17af964..1ad1068 100644
--- a/src/crypto/sha256.c
+++ b/src/crypto/sha256.c
@@ -30,6 +30,7 @@
unsigned char tk[32];
const u8 *_addr[11];
size_t _len[11], i;
+ int ret;
if (num_elem > 10) {
/*
@@ -70,8 +71,9 @@
_addr[i + 1] = addr[i];
_len[i + 1] = len[i];
}
- if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0)
- return -1;
+ ret = sha256_vector(1 + num_elem, _addr, _len, mac);
+ if (ret < 0)
+ goto fail;
os_memset(k_pad, 0, sizeof(k_pad));
os_memcpy(k_pad, key, key_len);
@@ -84,7 +86,14 @@
_len[0] = 64;
_addr[1] = mac;
_len[1] = SHA256_MAC_LEN;
- return sha256_vector(2, _addr, _len, mac);
+
+ ret = sha256_vector(2, _addr, _len, mac);
+
+fail:
+ forced_memzero(k_pad, sizeof(k_pad));
+ forced_memzero(tk, sizeof(tk));
+
+ return ret;
}
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index e215762..c201dcd 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -353,7 +353,9 @@
* tls_connection_set_verify - Set certificate verification options
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
- * @verify_peer: 1 = verify peer certificate
+ * @verify_peer: 0 = do not verify peer certificate, 1 = verify peer
+ * certificate (require it to be provided), 2 = verify peer certificate if
+ * provided
* @flags: Connection flags (TLS_CONN_*)
* @session_ctx: Session caching context or %NULL to use default
* @session_ctx_len: Length of @session_ctx in bytes.
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index ab82e3d..e0e10fd 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -5416,6 +5416,12 @@
__func__, ERR_error_string(err, NULL));
}
+ if (tls_set_conn_flags(conn, params->flags,
+ params->openssl_ciphers) < 0) {
+ wpa_printf(MSG_ERROR, "TLS: Failed to set connection flags");
+ return -1;
+ }
+
if (engine_id) {
wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine %s",
engine_id);
@@ -5520,12 +5526,6 @@
#endif /* OPENSSL_IS_BORINGSSL */
}
- if (tls_set_conn_flags(conn, params->flags,
- params->openssl_ciphers) < 0) {
- wpa_printf(MSG_ERROR, "TLS: Failed to set connection flags");
- return -1;
- }
-
#ifdef OPENSSL_IS_BORINGSSL
if (params->flags & TLS_CONN_REQUEST_OCSP) {
SSL_enable_ocsp_stapling(conn->ssl);
@@ -5598,8 +5598,9 @@
return "DH";
case EVP_PKEY_EC:
return "EC";
+ default:
+ return "?";
}
- return "?";
}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 71d799d..4ddfe07 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -212,6 +212,7 @@
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
+#define HOSTAPD_MODE_FLAG_HE_INFO_KNOWN BIT(2)
enum ieee80211_op_mode {
@@ -720,6 +721,21 @@
* auth_data_len - Length of auth_data buffer in octets
*/
size_t auth_data_len;
+
+ /**
+ * mld - Establish an MLD connection
+ */
+ bool mld;
+
+ /**
+ * mld_link_id - The link ID of the MLD AP to which we are associating
+ */
+ u8 mld_link_id;
+
+ /**
+ * The MLD AP address
+ */
+ const u8 *ap_mld_addr;
};
/**
@@ -883,6 +899,38 @@
size_t fils_kek_len;
};
+struct wpa_driver_mld_params {
+ /**
+ * mld_addr - AP's MLD address
+ */
+ const u8 *mld_addr;
+
+ /**
+ * valid_links - The valid links including the association link
+ */
+ u16 valid_links;
+
+ /**
+ * assoc_link_id - The link on which the association is performed
+ */
+ u8 assoc_link_id;
+
+ /**
+ * mld_links - Link information
+ *
+ * Should include information on all the requested links for association
+ * including the link on which the association should take place.
+ * For the association link, the ies and ies_len should be NULL and
+ * 0 respectively.
+ */
+ struct {
+ int freq;
+ const u8 *bssid;
+ const u8 *ies;
+ size_t ies_len;
+ } mld_links[MAX_NUM_MLD_LINKS];
+};
+
/**
* struct wpa_driver_associate_params - Association parameters
* Data for struct wpa_driver_ops::associate().
@@ -1250,13 +1298,24 @@
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
- int sae_pwe;
+ enum sae_pwe sae_pwe;
+
#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
/**
* td_policy - Transition Disable Policy
*/
u32 td_policy;
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
+ /**
+ * disable_eht - Disable EHT for this connection
+ */
+ int disable_eht;
+
+ /*
+ * mld_params - MLD association parameters
+ */
+ struct wpa_driver_mld_params mld_params;
};
enum hide_ssid {
@@ -1600,7 +1659,7 @@
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
- int sae_pwe;
+ enum sae_pwe sae_pwe;
/**
* FILS Discovery frame minimum interval in TUs
@@ -1636,6 +1695,42 @@
* Unsolicited broadcast Probe Response template length
*/
size_t unsol_bcast_probe_resp_tmpl_len;
+
+ /**
+ * mbssid_tx_iface - Transmitting interface of the MBSSID set
+ */
+ const char *mbssid_tx_iface;
+
+ /**
+ * mbssid_index - The index of this BSS in the MBSSID set
+ */
+ unsigned int mbssid_index;
+
+ /**
+ * mbssid_elem - Buffer containing all MBSSID elements
+ */
+ u8 *mbssid_elem;
+
+ /**
+ * mbssid_elem_len - Total length of all MBSSID elements
+ */
+ size_t mbssid_elem_len;
+
+ /**
+ * mbssid_elem_count - The number of MBSSID elements
+ */
+ u8 mbssid_elem_count;
+
+ /**
+ * mbssid_elem_offset - Offsets to elements in mbssid_elem.
+ * Kernel will use these offsets to generate multiple BSSID beacons.
+ */
+ u8 **mbssid_elem_offset;
+
+ /**
+ * ema - Enhanced MBSSID advertisements support.
+ */
+ bool ema;
};
struct wpa_driver_mesh_bss_params {
@@ -1789,6 +1884,12 @@
* %KEY_FLAG_RX_TX
* RX/TX key. */
enum key_flag key_flag;
+
+ /**
+ * link_id - MLO Link ID
+ *
+ * Set to a valid Link ID (0-14) when applicable, otherwise -1. */
+ int link_id;
};
enum wpa_driver_if_type {
@@ -2096,6 +2197,8 @@
* frames in STA mode
*/
#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA 0x0000000000002000ULL
+/** Driver supports MLO in station/AP mode */
+#define WPA_DRIVER_FLAGS2_MLO 0x0000000000004000ULL
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2211,6 +2314,11 @@
/* Maximum number of supported AKM suites in commands */
unsigned int max_num_akms;
+
+ /* Maximum number of interfaces supported for MBSSID advertisement */
+ unsigned int mbssid_max_interfaces;
+ /* Maximum profile periodicity for enhanced MBSSID advertisement */
+ unsigned int ema_max_periodicity;
};
@@ -2226,14 +2334,19 @@
#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
#define STA_DRV_DATA_CONN_TIME BIT(9)
+#define STA_DRV_DATA_TX_HE_MCS BIT(10)
+#define STA_DRV_DATA_RX_HE_MCS BIT(11)
+#define STA_DRV_DATA_TX_HE_NSS BIT(12)
+#define STA_DRV_DATA_RX_HE_NSS BIT(13)
struct hostap_sta_driver_data {
unsigned long rx_packets, tx_packets;
unsigned long long rx_bytes, tx_bytes;
unsigned long long rx_airtime, tx_airtime;
+ unsigned long long beacons_count;
int bytes_64bit; /* whether 64-bit byte counters are supported */
- unsigned long current_tx_rate;
- unsigned long current_rx_rate;
+ unsigned long current_tx_rate; /* in kbps */
+ unsigned long current_rx_rate; /* in kbps */
unsigned long inactive_msec;
unsigned long connected_sec;
unsigned long flags; /* bitfield of STA_DRV_DATA_* */
@@ -2243,13 +2356,25 @@
s8 last_ack_rssi;
unsigned long backlog_packets;
unsigned long backlog_bytes;
- s8 signal;
+ unsigned long fcs_error_count;
+ unsigned long beacon_loss_count;
+ unsigned long expected_throughput;
+ unsigned long rx_drop_misc;
+ unsigned long rx_mpdus;
+ int signal; /* dBm; or -WPA_INVALID_NOISE */
+ u8 rx_hemcs;
+ u8 tx_hemcs;
u8 rx_vhtmcs;
u8 tx_vhtmcs;
u8 rx_mcs;
u8 tx_mcs;
+ u8 rx_he_nss;
+ u8 tx_he_nss;
u8 rx_vht_nss;
u8 tx_vht_nss;
+ s8 avg_signal; /* dBm */
+ s8 avg_beacon_signal; /* dBm */
+ s8 avg_ack_signal; /* dBm */
};
struct hostapd_sta_add_params {
@@ -2376,11 +2501,8 @@
* @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
+ * @data: STA information
* @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)
@@ -2388,12 +2510,8 @@
struct wpa_signal_info {
u32 frequency;
int above_threshold;
- int current_signal;
- int avg_signal;
- int avg_beacon_signal;
+ struct hostap_sta_driver_data data;
int current_noise;
- int current_txrate;
- int current_rxrate;
enum chan_width chanwidth;
int center_frq1;
int center_frq2;
@@ -2746,7 +2864,9 @@
};
struct driver_sta_mlo_info {
- u16 valid_links; /* bitmap of valid link IDs */
+ u16 req_links; /* bitmap of requested link IDs */
+ u16 valid_links; /* bitmap of accepted link IDs */
+ u8 assoc_link_id;
u8 ap_mld_addr[ETH_ALEN];
struct {
u8 addr[ETH_ALEN];
@@ -2975,9 +3095,9 @@
* poll - Poll driver for association information
* @priv: private driver interface data
*
- * This is an option callback that can be used when the driver does not
- * provide event mechanism for association events. This is called when
- * receiving WPA EAPOL-Key messages that require association
+ * This is an optional callback that can be used when the driver does
+ * not provide event mechanism for association events. This is called
+ * when receiving WPA/RSN EAPOL-Key messages that require association
* information. The driver interface is supposed to generate associnfo
* event before returning from this callback function. In addition, the
* driver interface should generate an association event after having
@@ -6362,6 +6482,13 @@
*/
struct pasn_auth pasn_auth;
+ /**
+ * struct port_authorized - Data for EVENT_PORT_AUTHORIZED
+ */
+ struct port_authorized {
+ const u8 *td_bitmap;
+ size_t td_bitmap_len;
+ } port_authorized;
};
/**
@@ -6453,6 +6580,8 @@
int ht_supported(const struct hostapd_hw_modes *mode);
int vht_supported(const struct hostapd_hw_modes *mode);
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode);
struct wowlan_triggers *
wpa_get_wowlan_triggers(const char *wowlan_triggers,
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 0ac0aa6..bbd1a7c 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -185,6 +185,21 @@
}
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode)
+{
+ if (!(hw_mode->flags & HOSTAPD_MODE_FLAG_HE_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports HE. Assume
+ * it does to avoid connection issues.
+ */
+ return true;
+ }
+
+ return hw_mode->he_capab[op_mode].he_supported;
+}
+
+
static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
int capa_trigger, u8 *param_trigger)
{
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 0568894..c7e9bfe 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -223,8 +223,9 @@
return CHAN_WIDTH_160;
case NL80211_CHAN_WIDTH_320:
return CHAN_WIDTH_320;
+ default:
+ return CHAN_WIDTH_UNKNOWN;
}
- return CHAN_WIDTH_UNKNOWN;
}
@@ -274,13 +275,17 @@
if (drv->associated)
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
drv->associated = 0;
- drv->sta_mlo_info.valid_links = 0;
+ os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
os_memset(drv->bssid, 0, ETH_ALEN);
drv->first_bss->freq = 0;
#ifdef CONFIG_DRIVER_NL80211_QCA
os_free(drv->pending_roam_data);
drv->pending_roam_data = NULL;
#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ drv->auth_mld = false;
+ drv->auth_mld_link_id = -1;
+ os_memset(drv->auth_ap_mld_addr, 0, ETH_ALEN);
}
@@ -1024,6 +1029,51 @@
}
+static int get_mlo_info(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
+ static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
+ [NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
+ [NL80211_ATTR_MAC] = { .minlen = ETH_ALEN, .maxlen = ETH_ALEN },
+ };
+ struct driver_sta_mlo_info *info = arg;
+ int rem;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_MLO_LINKS])
+ return NL_SKIP;
+
+ info->valid_links = 0;
+ nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
+ u8 link_id;
+
+ if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
+ link_attr, link_policy) != 0)
+ continue;
+
+ if (!link_data[NL80211_ATTR_MLO_LINK_ID] ||
+ !link_data[NL80211_ATTR_MAC])
+ continue;
+
+ link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+ info->valid_links |= BIT(link_id);
+ os_memcpy(info->links[link_id].addr,
+ nla_data(link_data[NL80211_ATTR_MAC]), ETH_ALEN);
+ if (link_data[NL80211_ATTR_WIPHY_FREQ])
+ info->links[link_id].freq =
+ nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
+ }
+
+ return NL_SKIP;
+}
+
+
static int nl80211_get_sta_mlo_info(void *priv,
struct driver_sta_mlo_info *mlo_info)
{
@@ -1033,6 +1083,15 @@
if (!drv->associated)
return -1;
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ if (send_and_recv_msgs(drv, msg, get_mlo_info,
+ &drv->sta_mlo_info, NULL, NULL))
+ return -1;
+ }
+
os_memcpy(mlo_info, &drv->sta_mlo_info, sizeof(*mlo_info));
return 0;
}
@@ -1192,6 +1251,7 @@
MACSTR " to " MACSTR,
ifindex, bss->ifname,
MAC2STR(bss->addr), MAC2STR(addr));
+ os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
os_memcpy(bss->addr, addr, ETH_ALEN);
if (notify)
wpa_supplicant_event(drv->ctx,
@@ -1508,7 +1568,7 @@
}
if (!drv->sta_mlo_info.valid_links ||
- drv->mlo_assoc_link_id == link_id) {
+ drv->sta_mlo_info.assoc_link_id == link_id) {
ctx->assoc_freq = freq;
wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
ctx->assoc_freq);
@@ -1536,7 +1596,7 @@
}
if (!drv->sta_mlo_info.valid_links ||
- drv->mlo_assoc_link_id == link_id) {
+ drv->sta_mlo_info.assoc_link_id == link_id) {
os_memcpy(ctx->assoc_bssid, bssid, ETH_ALEN);
wpa_printf(MSG_DEBUG, "nl80211: Associated with "
MACSTR, MAC2STR(bssid));
@@ -1642,102 +1702,6 @@
return drv->assoc_freq;
}
-
-static int get_link_signal(struct nl_msg *msg, void *arg)
-{
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
- static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
- [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
- [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
- [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 },
- };
- struct nlattr *rinfo[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_MCS] = { .type = NLA_U8 },
- [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
- [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
- };
- struct wpa_signal_info *sig_change = arg;
-
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
- if (!tb[NL80211_ATTR_STA_INFO] ||
- nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
- tb[NL80211_ATTR_STA_INFO], policy))
- return NL_SKIP;
- if (!sinfo[NL80211_STA_INFO_SIGNAL])
- return NL_SKIP;
-
- sig_change->current_signal =
- (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
-
- if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
- sig_change->avg_signal =
- (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
- else
- sig_change->avg_signal = 0;
-
- if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
- sig_change->avg_beacon_signal =
- (s8)
- nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
- else
- sig_change->avg_beacon_signal = 0;
-
- if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
- if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
- sinfo[NL80211_STA_INFO_TX_BITRATE],
- rate_policy)) {
- sig_change->current_txrate = 0;
- } else {
- if (rinfo[NL80211_RATE_INFO_BITRATE]) {
- sig_change->current_txrate =
- nla_get_u16(rinfo[
- NL80211_RATE_INFO_BITRATE]) * 100;
- }
- }
- }
-
- if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
- if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
- sinfo[NL80211_STA_INFO_RX_BITRATE],
- rate_policy)) {
- sig_change->current_rxrate = 0;
- } else {
- if (rinfo[NL80211_RATE_INFO_BITRATE]) {
- sig_change->current_rxrate =
- nla_get_u16(rinfo[
- NL80211_RATE_INFO_BITRATE]) * 100;
- }
- }
- }
-
- return NL_SKIP;
-}
-
-
-int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- const u8 *bssid, struct wpa_signal_info *sig)
-{
- struct nl_msg *msg;
-
- sig->current_signal = -WPA_INVALID_NOISE;
- sig->current_txrate = 0;
- sig->current_rxrate = 0;
-
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
- nlmsg_free(msg);
- return -ENOBUFS;
- }
-
- return send_and_recv_msgs(drv, msg, get_link_signal, sig, NULL, NULL);
-}
-
-
static int get_link_noise(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -3211,9 +3175,9 @@
return RSN_CIPHER_SUITE_WEP40;
case WPA_CIPHER_GTK_NOT_USED:
return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
+ default:
+ return 0;
}
-
- return 0;
}
@@ -3466,6 +3430,7 @@
size_t key_len = params->key_len;
int vlan_id = params->vlan_id;
enum key_flag key_flag = params->key_flag;
+ int link_id = params->link_id;
/* Ignore for P2P Device */
if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
@@ -3473,9 +3438,10 @@
ifindex = if_nametoindex(ifname);
wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
- "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x",
+ "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x link_id=%d",
__func__, ifindex, ifname, alg, addr, key_idx, set_tx,
- (unsigned long) seq_len, (unsigned long) key_len, key_flag);
+ (unsigned long) seq_len, (unsigned long) key_len, key_flag,
+ link_id);
if (check_key_flag(key_flag)) {
wpa_printf(MSG_DEBUG, "%s: invalid key_flag", __func__);
@@ -3609,6 +3575,12 @@
goto fail;
}
+ if (link_id != -1) {
+ wpa_printf(MSG_DEBUG, "nl80211: Link ID %d", link_id);
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
ret = 0;
@@ -3671,6 +3643,13 @@
goto fail;
}
+ if (link_id != -1) {
+ wpa_printf(MSG_DEBUG, "nl80211: set_key default - Link ID %d",
+ link_id);
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG,
@@ -3894,6 +3873,15 @@
}
}
+ if (params->mld && params->ap_mld_addr) {
+ drv->auth_mld = params->mld;
+ drv->auth_mld_link_id = params->mld_link_id;
+ os_memcpy(drv->auth_ap_mld_addr, params->ap_mld_addr, ETH_ALEN);
+ } else {
+ drv->auth_mld = false;
+ drv->auth_mld_link_id = -1;
+ }
+
os_free(drv->auth_data);
drv->auth_data = NULL;
drv->auth_data_len = 0;
@@ -3998,6 +3986,7 @@
os_memset(&p, 0, sizeof(p));
p.ifname = bss->ifname;
p.alg = WPA_ALG_WEP;
+ p.link_id = -1;
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
continue;
@@ -4056,6 +4045,17 @@
goto fail;
}
+ if (params->mld && params->ap_mld_addr) {
+ wpa_printf(MSG_DEBUG, " * MLD: link_id=%u, MLD addr=" MACSTR,
+ params->mld_link_id, MAC2STR(params->ap_mld_addr));
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ params->mld_link_id) ||
+ nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+ params->ap_mld_addr))
+ goto fail;
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
@@ -4159,6 +4159,10 @@
params.ie_len = drv->auth_ie_len;
params.auth_data = drv->auth_data;
params.auth_data_len = drv->auth_data_len;
+ params.mld = drv->auth_mld;
+ params.mld_link_id = drv->auth_mld_link_id;
+ if (drv->auth_mld)
+ params.ap_mld_addr = drv->auth_ap_mld_addr;
for (i = 0; i < 4; i++) {
if (drv->auth_wep_key_len[i]) {
@@ -4646,18 +4650,18 @@
#ifdef CONFIG_SAE
-static int nl80211_put_sae_pwe(struct nl_msg *msg, int pwe)
+static int nl80211_put_sae_pwe(struct nl_msg *msg, enum sae_pwe pwe)
{
u8 sae_pwe;
wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe);
- if (pwe == 0)
+ if (pwe == SAE_PWE_HUNT_AND_PECK)
sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK;
- else if (pwe == 1)
+ else if (pwe == SAE_PWE_HASH_TO_ELEMENT)
sae_pwe = NL80211_SAE_PWE_HASH_TO_ELEMENT;
- else if (pwe == 2)
+ else if (pwe == SAE_PWE_BOTH)
sae_pwe = NL80211_SAE_PWE_BOTH;
- else if (pwe == 3)
+ else if (pwe == SAE_PWE_FORCE_HUNT_AND_PECK)
return 0; /* special test mode */
else
return -1;
@@ -4700,6 +4704,7 @@
#ifdef CONFIG_IEEE80211AX
+
static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
struct nl_msg *msg,
struct wpa_driver_ap_params *params)
@@ -4729,6 +4734,60 @@
nla_nest_end(msg, attr);
return 0;
}
+
+
+static int nl80211_mbssid(struct nl_msg *msg,
+ struct wpa_driver_ap_params *params)
+{
+ struct nlattr *config, *elems;
+ int ifidx;
+
+ if (!params->mbssid_tx_iface)
+ return 0;
+
+ config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
+ if (!config ||
+ nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_INDEX,
+ params->mbssid_index))
+ return -1;
+
+ if (params->mbssid_tx_iface) {
+ ifidx = if_nametoindex(params->mbssid_tx_iface);
+ if (ifidx <= 0 ||
+ nla_put_u32(msg, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
+ ifidx))
+ return -1;
+ }
+
+ if (params->ema && nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA))
+ return -1;
+
+ nla_nest_end(msg, config);
+
+ if (params->mbssid_elem_count && params->mbssid_elem_len &&
+ params->mbssid_elem_offset && *params->mbssid_elem_offset) {
+ u8 i, **offs = params->mbssid_elem_offset;
+
+ elems = nla_nest_start(msg, NL80211_ATTR_MBSSID_ELEMS);
+ if (!elems)
+ return -1;
+
+ for (i = 0; i < params->mbssid_elem_count - 1; i++) {
+ if (nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i]))
+ return -1;
+ }
+
+ if (nla_put(msg, i + 1,
+ *offs + params->mbssid_elem_len - offs[i],
+ offs[i]))
+ return -1;
+
+ nla_nest_end(msg, elems);
+ }
+
+ return 0;
+}
+
#endif /* CONFIG_IEEE80211AX */
@@ -5027,6 +5086,9 @@
if (params->unsol_bcast_probe_resp_interval &&
nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
goto fail;
+
+ if (nl80211_mbssid(msg, params) < 0)
+ goto fail;
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_SAE
@@ -6187,6 +6249,12 @@
}
#endif /* CONFIG_HE_OVERRIDES */
+ if (params->disable_eht) {
+ wpa_printf(MSG_DEBUG, " * EHT disabled");
+ if (nla_put_flag(msg, NL80211_ATTR_DISABLE_EHT))
+ return -1;
+ }
+
return 0;
}
@@ -6341,10 +6409,85 @@
struct wpa_driver_associate_params *params,
struct nl_msg *msg)
{
+ if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) {
+ struct wpa_driver_mld_params *mld_params = ¶ms->mld_params;
+ struct nlattr *links, *attr;
+ int i;
+ u8 link_id;
+
+ wpa_printf(MSG_DEBUG, " * MLD: MLD addr=" MACSTR,
+ MAC2STR(mld_params->mld_addr));
+
+ if (nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+ mld_params->mld_addr) ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ mld_params->assoc_link_id))
+ return -1;
+
+ links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+ if (!links)
+ return -1;
+
+ attr = nla_nest_start(msg, 0);
+ if (!attr)
+ return -1;
+
+ /* First add the association link ID */
+ link_id = mld_params->assoc_link_id;
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ mld_params->mld_links[link_id].bssid) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ mld_params->mld_links[link_id].freq))
+ return -1;
+
+ os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+ mld_params->mld_links[link_id].bssid, ETH_ALEN);
+
+ nla_nest_end(msg, attr);
+
+ for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS;
+ link_id++) {
+ if (!(mld_params->valid_links & BIT(link_id)) ||
+ link_id == mld_params->assoc_link_id)
+ continue;
+
+ attr = nla_nest_start(msg, i);
+ if (!attr)
+ return -1;
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ mld_params->mld_links[link_id].bssid) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ mld_params->mld_links[link_id].freq) ||
+ (mld_params->mld_links[link_id].ies &&
+ mld_params->mld_links[i].ies_len &&
+ nla_put(msg, NL80211_ATTR_IE,
+ mld_params->mld_links[link_id].ies_len,
+ mld_params->mld_links[link_id].ies)))
+ return -1;
+
+ os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+ mld_params->mld_links[link_id].bssid,
+ ETH_ALEN);
+ nla_nest_end(msg, attr);
+ i++;
+ }
+
+ nla_nest_end(msg, links);
+
+ os_memcpy(drv->sta_mlo_info.ap_mld_addr,
+ params->mld_params.mld_addr, ETH_ALEN);
+ drv->sta_mlo_info.assoc_link_id = mld_params->assoc_link_id;
+ drv->sta_mlo_info.req_links = mld_params->valid_links;
+ }
+
if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
return -1;
- if (params->bssid) {
+ if (params->bssid && !params->mld_params.mld_addr) {
wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
MAC2STR(params->bssid));
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
@@ -6359,7 +6502,7 @@
return -1;
}
- if (params->freq.freq) {
+ if (params->freq.freq && !params->mld_params.mld_addr) {
wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
params->freq.freq))
@@ -7353,16 +7496,26 @@
[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_BEACON_LOSS] = { .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 },
+ [NL80211_STA_INFO_EXPECTED_THROUGHPUT] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8},
[NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_ACK_SIGNAL_AVG] = { .type = NLA_S8 },
+ [NL80211_STA_INFO_RX_MPDUS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_FCS_ERROR_COUNT] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 },
- [NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
};
struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -7372,6 +7525,8 @@
[NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
[NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_HE_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_HE_NSS] = { .type = NLA_U8 },
};
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -7414,34 +7569,62 @@
nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
data->bytes_64bit = 1;
}
+ if (stats[NL80211_STA_INFO_SIGNAL])
+ data->signal = (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
if (stats[NL80211_STA_INFO_RX_PACKETS])
data->rx_packets =
nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
if (stats[NL80211_STA_INFO_TX_PACKETS])
data->tx_packets =
nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
- if (stats[NL80211_STA_INFO_RX_DURATION])
- data->rx_airtime =
- nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
- if (stats[NL80211_STA_INFO_TX_DURATION])
- data->tx_airtime =
- nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
+ if (stats[NL80211_STA_INFO_TX_RETRIES])
+ data->tx_retry_count =
+ nla_get_u32(stats[NL80211_STA_INFO_TX_RETRIES]);
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_SIGNAL_AVG])
+ data->avg_signal =
+ (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL_AVG]);
if (stats[NL80211_STA_INFO_CONNECTED_TIME]) {
data->connected_sec =
nla_get_u32(stats[NL80211_STA_INFO_CONNECTED_TIME]);
data->flags |= STA_DRV_DATA_CONN_TIME;
}
+ if (stats[NL80211_STA_INFO_BEACON_LOSS])
+ data->beacon_loss_count =
+ nla_get_u32(stats[NL80211_STA_INFO_BEACON_LOSS]);
+ if (stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT])
+ data->expected_throughput =
+ nla_get_u32(stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
+ if (stats[NL80211_STA_INFO_RX_DROP_MISC])
+ data->rx_drop_misc =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_DROP_MISC]);
+ if (stats[NL80211_STA_INFO_BEACON_RX])
+ data->beacons_count =
+ nla_get_u64(stats[NL80211_STA_INFO_BEACON_RX]);
+ if (stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+ data->avg_beacon_signal =
+ (s8) nla_get_u8(stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+ if (stats[NL80211_STA_INFO_RX_DURATION])
+ data->rx_airtime =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
+ 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_ACK_SIGNAL_AVG])
+ data->avg_ack_signal =
+ nla_get_s8(stats[NL80211_STA_INFO_ACK_SIGNAL_AVG]);
+ if (stats[NL80211_STA_INFO_RX_MPDUS])
+ data->rx_mpdus = nla_get_u32(stats[NL80211_STA_INFO_RX_MPDUS]);
+ if (stats[NL80211_STA_INFO_FCS_ERROR_COUNT])
+ data->fcs_error_count =
+ nla_get_u32(stats[NL80211_STA_INFO_FCS_ERROR_COUNT]);
+ if (stats[NL80211_STA_INFO_TX_DURATION])
+ data->tx_airtime =
+ nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
if (stats[NL80211_STA_INFO_TX_BITRATE] &&
nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
@@ -7454,6 +7637,10 @@
data->current_tx_rate =
nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+ /* Convert from 100 kbps to kbps; it's a more convenient unit.
+ * It's also safe up until ~1Tbps. */
+ data->current_tx_rate = data->current_tx_rate * 100;
+
if (rate[NL80211_RATE_INFO_MCS]) {
data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
data->flags |= STA_DRV_DATA_TX_MCS;
@@ -7470,6 +7657,16 @@
nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
data->flags |= STA_DRV_DATA_TX_VHT_NSS;
}
+ if (rate[NL80211_RATE_INFO_HE_MCS]) {
+ data->tx_hemcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+ data->flags |= STA_DRV_DATA_TX_HE_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_NSS]) {
+ data->tx_he_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+ data->flags |= STA_DRV_DATA_TX_HE_NSS;
+ }
}
if (stats[NL80211_STA_INFO_RX_BITRATE] &&
@@ -7483,9 +7680,12 @@
data->current_rx_rate =
nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+ /* Convert from 100 kbps to kbps; it's a more convenient unit.
+ * It's also safe up until ~1Tbps. */
+ data->current_rx_rate = data->current_rx_rate * 100;
+
if (rate[NL80211_RATE_INFO_MCS]) {
- data->rx_mcs =
- nla_get_u8(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]) {
@@ -7500,6 +7700,16 @@
nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
data->flags |= STA_DRV_DATA_RX_VHT_NSS;
}
+ if (rate[NL80211_RATE_INFO_HE_MCS]) {
+ data->rx_hemcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+ data->flags |= STA_DRV_DATA_RX_HE_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_NSS]) {
+ data->rx_he_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+ data->flags |= STA_DRV_DATA_RX_HE_NSS;
+ }
}
if (stats[NL80211_STA_INFO_TID_STATS])
@@ -7508,6 +7718,26 @@
return NL_SKIP;
}
+
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ const u8 *bssid,
+ struct hostap_sta_driver_data *data)
+{
+ struct nl_msg *msg;
+
+ data->signal = -WPA_INVALID_NOISE;
+ data->current_tx_rate = 0;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(drv, msg, get_sta_handler, data, NULL, NULL);
+}
+
+
static int i802_read_sta_data(struct i802_bss *bss,
struct hostap_sta_driver_data *data,
const u8 *addr)
@@ -8248,7 +8478,7 @@
if (!addr &&
(type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
- type == WPA_IF_STATION)) {
+ type == WPA_IF_STATION || type == WPA_IF_AP_BSS)) {
/* Enforce unique address */
u8 new_addr[ETH_ALEN];
@@ -8924,12 +9154,12 @@
int res;
os_memset(si, 0, sizeof(*si));
- res = nl80211_get_link_signal(drv, drv->bssid, si);
+ res = nl80211_get_link_signal(drv, drv->bssid, &si->data);
if (res) {
if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
drv->nlmode != NL80211_IFTYPE_MESH_POINT)
return res;
- si->current_signal = 0;
+ si->data.signal = 0;
}
res = nl80211_get_channel_width(drv, si);
@@ -9078,7 +9308,7 @@
res = nl80211_get_link_signal(drv,
drv->sta_mlo_info.links[i].bssid,
- &mlo_si->links[i]);
+ &mlo_si->links[i].data);
if (res != 0)
return res;
@@ -9246,10 +9476,14 @@
static int nl80211_pmkid(struct i802_bss *bss, int cmd,
- struct wpa_pmkid_params *params)
+ struct wpa_pmkid_params *params, bool skip_pmk)
{
struct nl_msg *msg;
- const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */
+
+ if (cmd == NL80211_CMD_SET_PMKSA)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_SET_PMKSA with skip_pmk=%s pmk_len=%zu",
+ skip_pmk ? "true" : "false", params->pmk_len);
if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
(params->pmkid &&
@@ -9268,7 +9502,7 @@
nla_put_u8(msg, NL80211_ATTR_PMK_REAUTH_THRESHOLD,
params->pmk_reauth_threshold)) ||
(cmd != NL80211_CMD_DEL_PMKSA &&
- params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
+ params->pmk_len && !skip_pmk &&
nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
@@ -9282,6 +9516,9 @@
static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
{
struct i802_bss *bss = priv;
+ const size_t PMK_MAX_LEN = 64; /* current cfg80211 limit */
+ const size_t LEGACY_PMK_MAX_LEN = 48; /* old cfg80211 limit */
+ bool skip_pmk = params->pmk_len > PMK_MAX_LEN;
int ret;
if (params->bssid)
@@ -9294,7 +9531,15 @@
wpa_ssid_txt(params->ssid, params->ssid_len));
}
- ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
+ ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, skip_pmk);
+ /*
+ * Try again by skipping PMK if the first attempt failed with ERANGE
+ * error, PMK was not skipped, and PMK length is greater than the
+ * legacy kernel maximum allowed limit.
+ */
+ if (ret == -ERANGE && !skip_pmk &&
+ params->pmk_len > LEGACY_PMK_MAX_LEN)
+ ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, true);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
@@ -9320,7 +9565,7 @@
wpa_ssid_txt(params->ssid, params->ssid_len));
}
- ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
+ ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params, true);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
@@ -10236,7 +10481,9 @@
"capa.max_conc_chan_5_0=%u\n"
"capa.max_sched_scan_plans=%u\n"
"capa.max_sched_scan_plan_interval=%u\n"
- "capa.max_sched_scan_plan_iterations=%u\n",
+ "capa.max_sched_scan_plan_iterations=%u\n"
+ "capa.mbssid_max_interfaces=%u\n"
+ "capa.ema_max_periodicity=%u\n",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
@@ -10258,7 +10505,9 @@
drv->capa.max_conc_chan_5_0,
drv->capa.max_sched_scan_plans,
drv->capa.max_sched_scan_plan_interval,
- drv->capa.max_sched_scan_plan_iterations);
+ drv->capa.max_sched_scan_plan_iterations,
+ drv->capa.mbssid_max_interfaces,
+ drv->capa.ema_max_periodicity);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -11054,6 +11303,7 @@
wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
bss->ifname, MAC2STR(addr));
drv->addr_changed = new_addr;
+ os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
os_memcpy(bss->addr, addr, ETH_ALEN);
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
@@ -12443,9 +12693,10 @@
ETH_ALEN, params->peer[i].peer_addr))
goto fail;
- if (params->peer[i].status == 0)
- nla_put_flag(msg,
- QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS);
+ if (params->peer[i].status == 0 &&
+ nla_put_flag(msg,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS))
+ goto fail;
wpa_printf(MSG_DEBUG,
"nl80211: Own address[%u]: " MACSTR
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index be5783b..694fb1b 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -68,6 +68,7 @@
unsigned int use_nl_connect:1;
u8 addr[ETH_ALEN];
+ u8 prev_addr[ETH_ALEN];
int freq;
int bandwidth;
@@ -128,7 +129,6 @@
u8 bssid[ETH_ALEN];
u8 prev_bssid[ETH_ALEN];
int associated;
- int mlo_assoc_link_id;
struct driver_sta_mlo_info sta_mlo_info;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
@@ -225,6 +225,9 @@
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
+ bool auth_mld;
+ u8 auth_mld_link_id;
+ u8 auth_ap_mld_addr[ETH_ALEN];
/*
* Tells whether the last scan issued from wpa_supplicant was a normal
@@ -272,7 +275,8 @@
int is_sta_interface(enum nl80211_iftype nlmode);
int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- const u8 *bssid, struct wpa_signal_info *sig);
+ const u8 *bssid,
+ struct hostap_sta_driver_data *data);
int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
struct wpa_signal_info *sig_change);
int nl80211_get_wiphy_index(struct i802_bss *bss);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index a211d2a..2f3b85a 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -874,6 +874,30 @@
}
+static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
+{
+ struct nlattr *config[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
+
+ if (nla_parse_nested(config, NL80211_MBSSID_CONFIG_ATTR_MAX, attr,
+ NULL))
+ return;
+
+ if (!config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES])
+ return;
+
+ cap->mbssid_max_interfaces =
+ nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES]);
+
+ if (config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY])
+ cap->ema_max_periodicity =
+ nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY]);
+
+ wpa_printf(MSG_DEBUG,
+ "mbssid: max interfaces %u, max profile periodicity %u",
+ cap->mbssid_max_interfaces, cap->ema_max_periodicity);
+}
+
+
static int wiphy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1118,6 +1142,12 @@
capa->max_num_akms =
nla_get_u16(tb[NL80211_ATTR_MAX_NUM_AKM_SUITES]);
+ if (tb[NL80211_ATTR_MBSSID_CONFIG])
+ wiphy_info_mbssid(capa, tb[NL80211_ATTR_MBSSID_CONFIG]);
+
+ if (tb[NL80211_ATTR_MLO_SUPPORT])
+ capa->flags2 |= WPA_DRIVER_FLAGS2_MLO;
+
return NL_SKIP;
}
@@ -2003,7 +2033,8 @@
os_memset(mode, 0, sizeof(*mode));
mode->mode = NUM_HOSTAPD_MODES;
mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
- HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
+ HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN |
+ HOSTAPD_MODE_FLAG_HE_INFO_KNOWN;
/*
* Unsupported VHT MCS stream is defined as value 3, so the VHT
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 50fe438..46e4efb 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -481,7 +481,16 @@
if (link_id >= MAX_NUM_MLD_LINKS)
continue;
- mlo->valid_links |= BIT(link_id);
+ if (tb[NL80211_ATTR_STATUS_CODE]) {
+ /* Set requested links only when status indicated */
+ mlo->req_links |= BIT(link_id);
+ if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) ==
+ WLAN_STATUS_SUCCESS)
+ mlo->valid_links |= BIT(link_id);
+ } else {
+ mlo->valid_links |= BIT(link_id);
+ }
+
os_memcpy(mlo->links[link_id].addr,
nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
os_memcpy(mlo->links[link_id].bssid,
@@ -494,9 +503,149 @@
}
+struct links_info {
+ /* bitmap of link IDs in Per-STA profile subelements */
+ u16 non_assoc_links;
+ u8 addr[MAX_NUM_MLD_LINKS][ETH_ALEN];
+};
+
+
+static void nl80211_get_basic_mle_links_info(const u8 *mle, size_t mle_len,
+ struct links_info *info)
+{
+ size_t rem_len;
+ const u8 *pos;
+
+ if (mle_len < MULTI_LINK_CONTROL_LEN + 1 ||
+ mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN])
+ return;
+
+ /* Skip Common Info */
+ pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN];
+ rem_len = mle_len -
+ (MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]);
+
+ /* Parse Subelements */
+ while (rem_len > 2) {
+ size_t ie_len = 2 + pos[1];
+
+ if (rem_len < ie_len)
+ break;
+
+ if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+ u8 link_id;
+ const u8 *sta_profile;
+ u16 sta_ctrl;
+
+ if (pos[1] < BASIC_MLE_STA_PROF_STA_MAC_IDX + ETH_ALEN)
+ goto next_subelem;
+
+ sta_profile = &pos[2];
+ sta_ctrl = WPA_GET_LE16(sta_profile);
+ link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ goto next_subelem;
+
+ if (!(sta_ctrl & BASIC_MLE_STA_CTRL_PRES_STA_MAC))
+ goto next_subelem;
+
+ info->non_assoc_links |= BIT(link_id);
+ os_memcpy(info->addr[link_id],
+ &sta_profile[BASIC_MLE_STA_PROF_STA_MAC_IDX],
+ ETH_ALEN);
+ }
+next_subelem:
+ pos += ie_len;
+ rem_len -= ie_len;
+ }
+}
+
+
+static int nl80211_update_rejected_links_info(struct driver_sta_mlo_info *mlo,
+ struct nlattr *req_ie,
+ struct nlattr *resp_ie)
+{
+ int i;
+ struct wpabuf *mle;
+ struct ieee802_11_elems req_elems, resp_elems;
+ struct links_info req_info, resp_info;
+
+ if (!req_ie || !resp_ie) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: (Re)Association Request/Response frame elements not available");
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(nla_data(req_ie), nla_len(req_ie),
+ &req_elems, 0) == ParseFailed ||
+ ieee802_11_parse_elems(nla_data(resp_ie), nla_len(resp_ie),
+ &resp_elems, 0) == ParseFailed) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: Failed to parse (Re)Association Request/Response elements");
+ return -1;
+ }
+
+ mle = ieee802_11_defrag_mle(&req_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mle) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: Basic Multi-Link element not found in Association Request");
+ return -1;
+ }
+ os_memset(&req_info, 0, sizeof(req_info));
+ nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+ &req_info);
+ wpabuf_free(mle);
+
+ mle = ieee802_11_defrag_mle(&resp_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mle) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Basic Multi-Link element not found in Association Response");
+ return -1;
+ }
+ os_memset(&resp_info, 0, sizeof(resp_info));
+ nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+ &resp_info);
+ wpabuf_free(mle);
+
+ if (req_info.non_assoc_links != resp_info.non_assoc_links) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Association Request and Response links bitmaps not equal (0x%x != 0x%x)",
+ req_info.non_assoc_links,
+ resp_info.non_assoc_links);
+ return -1;
+ }
+
+ mlo->req_links = BIT(mlo->assoc_link_id) | req_info.non_assoc_links;
+ if ((mlo->req_links & mlo->valid_links) != mlo->valid_links) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Accepted links are not a subset of requested links (req_links=0x%x valid_links=0x%x non_assoc_links=0x%x assoc_link_id=0x%x)",
+ mlo->req_links, mlo->valid_links,
+ req_info.non_assoc_links, BIT(mlo->assoc_link_id));
+ return -1;
+ }
+
+ /* Get MLO links info for rejected links */
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!((mlo->req_links & ~mlo->valid_links) & BIT(i)))
+ continue;
+
+ os_memcpy(mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN);
+ os_memcpy(mlo->links[i].addr, req_info.addr[i], ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
static int nl80211_get_assoc_link_id(const u8 *data, u8 len)
{
- if (!(data[0] & BASIC_MULTI_LINK_CTRL0_PRES_LINK_ID))
+ u16 control;
+
+ if (len < 2)
+ return -1;
+
+ control = WPA_GET_LE16(data);
+ if (!(control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID))
return -1;
#define BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX \
@@ -514,10 +663,12 @@
bool qca_roam_auth,
struct nlattr *addr,
struct nlattr *mlo_links,
+ struct nlattr *req_ie,
struct nlattr *resp_ie)
{
const u8 *ml_ie;
struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+ int res;
if (!addr || !mlo_links || !resp_ie)
return;
@@ -527,11 +678,14 @@
if (!ml_ie)
return;
- drv->mlo_assoc_link_id = nl80211_get_assoc_link_id(&ml_ie[3],
- ml_ie[1] - 1);
- if (drv->mlo_assoc_link_id < 0 ||
- drv->mlo_assoc_link_id >= MAX_NUM_MLD_LINKS)
+ res = nl80211_get_assoc_link_id(&ml_ie[3], ml_ie[1] - 1);
+ if (res < 0 || res >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not find a valid association Link ID (res=%d)",
+ res);
return;
+ }
+ drv->sta_mlo_info.assoc_link_id = res;
os_memcpy(mlo->ap_mld_addr, nla_data(addr), ETH_ALEN);
wpa_printf(MSG_DEBUG, "nl80211: AP MLD MAC Address " MACSTR,
@@ -544,14 +698,15 @@
nl80211_parse_qca_vendor_mlo_link_info(mlo, mlo_links);
#endif /* CONFIG_DRIVER_NL80211_QCA */
- if (!(mlo->valid_links & BIT(drv->mlo_assoc_link_id))) {
- wpa_printf(MSG_ERROR, "nl80211: Invalid MLO assoc link ID %d",
- drv->mlo_assoc_link_id);
+ if (!(mlo->valid_links & BIT(mlo->assoc_link_id)) ||
+ (!mlo->req_links &&
+ nl80211_update_rejected_links_info(mlo, req_ie, resp_ie))) {
+ wpa_printf(MSG_INFO, "nl80211: Invalid MLO connection info");
mlo->valid_links = 0;
return;
}
- os_memcpy(drv->bssid, mlo->links[drv->mlo_assoc_link_id].bssid,
+ os_memcpy(drv->bssid, mlo->links[drv->sta_mlo_info.assoc_link_id].bssid,
ETH_ALEN);
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
}
@@ -664,8 +819,9 @@
}
drv->associated = 1;
- drv->sta_mlo_info.valid_links = 0;
- nl80211_parse_mlo_info(drv, qca_roam_auth, addr, mlo_links, resp_ie);
+ os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
+ nl80211_parse_mlo_info(drv, qca_roam_auth, addr, mlo_links, req_ie,
+ resp_ie);
if (!drv->sta_mlo_info.valid_links && addr) {
os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
@@ -916,7 +1072,7 @@
EVENT_LINK_CH_SWITCH_STARTED, &data);
}
- if (link_id != drv->mlo_assoc_link_id)
+ if (link_id != drv->sta_mlo_info.assoc_link_id)
return;
}
@@ -1089,8 +1245,11 @@
* ignore_next_local_deauth as well, to avoid next local
* deauth event be wrongly ignored.
*/
- if (!os_memcmp(mgmt->sa, drv->first_bss->addr,
- ETH_ALEN)) {
+ if (os_memcmp(mgmt->sa, drv->first_bss->addr,
+ ETH_ALEN) == 0 ||
+ (!is_zero_ether_addr(drv->first_bss->prev_addr) &&
+ os_memcmp(mgmt->sa, drv->first_bss->prev_addr,
+ ETH_ALEN) == 0)) {
wpa_printf(MSG_DEBUG,
"nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
drv->ignore_next_local_deauth = 0;
@@ -1300,7 +1459,10 @@
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) {
+ os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0 &&
+ (is_zero_ether_addr(drv->first_bss->prev_addr) ||
+ os_memcmp(bss->prev_addr, data + 4 + ETH_ALEN,
+ ETH_ALEN) != 0)) {
wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
"for foreign address", bss->ifname);
return;
@@ -1522,7 +1684,7 @@
struct nlattr *nl;
int rem;
struct scan_info *info;
-#define MAX_REPORT_FREQS 100
+#define MAX_REPORT_FREQS 110
int freqs[MAX_REPORT_FREQS];
int num_freqs = 0;
@@ -1554,7 +1716,7 @@
}
}
if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
- char msg[500], *pos, *end;
+ char msg[MAX_REPORT_FREQS * 5], *pos, *end;
int res;
pos = msg;
@@ -1677,11 +1839,11 @@
* nl80211_get_link_signal() and nl80211_get_link_noise() set default
* values in case querying the driver fails.
*/
- res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change);
+ res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change.data);
if (res == 0) {
- wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d",
- ed.signal_change.current_signal,
- ed.signal_change.current_txrate);
+ wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %lu",
+ ed.signal_change.data.signal,
+ ed.signal_change.data.current_tx_rate);
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Querying the driver for signal info failed");
@@ -2293,6 +2455,14 @@
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS]);
+
+#ifdef ANDROID
+#ifdef ANDROID_LIB_EVENT
+ wpa_driver_nl80211_driver_event(
+ drv, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH,
+ data, len);
+#endif /* ANDROID_LIB_EVENT */
+#endif /* ANDROID */
}
@@ -2614,11 +2784,11 @@
nl_src = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR];
nl_peer = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR];
if (nl_src)
- os_memcpy(event.pasn_auth.peer[idx].own_addr, nl_src,
- ETH_ALEN);
+ os_memcpy(event.pasn_auth.peer[idx].own_addr,
+ nla_data(nl_src), ETH_ALEN);
if (nl_peer)
- os_memcpy(event.pasn_auth.peer[idx].peer_addr, nl_peer,
- ETH_ALEN);
+ os_memcpy(event.pasn_auth.peer[idx].peer_addr,
+ nla_data(nl_peer), ETH_ALEN);
if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED])
event.pasn_auth.peer[idx].ltf_keyseed_required = true;
idx++;
@@ -2802,7 +2972,13 @@
#ifdef ANDROID
#ifdef ANDROID_LIB_EVENT
- wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data, len);
+ /* Postpone QCA roam+auth event indication to the point when both that
+ * and the NL80211_CMD_ROAM event have been received (see calls to
+ * qca_nl80211_key_mgmt_auth() and drv->pending_roam_data). */
+ if (!(vendor_id == OUI_QCA &&
+ subcmd == QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH))
+ wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data,
+ len);
#endif /* ANDROID_LIB_EVENT */
#endif /* ANDROID */
@@ -2980,6 +3156,9 @@
struct nlattr **tb)
{
const u8 *addr;
+ union wpa_event_data event;
+
+ os_memset(&event, 0, sizeof(event));
if (!tb[NL80211_ATTR_MAC] ||
nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) {
@@ -2997,7 +3176,15 @@
return;
}
- wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, NULL);
+ if (tb[NL80211_ATTR_TD_BITMAP]) {
+ event.port_authorized.td_bitmap_len =
+ nla_len(tb[NL80211_ATTR_TD_BITMAP]);
+ if (event.port_authorized.td_bitmap_len > 0)
+ event.port_authorized.td_bitmap =
+ nla_data(tb[NL80211_ATTR_TD_BITMAP]);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, &event);
}
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index d54dd3a..7a7890c 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -911,8 +911,21 @@
"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);
+
+ if (os_memcmp(drv->sta_mlo_info.ap_mld_addr, drv->bssid,
+ ETH_ALEN) != 0) {
+ clear_state_mismatch(drv, r->bssid);
+
+ if (!is_zero_ether_addr(drv->sta_mlo_info.ap_mld_addr))
+ clear_state_mismatch(
+ drv, drv->sta_mlo_info.ap_mld_addr);
+ else
+ clear_state_mismatch(drv, drv->bssid);
+
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: BSSID is the MLD address");
+ }
}
}
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 0f0ad1f..cf201fe 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -2424,7 +2424,7 @@
struct iwreq iwr;
os_memset(si, 0, sizeof(*si));
- si->current_signal = -WPA_INVALID_NOISE;
+ si->data.signal = -WPA_INVALID_NOISE;
si->current_noise = WPA_INVALID_NOISE;
si->chanwidth = CHAN_WIDTH_UNKNOWN;
@@ -2440,7 +2440,7 @@
return -1;
}
- si->current_signal = stats.qual.level -
+ si->data.signal = stats.qual.level -
((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
si->current_noise = stats.qual.noise -
((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
index 0e960f4..7780479 100644
--- a/src/drivers/netlink.c
+++ b/src/drivers/netlink.c
@@ -147,8 +147,9 @@
return "kernel-control";
case 1:
return "userspace-control";
+ default:
+ return "?";
}
- return "?";
}
@@ -161,8 +162,9 @@
return "IF_OPER_DORMANT";
case IF_OPER_UP:
return "IF_OPER_UP";
+ default:
+ return "?";
}
- return "?";
}
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index ffb7c57..c14a91b 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -377,14 +377,22 @@
* the non-transmitting interfaces are deleted as well.
*
* @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
- * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
+ * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC
+ * represents peer's MLD address for MLO pairwise key. For MLO group key,
+ * the link is identified by %NL80211_ATTR_MLO_LINK_ID.
* @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
* %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
+ * For MLO connection, the link to set default key is identified by
+ * %NL80211_ATTR_MLO_LINK_ID.
* @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
* %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
- * and %NL80211_ATTR_KEY_SEQ attributes.
+ * and %NL80211_ATTR_KEY_SEQ attributes. %NL80211_ATTR_MAC represents
+ * peer's MLD address for MLO pairwise key. The link to add MLO
+ * group key is identified by %NL80211_ATTR_MLO_LINK_ID.
* @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
- * or %NL80211_ATTR_MAC.
+ * or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC represents peer's MLD address
+ * for MLO pairwise key. The link to delete group key is identified by
+ * %NL80211_ATTR_MLO_LINK_ID.
*
* @NL80211_CMD_GET_BEACON: (not used)
* @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
@@ -2741,6 +2749,8 @@
* When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
* timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
* the incoming frame RX timestamp.
+ * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
+ * (re)associations.
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3268,6 +3278,7 @@
NL80211_ATTR_TX_HW_TIMESTAMP,
NL80211_ATTR_RX_HW_TIMESTAMP,
+ NL80211_ATTR_TD_BITMAP,
/* add attributes here, update the policy in nl80211.c */
@@ -4951,6 +4962,7 @@
* using the nesting index as the antenna number.
* @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
* @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
+ * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -4977,6 +4989,7 @@
NL80211_BSS_CHAIN_SIGNAL,
NL80211_BSS_FREQUENCY_OFFSET,
NL80211_BSS_MLO_LINK_ID,
+ NL80211_BSS_MLD_ADDR,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -6273,6 +6286,14 @@
* @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
* detection.
*
+ * @NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE: Device can perform a MAC address
+ * change without having to bring the underlying network device down
+ * first. For example, in station mode this can be used to vary the
+ * origin MAC address prior to a connection to a new AP for privacy
+ * or other reasons. Note that certain driver specific restrictions
+ * might apply, e.g. no scans in progress, no offchannel operations
+ * in progress, and no active connections.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
@@ -6340,6 +6361,7 @@
NL80211_EXT_FEATURE_BSS_COLOR,
NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
NL80211_EXT_FEATURE_RADAR_BACKGROUND,
+ NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 0e3a7b2..ff8ad8d 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -356,9 +356,19 @@
return -1;
}
eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
- crypto_bignum_to_bin(peer_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(peer_scalar, cruft, order_len,
+ order_len) < 0) {
+ os_free(cruft);
+ return -1;
+ }
+
eap_pwd_h_update(hash, cruft, order_len);
- crypto_bignum_to_bin(server_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(server_scalar, cruft, order_len,
+ order_len) < 0) {
+ os_free(cruft);
+ return -1;
+ }
+
eap_pwd_h_update(hash, cruft, order_len);
eap_pwd_h_final(hash, &session_id[1]);
@@ -368,7 +378,12 @@
os_free(cruft);
return -1;
}
- crypto_bignum_to_bin(k, cruft, prime_len, prime_len);
+
+ if (crypto_bignum_to_bin(k, cruft, prime_len, prime_len) < 0) {
+ os_free(cruft);
+ return -1;
+ }
+
eap_pwd_h_update(hash, cruft, prime_len);
os_free(cruft);
eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 15664df..ce44a4f 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -267,6 +267,7 @@
sm->reauthInit = false;
sm->erp_seq = (u32) -1;
sm->use_machine_cred = 0;
+ sm->eap_fast_mschapv2 = false;
}
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index b12cfee..d7677ca 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -354,6 +354,7 @@
sm->auth_challenge = data->key_block_p->server_challenge;
sm->peer_challenge = data->key_block_p->client_challenge;
}
+ sm->eap_fast_mschapv2 = true;
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
@@ -693,18 +694,7 @@
if (key_len > isk_len)
key_len = isk_len;
- if (key_len == 32 &&
- data->phase2_method->vendor == EAP_VENDOR_IETF &&
- data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
- /*
- * EAP-FAST uses reverse order for MS-MPPE keys when deriving
- * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
- * ISK for EAP-FAST cryptobinding.
- */
- os_memcpy(isk, key + 16, 16);
- os_memcpy(isk + 16, key, 16);
- } else
- os_memcpy(isk, key, key_len);
+ os_memcpy(isk, key, key_len);
os_free(key);
return 0;
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 652b67e..3fe5f77 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -365,6 +365,11 @@
/* Optional challenges generated in Phase 1 (EAP-FAST) */
u8 *peer_challenge, *auth_challenge;
+ /* Whether to use the EAP-FAST-MSCHAPv2 instantiation of EAP-MSCHAPv2.
+ * That variant is otherwise identical, but it generates the MSK using
+ * MS-MPPE keys in reverse order. */
+ bool eap_fast_mschapv2;
+
int num_rounds;
int num_rounds_short;
int force_disabled;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 8ad4d18..e84ae16 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -108,6 +108,12 @@
if (data == NULL)
return NULL;
+ wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s",
+ sm->eap_fast_mschapv2 ? "FAST-" : "",
+ sm->peer_challenge && sm->auth_challenge ?
+ " with preset challenges" : "",
+ sm->init_phase2 ? " for Phase 2" : "");
+
if (sm->peer_challenge) {
data->peer_challenge = os_memdup(sm->peer_challenge,
MSCHAPV2_CHAL_LEN);
@@ -844,6 +850,7 @@
struct eap_mschapv2_data *data = priv;
u8 *key;
int key_len;
+ bool first_is_send;
if (!data->master_key_valid || !data->success)
return NULL;
@@ -854,12 +861,25 @@
if (key == NULL)
return NULL;
- /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
- * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
- if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1,
- 0) < 0 ||
+ /*
+ * [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation
+ * MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding)
+ * On a Peer:
+ * MS-MPPE-Recv-Key = MasterSendKey
+ * MS-MPPE-Send-Key = MasterReceiveKey
+ *
+ * RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2)
+ * MSK = MasterSendKey + MasterReceiveKey
+ * (i.e., reverse order and no padding)
+ *
+ * On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2
+ * starts with Receive key.
+ */
+ first_is_send = !sm->eap_fast_mschapv2;
+ if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN,
+ first_is_send, 0) < 0 ||
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 0, 0) < 0) {
+ MSCHAPV2_KEY_LEN, !first_is_send, 0) < 0) {
os_free(key);
return NULL;
}
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 605feb2..97f4dd2 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -666,7 +666,10 @@
* sufficiently smaller than the prime or order might need pre-pending
* with zeros.
*/
- crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, scalar, order_len,
+ order_len) < 0)
+ goto fin;
+
if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
element + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
@@ -742,7 +745,9 @@
* 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.
*/
- crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, prime_len);
/* server element: x, y */
@@ -755,7 +760,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->server_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* my element: x, y */
@@ -768,7 +776,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* my scalar */
- crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* the ciphersuite */
@@ -796,7 +807,9 @@
goto fin;
/* k */
- crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, prime_len);
/* my element */
@@ -809,7 +822,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* my scalar */
- crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* server element: x, y */
@@ -822,7 +838,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->server_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* the ciphersuite */
diff --git a/src/eap_peer/eap_teap.c b/src/eap_peer/eap_teap.c
index bc7f6f4..ced7b16 100644
--- a/src/eap_peer/eap_teap.c
+++ b/src/eap_peer/eap_teap.c
@@ -319,6 +319,13 @@
if (!data->phase2_method)
return -1;
+ /* While RFC 7170 does not describe this, EAP-TEAP has been deployed
+ * with implementations that use the EAP-FAST-MSCHAPv2, instead of the
+ * EAP-MSCHAPv2, way of deriving the MSK for IMSK. Use that design here
+ * to interoperate.
+ */
+ sm->eap_fast_mschapv2 = true;
+
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
@@ -1298,6 +1305,33 @@
goto done;
}
+ if (tlv.crypto_binding) {
+ if (tlv.iresult != TEAP_STATUS_SUCCESS &&
+ tlv.result != TEAP_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
+ failed = 1;
+ error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
+ goto done;
+ }
+
+ tmp = eap_teap_process_crypto_binding(sm, data, ret,
+ tlv.crypto_binding,
+ tlv.crypto_binding_len);
+ if (!tmp) {
+ failed = 1;
+ error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+ } else {
+ resp = wpabuf_concat(resp, tmp);
+ if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
+ data->result_success_done = 1;
+ if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) {
+ data->inner_method_done = 0;
+ data->iresult_verified = 1;
+ }
+ }
+ }
+
if (tlv.identity_type == TEAP_IDENTITY_TYPE_MACHINE) {
struct eap_peer_config *config = eap_get_config(sm);
@@ -1353,33 +1387,6 @@
}
}
- if (tlv.crypto_binding) {
- if (tlv.iresult != TEAP_STATUS_SUCCESS &&
- tlv.result != TEAP_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG,
- "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
- failed = 1;
- error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
- goto done;
- }
-
- tmp = eap_teap_process_crypto_binding(sm, data, ret,
- tlv.crypto_binding,
- tlv.crypto_binding_len);
- if (!tmp) {
- failed = 1;
- error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
- } else {
- resp = wpabuf_concat(resp, tmp);
- if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
- data->result_success_done = 1;
- if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) {
- data->inner_method_done = 0;
- data->iresult_verified = 1;
- }
- }
- }
-
if (data->result_success_done && data->session_ticket_used &&
eap_teap_derive_msk(data) == 0) {
/* Assume the server might accept authentication without going
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 2894cfb..3696e1d 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -209,6 +209,7 @@
EAP_TEAP_ID_REQUEST_MACHINE_ACCEPT_USER = 4,
EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE = 5,
} eap_teap_id;
+ int eap_teap_method_sequence;
/**
* eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 28bb564..1c59bb0 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -176,9 +176,15 @@
METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
} method_pending;
+ /* Optional challenges generated in Phase 1 (EAP-FAST) */
u8 *auth_challenge;
u8 *peer_challenge;
+ /* Whether to use the EAP-FAST-MSCHAPv2 instantiation of EAP-MSCHAPv2.
+ * That variant is otherwise identical, but it generates the MSK using
+ * MS-MPPE keys in reverse order. */
+ bool eap_fast_mschapv2;
+
struct wpabuf *assoc_wps_ie;
struct wpabuf *assoc_p2p_ie;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 55d48d9..0596334 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -357,18 +357,7 @@
if (key_len > isk_len)
key_len = isk_len;
- if (key_len == 32 &&
- data->phase2_method->vendor == EAP_VENDOR_IETF &&
- data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
- /*
- * EAP-FAST uses reverse order for MS-MPPE keys when deriving
- * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
- * ISK for EAP-FAST cryptobinding.
- */
- os_memcpy(isk, key + 16, 16);
- os_memcpy(isk + 16, key, 16);
- } else
- os_memcpy(isk, key, key_len);
+ os_memcpy(isk, key, key_len);
os_free(key);
return 0;
@@ -961,6 +950,7 @@
sm->auth_challenge = data->key_block_p->server_challenge;
sm->peer_challenge = data->key_block_p->client_challenge;
}
+ sm->eap_fast_mschapv2 = true;
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 9b3eb26..3dc6a39 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -64,6 +64,12 @@
return NULL;
data->state = CHALLENGE;
+ wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s",
+ sm->eap_fast_mschapv2 ? "FAST-" : "",
+ sm->peer_challenge && sm->auth_challenge ?
+ " with preset challenges" : "",
+ sm->init_phase2 ? " for Phase 2" : "");
+
if (sm->auth_challenge) {
os_memcpy(data->auth_challenge, sm->auth_challenge,
CHALLENGE_LEN);
@@ -542,6 +548,7 @@
{
struct eap_mschapv2_data *data = priv;
u8 *key;
+ bool first_is_send;
if (data->state != SUCCESS || !data->master_key_valid)
return NULL;
@@ -550,11 +557,26 @@
key = os_malloc(*len);
if (key == NULL)
return NULL;
+ /*
+ * [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation
+ * MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding)
+ * On an Authenticator:
+ * MS-MPPE-Recv-Key = MasterReceiveKey
+ * MS-MPPE-Send-Key = MasterSendKey
+ *
+ * RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2)
+ * MSK = MasterSendKey + MasterReceiveKey
+ * (i.e., reverse order and no padding)
+ *
+ * On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2
+ * starts with Receive key.
+ */
+ first_is_send = sm->eap_fast_mschapv2;
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
- if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
- 1) < 0 ||
+ if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN,
+ first_is_send, 1) < 0 ||
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 1, 1) < 0) {
+ MSCHAPV2_KEY_LEN, !first_is_send, 1) < 0) {
os_free(key);
return NULL;
}
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 81cddca..afafaef 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -293,7 +293,10 @@
/* We send the element as (x,y) followed by the scalar */
element = wpabuf_put(data->outbuf, 2 * prime_len);
scalar = wpabuf_put(data->outbuf, order_len);
- crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, scalar, order_len,
+ order_len) < 0)
+ goto fin;
+
if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
element + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
@@ -349,7 +352,9 @@
*
* First is k
*/
- crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, prime_len);
/* server element: x, y */
@@ -362,7 +367,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* peer element: x, y */
@@ -375,7 +383,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* peer scalar */
- crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->peer_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* ciphersuite */
@@ -785,7 +796,9 @@
goto fin;
/* k */
- crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, prime_len);
/* peer element: x, y */
@@ -798,7 +811,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* peer scalar */
- crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->peer_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* server element: x, y */
@@ -811,7 +827,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* ciphersuite */
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
index 691b44a..e32c6e4 100644
--- a/src/eap_server/eap_server_teap.c
+++ b/src/eap_server/eap_server_teap.c
@@ -74,11 +74,15 @@
enum teap_error_codes error_code;
enum teap_identity_types cur_id_type;
+
+ bool check_crypto_binding;
};
static int eap_teap_process_phase2_start(struct eap_sm *sm,
struct eap_teap_data *data);
+static int eap_teap_phase2_init(struct eap_sm *sm, struct eap_teap_data *data,
+ int vendor, enum eap_type eap_type);
static const char * eap_teap_state_txt(int state)
@@ -704,6 +708,8 @@
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+ data->check_crypto_binding = true;
+
return buf;
}
@@ -889,6 +895,7 @@
struct eap_teap_data *data = priv;
struct wpabuf *req = NULL;
int piggyback = 0;
+ bool move_to_method = true;
if (data->ssl.state == FRAG_ACK) {
return eap_server_tls_build_ack(id, EAP_TYPE_TEAP,
@@ -940,6 +947,21 @@
break;
case CRYPTO_BINDING:
req = eap_teap_build_crypto_binding(sm, data);
+ if (req && sm->cfg->eap_teap_auth == 0 &&
+ data->inner_eap_not_done &&
+ !data->phase2_method &&
+ sm->cfg->eap_teap_method_sequence == 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Continue with inner EAP authentication for second credential (optimized)");
+ eap_teap_state(data, PHASE2_ID);
+ if (eap_teap_phase2_init(sm, data, EAP_VENDOR_IETF,
+ EAP_TYPE_IDENTITY) < 0) {
+ eap_teap_state(data, FAILURE);
+ wpabuf_free(req);
+ return NULL;
+ }
+ move_to_method = false;
+ }
if (data->phase2_method) {
/*
* Include the start of the next EAP method in the
@@ -950,7 +972,8 @@
eap = eap_teap_build_phase2_req(sm, data, id);
req = wpabuf_concat(req, eap);
- eap_teap_state(data, PHASE2_METHOD);
+ if (move_to_method)
+ eap_teap_state(data, PHASE2_METHOD);
}
break;
case REQUEST_PAC:
@@ -1008,6 +1031,13 @@
if (!data->phase2_method)
return -1;
+ /* While RFC 7170 does not describe this, EAP-TEAP has been deployed
+ * with implementations that use the EAP-FAST-MSCHAPv2, instead of the
+ * EAP-MSCHAPv2, way of deriving the MSK for IMSK. Use that design here
+ * to interoperate.
+ */
+ sm->eap_fast_mschapv2 = true;
+
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
@@ -1503,7 +1533,8 @@
struct wpabuf *in_data)
{
struct eap_teap_tlv_parse tlv;
- int check_crypto_binding = data->state == CRYPTO_BINDING;
+ bool check_crypto_binding = data->state == CRYPTO_BINDING ||
+ data->check_crypto_binding;
if (eap_teap_parse_tlvs(in_data, &tlv) < 0) {
wpa_printf(MSG_DEBUG,
@@ -1586,6 +1617,7 @@
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Valid Crypto-Binding TLV received");
+ data->check_crypto_binding = false;
if (data->final_result) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Authentication completed successfully");
@@ -1664,7 +1696,8 @@
"EAP-TEAP: Continue with basic password authentication for second credential");
eap_teap_state(data, PHASE2_BASIC_AUTH);
} else if (check_crypto_binding && data->state == CRYPTO_BINDING &&
- sm->cfg->eap_teap_auth == 0 && data->inner_eap_not_done) {
+ sm->cfg->eap_teap_auth == 0 && data->inner_eap_not_done &&
+ sm->cfg->eap_teap_method_sequence == 1) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Continue with inner EAP authentication for second credential");
eap_teap_state(data, PHASE2_ID);
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b7fde1b..e1f72ac 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -441,6 +441,7 @@
return NULL;
dl_list_add(&p2p->devices, &dev->list);
os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
+ dev->support_6ghz = false;
return dev;
}
@@ -611,6 +612,8 @@
dev->info.group_capab = msg->capability[1];
}
+ p2p_update_peer_6ghz_capab(dev, msg);
+
if (msg->ext_listen_timing) {
dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
dev->ext_listen_interval =
@@ -634,6 +637,15 @@
}
+void p2p_update_peer_6ghz_capab(struct p2p_device *dev,
+ const struct p2p_message *msg)
+{
+ if (msg->capability &&
+ (msg->capability[0] & P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE))
+ dev->support_6ghz = true;
+}
+
+
static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies,
size_t ies_len)
{
@@ -2085,6 +2097,7 @@
}
}
+ p2p_update_peer_6ghz_capab(dev, &msg);
os_get_reltime(&dev->last_seen);
p2p_parse_free(&msg);
return; /* already known */
@@ -5606,7 +5619,7 @@
if (!dev)
return false;
- return !!(dev->info.dev_capab & P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE);
+ return dev->support_6ghz;
}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index e3d704b..afd8969 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -904,6 +904,9 @@
p2p_add_dev_info(p2p, sa, dev, &msg);
}
+ if (dev)
+ p2p_update_peer_6ghz_capab(dev, &msg);
+
if (p2p->go_neg_peer && p2p->go_neg_peer == dev)
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
@@ -1230,6 +1233,7 @@
return;
}
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ p2p_update_peer_6ghz_capab(dev, &msg);
if (msg.dialog_token != dev->dialog_token) {
p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
@@ -1525,6 +1529,8 @@
return;
}
+ p2p_update_peer_6ghz_capab(dev, &msg);
+
if (dev->go_state == REMOTE_GO && msg.group_id) {
/* Store SSID for Provisioning step */
p2p->ssid_len = msg.group_id_len - ETH_ALEN;
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index d8dc6f7..235467e 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -150,6 +150,7 @@
struct wpabuf *go_neg_conf;
int sd_pending_bcast_queries;
+ bool support_6ghz;
};
struct p2p_sd_query {
@@ -875,6 +876,8 @@
struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
const u8 *addr,
struct p2p_message *msg);
+void p2p_update_peer_6ghz_capab(struct p2p_device *dev,
+ const struct p2p_message *msg);
void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
struct p2p_device *dev, struct p2p_message *msg);
int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 1a78e14..f75cee8 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -365,7 +365,7 @@
(conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) {
bool is_6ghz_capab;
- is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+ is_6ghz_capab = is_p2p_6ghz_capable(p2p) && dev &&
p2p_is_peer_6ghz_capab(
p2p, dev->info.p2p_device_addr);
p2p_buf_add_channel_list(buf, p2p->cfg->country,
@@ -619,6 +619,8 @@
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
+ p2p_update_peer_6ghz_capab(dev, &msg);
+
if (!msg.adv_id) {
allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
if (!(msg.wps_config_methods & allowed_config_methods)) {
@@ -1367,6 +1369,8 @@
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
+ p2p_update_peer_6ghz_capab(dev, &msg);
+
if (dev->dialog_token != msg.dialog_token) {
p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
msg.dialog_token, dev->dialog_token);
diff --git a/src/pasn/Makefile b/src/pasn/Makefile
new file mode 100644
index 0000000..a5b2c6b
--- /dev/null
+++ b/src/pasn/Makefile
@@ -0,0 +1,16 @@
+CFLAGS += -DCONFIG_SAE
+CFLAGS += -DCONFIG_FILS
+CFLAGS += -DIEEE8021X_EAPOL
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_SAE_PK
+CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_SHA512
+CFLAGS += -DCONFIG_PASN
+
+LIB_OBJS= \
+ pasn_initiator.o \
+ pasn_responder.o
+
+include ../lib.rules
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
new file mode 100644
index 0000000..9c2f397
--- /dev/null
+++ b/src/pasn/pasn_common.h
@@ -0,0 +1,184 @@
+/*
+ * PASN info for initiator and responder
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (c) 2022, Jouni Malinen <j@w1.fi>
+ * Copyright (C) 2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PASN_COMMON_H
+#define PASN_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef CONFIG_PASN
+
+enum pasn_fils_state {
+ PASN_FILS_STATE_NONE = 0,
+ PASN_FILS_STATE_PENDING_AS,
+ PASN_FILS_STATE_COMPLETE
+};
+
+struct pasn_fils {
+ u8 state;
+ u8 nonce[FILS_NONCE_LEN];
+ u8 anonce[FILS_NONCE_LEN];
+ u8 session[FILS_SESSION_LEN];
+ u8 erp_pmkid[PMKID_LEN];
+ bool completed;
+ struct wpabuf *erp_resp;
+};
+
+struct pasn_data {
+ int akmp;
+ int cipher;
+ u16 group;
+ bool secure_ltf;
+ int freq;
+ size_t kdk_len;
+
+ u8 trans_seq;
+ u8 status;
+
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ size_t pmk_len;
+ u8 pmk[PMK_LEN_MAX];
+ bool using_pmksa;
+
+ u8 hash[SHA384_MAC_LEN];
+
+ struct wpabuf *beacon_rsne_rsnxe;
+ struct wpa_ptk ptk;
+ struct crypto_ecdh *ecdh;
+
+ struct wpabuf *comeback;
+ u16 comeback_after;
+
+#ifdef CONFIG_SAE
+ struct sae_data sae;
+ struct sae_pt *pt;
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ bool fils_eapol;
+ bool fils_wd_valid;
+ struct pasn_fils fils;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_IEEE80211R
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+#endif /* CONFIG_IEEE80211R */
+ /* Note that this pointers to RSN PMKSA cache are actually defined
+ * differently for the PASN initiator (using RSN Supplicant
+ * implementation) and PASN responser (using RSN Authenticator
+ * implementation). Functions cannot be mixed between those cases. */
+ struct rsn_pmksa_cache *pmksa;
+ struct rsn_pmksa_cache_entry *pmksa_entry;
+ struct eapol_sm *eapol;
+ int fast_reauth;
+#ifdef CONFIG_TESTING_OPTIONS
+ int corrupt_mic;
+#endif /* CONFIG_TESTING_OPTIONS */
+ void *cb_ctx;
+ u16 rsnxe_capab;
+ int network_id;
+
+ u8 wrapped_data_format;
+ struct wpabuf *secret;
+
+ /* Reponder */
+ int wpa_key_mgmt;
+ int rsn_pairwise;
+ bool derive_kdk;
+ const char *password;
+ int disable_pmksa_caching;
+ int *pasn_groups;
+ struct wpabuf *wrapped_data;
+ int use_anti_clogging;
+ const u8 *rsn_ie;
+ const u8 *rsnxe_ie;
+ size_t rsn_ie_len;
+
+ u8 *comeback_key;
+ struct os_reltime last_comeback_key_update;
+ u16 comeback_idx;
+ u16 *comeback_pending_idx;
+
+ bool custom_pmkid_valid;
+ u8 custom_pmkid[PMKID_LEN];
+
+ /**
+ * Extra elements to add into Authentication frames. These can be used,
+ * e.g., for Wi-Fi Aware use cases.
+ */
+ const u8 *extra_ies;
+ size_t extra_ies_len;
+
+ /**
+ * send_mgmt - Function handler to transmit a Management frame
+ * @ctx: Callback context from cb_ctx
+ * @frame_buf : Frame to transmit
+ * @frame_len: Length of frame to transmit
+ * @freq: Frequency in MHz for the channel on which to transmit
+ * @wait_dur: How many milliseconds to wait for a response frame
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_mgmt)(void *ctx, const u8 *data, size_t data_len, int noack,
+ unsigned int freq, unsigned int wait);
+ /**
+ * validate_custom_pmkid - Handler to validate vendor specific PMKID
+ * @ctx: Callback context from cb_ctx
+ * @addr : MAC address of the peer
+ * @pmkid: Custom PMKID
+ * Returns: 0 on success (valid PMKID), -1 on failure
+ */
+ int (*validate_custom_pmkid)(void *ctx, const u8 *addr,
+ const u8 *pmkid);
+};
+
+/* Initiator */
+
+void wpa_pasn_reset(struct pasn_data *pasn);
+int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid,
+ int akmp, int cipher, u16 group,
+ int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback);
+int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid,
+ int akmp, int cipher, u16 group,
+ int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback);
+int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
+ struct wpa_pasn_params_data *pasn_params);
+int wpa_pasn_auth_tx_status(struct pasn_data *pasn,
+ const u8 *data, size_t data_len, u8 acked);
+
+/* Responder */
+int handle_auth_pasn_1(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr,
+ struct rsn_pmksa_cache_entry *pmksa, u16 status);
+
+#endif /* CONFIG_PASN */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* PASN_COMMON_H */
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
new file mode 100644
index 0000000..1f9a508
--- /dev/null
+++ b/src/pasn/pasn_initiator.c
@@ -0,0 +1,1393 @@
+/*
+ * PASN initiator processing
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (C) 2022, Qualcomm Innovation Center, 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/wpa_common.h"
+#include "common/sae.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/dragonfly.h"
+#include "crypto/sha384.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "eap_common/eap_defs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "pasn_common.h"
+
+
+#ifdef CONFIG_SAE
+
+static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn)
+{
+ struct wpabuf *buf = NULL;
+ int ret;
+
+ ret = sae_set_group(&pasn->sae, pasn->group);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+ return NULL;
+ }
+
+ ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt,
+ pasn->own_addr, pasn->peer_addr,
+ NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+ return NULL;
+ }
+
+ /* Need to add the entire Authentication frame body */
+ buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+ return NULL;
+ }
+
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+
+ sae_write_commit(&pasn->sae, buf, NULL, 0);
+ pasn->sae.state = SAE_COMMITTED;
+
+ return buf;
+}
+
+
+static int wpas_pasn_wd_sae_rx(struct pasn_data *pasn, struct wpabuf *wd)
+{
+ const u8 *data;
+ size_t buf_len;
+ u16 len, res, alg, seq, status;
+ int groups[] = { pasn->group, 0 };
+ int ret;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head_u8(wd);
+ buf_len = wpabuf_len(wd);
+
+ /* first handle the commit message */
+ if (buf_len < 2) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
+ return -1;
+ }
+
+ len = WPA_GET_LE16(data);
+ if (len < 6 || buf_len - 2 < len) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
+ return -1;
+ }
+
+ buf_len -= 2;
+ data += 2;
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_SAE || seq != 1 ||
+ status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
+ return -1;
+ }
+
+ res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
+ 1, NULL);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
+ return -1;
+ }
+
+ /* Process the commit message and derive the PMK */
+ ret = sae_process_commit(&pasn->sae);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+ return -1;
+ }
+
+ buf_len -= len;
+ data += len;
+
+ /* Handle the confirm message */
+ if (buf_len < 2) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
+ return -1;
+ }
+
+ len = WPA_GET_LE16(data);
+ if (len < 6 || buf_len - 2 < len) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
+ return -1;
+ }
+
+ buf_len -= 2;
+ data += 2;
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
+ return -1;
+ }
+
+ res = sae_check_confirm(&pasn->sae, data + 6, len - 6, NULL);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
+ pasn->sae.state = SAE_ACCEPTED;
+
+ return 0;
+}
+
+
+static struct wpabuf * wpas_pasn_wd_sae_confirm(struct pasn_data *pasn)
+{
+ struct wpabuf *buf = NULL;
+
+ /* Need to add the entire authentication frame body */
+ buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+ return NULL;
+ }
+
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 2);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ sae_write_confirm(&pasn->sae, buf);
+ pasn->sae.state = SAE_CONFIRMED;
+
+ return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_FILS
+
+static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn)
+{
+ struct wpabuf *buf = NULL;
+ struct wpabuf *erp_msg;
+ int ret;
+
+ erp_msg = eapol_sm_build_erp_reauth_start(pasn->eapol);
+ if (!erp_msg) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: ERP EAP-Initiate/Re-auth unavailable");
+ return NULL;
+ }
+
+ if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 ||
+ random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0)
+ goto fail;
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce,
+ FILS_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session,
+ FILS_SESSION_LEN);
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ /* Add the authentication algorithm */
+ wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
+
+ /* Authentication Transaction seq# */
+ wpabuf_put_le16(buf, 1);
+
+ /* Status Code */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ /* Own RSNE */
+ wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
+
+ /* FILS Nonce */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+ wpabuf_put_data(buf, pasn->fils.nonce, FILS_NONCE_LEN);
+
+ /* FILS Session */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(buf, pasn->fils.session, FILS_SESSION_LEN);
+
+ /* Wrapped Data (ERP) */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_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.
+ */
+ ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg),
+ wpabuf_len(erp_msg),
+ pasn->fils.erp_pmkid);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID");
+ goto fail;
+ }
+
+ wpabuf_free(erp_msg);
+ erp_msg = NULL;
+
+ wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf);
+ return buf;
+fail:
+ wpabuf_free(erp_msg);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+static struct wpabuf * wpas_pasn_wd_fils_auth(struct pasn_data *pasn)
+{
+ wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u",
+ pasn->fils.completed);
+
+ /* Nothing to add as we are done */
+ if (pasn->fils.completed)
+ return NULL;
+
+ if (!pasn->fils_eapol) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Missing Indication IE or PFS");
+ return NULL;
+ }
+
+ return wpas_pasn_fils_build_auth(pasn);
+}
+
+
+static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd)
+{
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsne_data;
+ u8 rmsk[ERP_MAX_KEY_LEN];
+ size_t rmsk_len;
+ u8 anonce[FILS_NONCE_LEN];
+ const u8 *data;
+ size_t buf_len;
+ struct wpabuf *fils_wd = NULL;
+ u16 alg, seq, status;
+ int ret;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head(wd);
+ buf_len = wpabuf_len(wd);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu",
+ data, buf_len);
+
+ /* first handle the header */
+ if (buf_len < 6) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short");
+ return -1;
+ }
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: commit: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_FILS_SK || seq != 2 ||
+ status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Dropping peer authentication");
+ return -1;
+ }
+
+ data += 6;
+ buf_len -= 6;
+
+ if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
+ return -1;
+ }
+
+ if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
+ !elems.wrapped_data) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
+ return -1;
+ }
+
+ ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsne_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
+ return -1;
+ }
+
+ ret = wpa_pasn_validate_rsne(&rsne_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
+ return -1;
+ }
+
+ if (rsne_data.num_pmkid) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Not expecting PMKID in RSNE");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.fils_nonce,
+ FILS_NONCE_LEN);
+ os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session,
+ FILS_SESSION_LEN);
+
+ if (os_memcmp(pasn->fils.session, elems.fils_session,
+ FILS_SESSION_LEN)) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch");
+ return -1;
+ }
+
+ fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+
+ if (!fils_wd) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Failed getting wrapped data");
+ return -1;
+ }
+
+ eapol_sm_process_erp_finish(pasn->eapol, wpabuf_head(fils_wd),
+ wpabuf_len(fils_wd));
+
+ wpabuf_free(fils_wd);
+ fils_wd = NULL;
+
+ if (eapol_sm_failed(pasn->eapol)) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed");
+ return -1;
+ }
+
+ rmsk_len = ERP_MAX_KEY_LEN;
+ ret = eapol_sm_get_key(pasn->eapol, rmsk, rmsk_len);
+
+ if (ret == PMK_LEN) {
+ rmsk_len = PMK_LEN;
+ ret = eapol_sm_get_key(pasn->eapol, rmsk, rmsk_len);
+ }
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK");
+ return -1;
+ }
+
+ ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len,
+ pasn->fils.nonce, anonce, NULL, 0,
+ pasn->pmk, &pasn->pmk_len);
+
+ forced_memzero(rmsk, sizeof(rmsk));
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid,
+ PMKID_LEN);
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded");
+
+ pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk,
+ pasn->pmk_len, pasn->fils.erp_pmkid,
+ NULL, 0, pasn->peer_addr,
+ pasn->own_addr, NULL,
+ pasn->akmp, 0);
+
+ pasn->fils.completed = true;
+ return 0;
+}
+
+#endif /* CONFIG_FILS */
+
+
+static struct wpabuf * wpas_pasn_get_wrapped_data(struct pasn_data *pasn)
+{
+ if (pasn->using_pmksa)
+ return NULL;
+
+ switch (pasn->akmp) {
+ case WPA_KEY_MGMT_PASN:
+ /* no wrapped data */
+ return NULL;
+ case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+ if (pasn->trans_seq == 0)
+ return wpas_pasn_wd_sae_commit(pasn);
+ if (pasn->trans_seq == 2)
+ return wpas_pasn_wd_sae_confirm(pasn);
+#endif /* CONFIG_SAE */
+ wpa_printf(MSG_ERROR,
+ "PASN: SAE: Cannot derive wrapped data");
+ return NULL;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+#ifdef CONFIG_FILS
+ return wpas_pasn_wd_fils_auth(pasn);
+#endif /* CONFIG_FILS */
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ /*
+ * Wrapped data with these AKMs is optional and is only needed
+ * for further validation of FT security parameters. For now do
+ * not use them.
+ */
+ return NULL;
+ default:
+ wpa_printf(MSG_ERROR,
+ "PASN: TODO: Wrapped data for akmp=0x%x",
+ pasn->akmp);
+ return NULL;
+ }
+}
+
+
+static u8 wpas_pasn_get_wrapped_data_format(struct pasn_data *pasn)
+{
+ if (pasn->using_pmksa)
+ return WPA_PASN_WRAPPED_DATA_NO;
+
+ /* Note: Valid AKMP is expected to already be validated */
+ switch (pasn->akmp) {
+ case WPA_KEY_MGMT_SAE:
+ return WPA_PASN_WRAPPED_DATA_SAE;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+ return WPA_PASN_WRAPPED_DATA_FILS_SK;
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ /*
+ * Wrapped data with these AKMs is optional and is only needed
+ * for further validation of FT security parameters. For now do
+ * not use them.
+ */
+ return WPA_PASN_WRAPPED_DATA_NO;
+ case WPA_KEY_MGMT_PASN:
+ default:
+ return WPA_PASN_WRAPPED_DATA_NO;
+ }
+}
+
+
+static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn,
+ const struct wpabuf *comeback,
+ bool verify)
+{
+ struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+ const u8 *pmkid;
+ u8 wrapped_data;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "PASN: Building frame 1");
+
+ if (pasn->trans_seq)
+ return NULL;
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ /* Get public key */
+ pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
+ pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh));
+ if (!pubkey) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
+ goto fail;
+ }
+
+ wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid,
+ pasn->own_addr, pasn->peer_addr,
+ pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+ pmkid = NULL;
+ if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+ pmkid = pasn->pmk_r1_name;
+#else /* CONFIG_IEEE80211R */
+ goto fail;
+#endif /* CONFIG_IEEE80211R */
+ } else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr,
+ pasn->own_addr, NULL, NULL, pasn->akmp);
+ if (pmksa && pasn->custom_pmkid_valid)
+ pmkid = pasn->custom_pmkid;
+ else if (pmksa)
+ pmkid = pmksa->pmkid;
+
+ /*
+ * Note: Even when PMKSA is available, also add wrapped data as
+ * it is possible that the PMKID is no longer valid at the AP.
+ */
+ if (!verify)
+ wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+ }
+
+ if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
+ goto fail;
+
+ if (!wrapped_data_buf)
+ wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
+
+ wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+ pubkey, true, comeback, -1);
+
+ if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+ goto fail;
+
+ wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab);
+
+ wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
+ ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+ wpabuf_head_u8(buf) + IEEE80211_HDRLEN,
+ wpabuf_len(buf) - IEEE80211_HDRLEN,
+ pasn->hash);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
+ goto fail;
+ }
+
+ pasn->trans_seq++;
+
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(pubkey);
+
+ wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success");
+ return buf;
+fail:
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(pubkey);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn)
+{
+ struct wpabuf *buf, *wrapped_data_buf = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len, data_len;
+ const u8 *data;
+ u8 *ptr;
+ u8 wrapped_data;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
+
+ if (pasn->trans_seq != 2)
+ return NULL;
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid,
+ pasn->own_addr, pasn->peer_addr,
+ pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+ wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+
+ if (!wrapped_data_buf)
+ wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
+
+ wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+ NULL, false, NULL, -1);
+
+ if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+ goto fail;
+ wpabuf_free(wrapped_data_buf);
+ wrapped_data_buf = NULL;
+
+ /* Add the MIC */
+ mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+ wpabuf_put_u8(buf, WLAN_EID_MIC);
+ wpabuf_put_u8(buf, mic_len);
+ ptr = wpabuf_put(buf, mic_len);
+
+ os_memset(ptr, 0, mic_len);
+
+ data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
+ data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
+
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ pasn->own_addr, pasn->peer_addr,
+ pasn->hash, mic_len * 2, data, data_len, mic);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
+ goto fail;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (pasn->corrupt_mic) {
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC");
+ mic[0] = ~mic[0];
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(ptr, mic, mic_len);
+
+ pasn->trans_seq++;
+
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Success");
+ return buf;
+fail:
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+void wpa_pasn_reset(struct pasn_data *pasn)
+{
+ wpa_printf(MSG_DEBUG, "PASN: Reset");
+
+ crypto_ecdh_deinit(pasn->ecdh);
+ pasn->ecdh = NULL;
+
+
+ pasn->akmp = 0;
+ pasn->cipher = 0;
+ pasn->group = 0;
+ pasn->trans_seq = 0;
+ pasn->pmk_len = 0;
+ pasn->using_pmksa = false;
+
+ forced_memzero(pasn->pmk, sizeof(pasn->pmk));
+ forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+ forced_memzero(&pasn->hash, sizeof(pasn->hash));
+
+ wpabuf_free(pasn->beacon_rsne_rsnxe);
+ pasn->beacon_rsne_rsnxe = NULL;
+
+ wpabuf_free(pasn->comeback);
+ pasn->comeback = NULL;
+ pasn->comeback_after = 0;
+
+#ifdef CONFIG_SAE
+ sae_clear_data(&pasn->sae);
+ if (pasn->pt) {
+ sae_deinit_pt(pasn->pt);
+ pasn->pt = NULL;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ pasn->fils_eapol = false;
+ os_memset(&pasn->fils, 0, sizeof(pasn->fils));
+#endif /* CONFIG_FILS*/
+
+#ifdef CONFIG_IEEE80211R
+ forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
+ pasn->pmk_r1_len = 0;
+ os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
+#endif /* CONFIG_IEEE80211R */
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ pasn->pmksa_entry = NULL;
+#ifdef CONFIG_TESTING_OPTIONS
+ pasn->corrupt_mic = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+ pasn->network_id = 0;
+ pasn->derive_kdk = false;
+ pasn->rsn_ie = NULL;
+ pasn->rsn_ie_len = 0;
+ pasn->rsnxe_ie = NULL;
+ pasn->custom_pmkid_valid = false;
+}
+
+
+static int wpas_pasn_set_pmk(struct pasn_data *pasn,
+ struct wpa_ie_data *rsn_data,
+ struct wpa_pasn_params_data *pasn_data,
+ struct wpabuf *wrapped_data)
+{
+ static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
+
+ os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
+ pasn->pmk_len = 0;
+
+ if (pasn->akmp == WPA_KEY_MGMT_PASN) {
+ wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
+
+ pasn->pmk_len = WPA_PASN_PMK_LEN;
+ os_memcpy(pasn->pmk, pasn_default_pmk,
+ sizeof(pasn_default_pmk));
+ return 0;
+ }
+
+ if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+ wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
+ pasn->pmk_len = pasn->pmk_r1_len;
+ os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
+ pasn->using_pmksa = true;
+ return 0;
+#else /* CONFIG_IEEE80211R */
+ wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
+ return -1;
+#endif /* CONFIG_IEEE80211R */
+ }
+
+ if (rsn_data->num_pmkid) {
+ int ret;
+ struct rsn_pmksa_cache_entry *pmksa;
+ const u8 *pmkid = NULL;
+
+ if (pasn->custom_pmkid_valid) {
+ ret = pasn->validate_custom_pmkid(pasn->cb_ctx,
+ pasn->peer_addr,
+ rsn_data->pmkid);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed custom PMKID validation");
+ return -1;
+ }
+ } else {
+ pmkid = rsn_data->pmkid;
+ }
+
+ pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr,
+ pasn->own_addr,
+ pmkid, NULL, pasn->akmp);
+ if (pmksa) {
+ wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");
+
+ pasn->pmk_len = pmksa->pmk_len;
+ os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
+ pasn->using_pmksa = true;
+
+ return 0;
+ }
+ }
+
+#ifdef CONFIG_SAE
+ if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ int ret;
+
+ ret = wpas_pasn_wd_sae_rx(pasn, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing SAE wrapped data");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
+ pasn->pmk_len = PMK_LEN;
+ os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
+
+ pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk,
+ pasn->pmk_len,
+ pasn->sae.pmkid,
+ NULL, 0, pasn->peer_addr,
+ pasn->own_addr, NULL,
+ pasn->akmp, 0);
+ return 0;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ int ret;
+
+ ret = wpas_pasn_wd_fils_rx(pasn, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing FILS wrapped data");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+ }
+
+ return 0;
+ }
+#endif /* CONFIG_FILS */
+
+ /* TODO: Derive PMK based on wrapped data */
+ wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+}
+
+
+static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid, int akmp,
+ int cipher, u16 group, int freq,
+ const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback, bool verify)
+{
+ struct wpabuf *frame;
+ int ret;
+
+ pasn->ecdh = crypto_ecdh_init(group);
+ if (!pasn->ecdh) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
+ goto fail;
+ }
+
+ if (beacon_rsne && beacon_rsne_len) {
+ pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len +
+ beacon_rsnxe_len);
+ if (!pasn->beacon_rsne_rsnxe) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed storing beacon RSNE/RSNXE");
+ goto fail;
+ }
+
+ wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne,
+ beacon_rsne_len);
+ if (beacon_rsnxe && beacon_rsnxe_len)
+ wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsnxe,
+ beacon_rsnxe_len);
+ }
+
+ pasn->akmp = akmp;
+ pasn->cipher = cipher;
+ pasn->group = group;
+ pasn->freq = freq;
+
+ os_memcpy(pasn->own_addr, own_addr, ETH_ALEN);
+ os_memcpy(pasn->peer_addr, peer_addr, ETH_ALEN);
+ os_memcpy(pasn->bssid, bssid, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: Init%s: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
+ verify ? " (verify)" : "",
+ MAC2STR(pasn->peer_addr), pasn->akmp, pasn->cipher,
+ pasn->group);
+
+ frame = wpas_pasn_build_auth_1(pasn, comeback, verify);
+ if (!frame) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
+ goto fail;
+ }
+
+ ret = pasn->send_mgmt(pasn->cb_ctx,
+ wpabuf_head(frame), wpabuf_len(frame), 0,
+ pasn->freq, 1000);
+
+ wpabuf_free(frame);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+
+int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid,
+ int akmp, int cipher, u16 group,
+ int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback)
+{
+ /* TODO: Currently support only ECC groups */
+ if (!dragonfly_suitable_group(group, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Reject unsuitable group %u", group);
+ return -1;
+ }
+
+ switch (akmp) {
+ case WPA_KEY_MGMT_PASN:
+ break;
+#ifdef CONFIG_SAE
+ case WPA_KEY_MGMT_SAE:
+
+ if (beacon_rsnxe &&
+ !ieee802_11_rsnx_capab(beacon_rsnxe,
+ WLAN_RSNX_CAPAB_SAE_H2E)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: AP does not support SAE H2E");
+ return -1;
+ }
+
+ pasn->sae.state = SAE_NOTHING;
+ pasn->sae.send_confirm = 0;
+ break;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+ break;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ break;
+#endif /* CONFIG_IEEE80211R */
+ default:
+ wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp);
+ return -1;
+ }
+
+ return wpas_pasn_send_auth_1(pasn, own_addr, peer_addr, bssid, akmp,
+ cipher, group,
+ freq, beacon_rsne, beacon_rsne_len,
+ beacon_rsnxe, beacon_rsnxe_len, comeback,
+ false);
+}
+
+/*
+ * Wi-Fi Aware uses PASN handshake to authenticate peer devices.
+ * Devices can simply verify each other for subsequent sessions using
+ * pairing verification procedure.
+ *
+ * In pairing verification, Wi-Fi aware devices use PASN authentication
+ * frames with a custom PMKID and Wi-Fi Aware R4 specific verification IEs.
+ * It does not use wrapped data in the Authentication frames. This function
+ * provides support to construct PASN Authentication frames for pairing
+ * verification.
+ */
+int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid,
+ int akmp, int cipher, u16 group,
+ int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback)
+{
+ return wpas_pasn_send_auth_1(pasn, own_addr, peer_addr, bssid, akmp,
+ cipher, group, freq, beacon_rsne,
+ beacon_rsne_len, beacon_rsnxe,
+ beacon_rsnxe_len, comeback, true);
+}
+
+
+static bool is_pasn_auth_frame(struct pasn_data *pasn,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool rx)
+{
+ u16 fc;
+
+ if (!mgmt || len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ return false;
+
+ /* Not an Authentication frame; do nothing */
+ fc = le_to_host16(mgmt->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+ WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_AUTH)
+ return false;
+
+ /* Not our frame; do nothing */
+ if (os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
+ return false;
+
+ if (rx && (os_memcmp(mgmt->da, pasn->own_addr, ETH_ALEN) != 0 ||
+ os_memcmp(mgmt->sa, pasn->peer_addr, ETH_ALEN) != 0))
+ return false;
+
+ if (!rx && (os_memcmp(mgmt->sa, pasn->own_addr, ETH_ALEN) != 0 ||
+ os_memcmp(mgmt->da, pasn->peer_addr, ETH_ALEN) != 0))
+ return false;
+
+ /* Not PASN; do nothing */
+ if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
+ return false;
+
+ return true;
+}
+
+
+int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
+ struct wpa_pasn_params_data *pasn_params)
+
+{
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsn_data;
+ const struct ieee80211_mgmt *mgmt =
+ (const struct ieee80211_mgmt *) data;
+ struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len;
+ u16 status;
+ int ret, inc_y;
+ u8 *copy = NULL;
+ size_t mic_offset, copy_len;
+
+ if (!is_pasn_auth_frame(pasn, mgmt, len, true))
+ return -2;
+
+ if (mgmt->u.auth.auth_transaction !=
+ host_to_le16(pasn->trans_seq + 1)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: RX: Invalid transaction sequence: (%u != %u)",
+ le_to_host16(mgmt->u.auth.auth_transaction),
+ pasn->trans_seq + 1);
+ return -3;
+ }
+
+ status = le_to_host16(mgmt->u.auth.status_code);
+
+ if (status != WLAN_STATUS_SUCCESS &&
+ status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Authentication rejected - status=%u", status);
+ goto fail;
+ }
+
+ if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+ len - offsetof(struct ieee80211_mgmt,
+ u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed parsing Authentication frame");
+ goto fail;
+ }
+
+ /* Check that the MIC IE exists. Save it and zero out the memory */
+ mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+ if (status == WLAN_STATUS_SUCCESS) {
+ if (!elems.mic || elems.mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid MIC. Expecting len=%u",
+ mic_len);
+ goto fail;
+ }
+ os_memcpy(mic, elems.mic, mic_len);
+ }
+
+ if (!elems.pasn_params || !elems.pasn_params_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Missing PASN Parameters IE");
+ goto fail;
+ }
+
+ if (!pasn_params) {
+ wpa_printf(MSG_DEBUG, "PASN: pasn_params == NULL");
+ goto fail;
+ }
+
+ ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+ elems.pasn_params_len + 3,
+ true, pasn_params);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed validation PASN of Parameters IE");
+ goto fail;
+ }
+
+ if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Authentication temporarily rejected");
+
+ if (pasn_params->comeback && pasn_params->comeback_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Comeback token available. After=%u",
+ pasn_params->after);
+
+ if (!pasn_params->after)
+ return 1;
+
+ pasn->comeback = wpabuf_alloc_copy(
+ pasn_params->comeback,
+ pasn_params->comeback_len);
+ if (pasn->comeback)
+ pasn->comeback_after = pasn_params->after;
+ }
+
+ pasn->status = status;
+ goto fail;
+ }
+
+ if (!elems.rsn_ie) {
+ wpa_printf(MSG_DEBUG, "PASN: Missing RSNE");
+ goto fail;
+ }
+
+ ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+ goto fail;
+ }
+
+ ret = wpa_pasn_validate_rsne(&rsn_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
+ goto fail;
+ }
+
+ if (pasn->akmp != rsn_data.key_mgmt ||
+ pasn->cipher != rsn_data.pairwise_cipher) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+ goto fail;
+ }
+
+ if (pasn->group != pasn_params->group) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in group");
+ goto fail;
+ }
+
+ if (!pasn_params->pubkey || !pasn_params->pubkey_len) {
+ wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
+ goto fail;
+ }
+
+ if (pasn_params->pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
+ inc_y = 1;
+ } else if (pasn_params->pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
+ pasn_params->pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
+ inc_y = 0;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid first octet in pubkey=0x%x",
+ pasn_params->pubkey[0]);
+ goto fail;
+ }
+
+ secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
+ pasn_params->pubkey + 1,
+ pasn_params->pubkey_len - 1);
+
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
+ goto fail;
+ }
+
+ if (pasn_params->wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+ wrapped_data = ieee802_11_defrag(&elems,
+ WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+
+ if (!wrapped_data) {
+ wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+ goto fail;
+ }
+ }
+
+ ret = wpas_pasn_set_pmk(pasn, &rsn_data, pasn_params, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK");
+ goto fail;
+ }
+
+ ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
+ pasn->own_addr, pasn->peer_addr,
+ wpabuf_head(secret), wpabuf_len(secret),
+ &pasn->ptk, pasn->akmp, pasn->cipher,
+ pasn->kdk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
+ goto fail;
+ }
+
+ if (pasn->secure_ltf) {
+ ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive LTF keyseed");
+ goto fail;
+ }
+ }
+
+ wpabuf_free(wrapped_data);
+ wrapped_data = NULL;
+ wpabuf_free(secret);
+ secret = NULL;
+
+ /* Use a copy of the message since we need to clear the MIC field */
+ if (!elems.mic)
+ goto fail;
+ mic_offset = elems.mic - (const u8 *) &mgmt->u.auth;
+ copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+ if (mic_offset + mic_len > copy_len)
+ goto fail;
+ copy = os_memdup(&mgmt->u.auth, copy_len);
+ if (!copy)
+ goto fail;
+ os_memset(copy + mic_offset, 0, mic_len);
+
+ if (pasn->beacon_rsne_rsnxe) {
+ /* Verify the MIC */
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ pasn->peer_addr, pasn->own_addr,
+ wpabuf_head(pasn->beacon_rsne_rsnxe),
+ wpabuf_len(pasn->beacon_rsne_rsnxe),
+ copy, copy_len, out_mic);
+ } else {
+ u8 *rsne_rsnxe;
+ size_t rsne_rsnxe_len = 0;
+
+ /*
+ * Note: When Beacon rsne_rsnxe is not initialized, it is likely
+ * that this is for Wi-Fi Aware using PASN handshake for which
+ * Beacon RSNE/RSNXE are same as RSNE/RSNXE in the
+ * Authentication frame
+ */
+ if (elems.rsn_ie && elems.rsn_ie_len)
+ rsne_rsnxe_len += elems.rsn_ie_len + 2;
+ if (elems.rsnxe && elems.rsnxe_len)
+ rsne_rsnxe_len += elems.rsnxe_len + 2;
+
+ rsne_rsnxe = os_zalloc(rsne_rsnxe_len);
+ if (!rsne_rsnxe)
+ goto fail;
+
+ if (elems.rsn_ie && elems.rsn_ie_len)
+ os_memcpy(rsne_rsnxe, elems.rsn_ie - 2,
+ elems.rsn_ie_len + 2);
+ if (elems.rsnxe && elems.rsnxe_len)
+ os_memcpy(rsne_rsnxe + elems.rsn_ie_len + 2,
+ elems.rsnxe - 2, elems.rsnxe_len + 2);
+
+ wpa_hexdump_key(MSG_DEBUG, "PASN: RSN + RSNXE buf",
+ rsne_rsnxe, rsne_rsnxe_len);
+
+ /* Verify the MIC */
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ pasn->peer_addr, pasn->own_addr,
+ rsne_rsnxe,
+ rsne_rsnxe_len,
+ copy, copy_len, out_mic);
+
+ os_free(rsne_rsnxe);
+ }
+ os_free(copy);
+ copy = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
+ if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
+ goto fail;
+ }
+
+ pasn->trans_seq++;
+
+ wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
+
+ frame = wpas_pasn_build_auth_3(pasn);
+ if (!frame) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
+ goto fail;
+ }
+
+ ret = pasn->send_mgmt(pasn->cb_ctx,
+ wpabuf_head(frame), wpabuf_len(frame), 0,
+ pasn->freq, 100);
+ wpabuf_free(frame);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
+
+ pasn->status = WLAN_STATUS_SUCCESS;
+
+ return 0;
+fail:
+ wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating");
+ wpabuf_free(wrapped_data);
+ wpabuf_free(secret);
+ os_free(copy);
+
+ /*
+ * TODO: In case of an error the standard allows to silently drop
+ * the frame and terminate the authentication exchange. However, better
+ * reply to the AP with an error status.
+ */
+ if (status == WLAN_STATUS_SUCCESS)
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ else
+ pasn->status = status;
+
+ return -1;
+}
+
+
+int wpa_pasn_auth_tx_status(struct pasn_data *pasn,
+ const u8 *data, size_t data_len, u8 acked)
+
+{
+ const struct ieee80211_mgmt *mgmt =
+ (const struct ieee80211_mgmt *) data;
+
+ wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
+
+ if (!is_pasn_auth_frame(pasn, mgmt, data_len, false))
+ return -1;
+
+ if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
+ wpa_printf(MSG_ERROR,
+ "PASN: Invalid transaction sequence: (%u != %u)",
+ pasn->trans_seq,
+ le_to_host16(mgmt->u.auth.auth_transaction));
+ return 0;
+ }
+
+ wpa_printf(MSG_ERROR,
+ "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
+ acked);
+
+ /*
+ * Even if the frame was not acked, do not treat this is an error, and
+ * try to complete the flow, relying on the PASN timeout callback to
+ * clean up.
+ */
+ if (pasn->trans_seq == 3) {
+ wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
+ MAC2STR(pasn->peer_addr));
+ /*
+ * Either frame was not ACKed or it was ACKed but the trans_seq
+ * != 1, i.e., not expecting an RX frame, so we are done.
+ */
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
new file mode 100644
index 0000000..3b1912d
--- /dev/null
+++ b/src/pasn/pasn_responder.c
@@ -0,0 +1,1016 @@
+/*
+ * PASN responder processing
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (C) 2022, Qualcomm Innovation Center, 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/wpa_common.h"
+#include "common/sae.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/sha384.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/crypto.h"
+#include "ap/hostapd.h"
+#include "ap/comeback_token.h"
+#include "ap/ieee802_1x.h"
+#include "ap/pmksa_cache_auth.h"
+#include "pasn_common.h"
+
+#ifdef CONFIG_PASN
+#ifdef CONFIG_SAE
+
+static int pasn_wd_handle_sae_commit(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ struct wpabuf *wd)
+{
+ const u8 *data;
+ size_t buf_len;
+ u16 res, alg, seq, status;
+ int groups[] = { pasn->group, 0 };
+ int ret;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head_u8(wd);
+ buf_len = wpabuf_len(wd);
+
+ if (buf_len < 6) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
+ buf_len);
+ return -1;
+ }
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_SAE || seq != 1 ||
+ status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
+ wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit");
+ return -1;
+ }
+
+ sae_clear_data(&pasn->sae);
+ pasn->sae.state = SAE_NOTHING;
+
+ ret = sae_set_group(&pasn->sae, pasn->group);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+ return -1;
+ }
+
+ if (!pasn->password || !pasn->pt) {
+ wpa_printf(MSG_DEBUG, "PASN: No SAE PT found");
+ return -1;
+ }
+
+ ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, own_addr, peer_addr,
+ NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+ return -1;
+ }
+
+ res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0,
+ groups, 0, NULL);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit");
+ return -1;
+ }
+
+ /* Process the commit message and derive the PMK */
+ ret = sae_process_commit(&pasn->sae);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+ return -1;
+ }
+
+ pasn->sae.state = SAE_COMMITTED;
+
+ return 0;
+}
+
+
+static int pasn_wd_handle_sae_confirm(struct pasn_data *pasn,
+ const u8 *peer_addr, struct wpabuf *wd)
+{
+ const u8 *data;
+ size_t buf_len;
+ u16 res, alg, seq, status;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head_u8(wd);
+ buf_len = wpabuf_len(wd);
+
+ if (buf_len < 6) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
+ buf_len);
+ return -1;
+ }
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
+ return -1;
+ }
+
+ res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6, NULL);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+ return -1;
+ }
+
+ pasn->sae.state = SAE_ACCEPTED;
+
+ /*
+ * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with
+ * PASN/SAE should only be allowed with future PASN only. For now do not
+ * restrict this only for PASN.
+ */
+ if (pasn->disable_pmksa_caching)
+ return 0;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE",
+ pasn->sae.pmk, pasn->sae.pmk_len);
+ if (!pasn->sae.akmp)
+ pasn->sae.akmp = WPA_KEY_MGMT_SAE;
+
+ pmksa_cache_auth_add(pasn->pmksa, pasn->sae.pmk, pasn->sae.pmk_len,
+ pasn->sae.pmkid, NULL, 0, pasn->own_addr,
+ peer_addr, 0, NULL, pasn->sae.akmp);
+ return 0;
+}
+
+
+static struct wpabuf * pasn_get_sae_wd(struct pasn_data *pasn)
+{
+ struct wpabuf *buf = NULL;
+ u8 *len_ptr;
+ size_t len;
+
+ /* Need to add the entire Authentication frame body */
+ buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+ return NULL;
+ }
+
+ /* Need to add the entire authentication frame body for the commit */
+ len_ptr = wpabuf_put(buf, 2);
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+
+ /* Write the actual commit and update the length accordingly */
+ sae_write_commit(&pasn->sae, buf, NULL, 0);
+ len = wpabuf_len(buf);
+ WPA_PUT_LE16(len_ptr, len - 2);
+
+ /* Need to add the entire Authentication frame body for the confirm */
+ len_ptr = wpabuf_put(buf, 2);
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 2);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ sae_write_confirm(&pasn->sae, buf);
+ WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2);
+
+ pasn->sae.state = SAE_CONFIRMED;
+
+ return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_FILS
+
+static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn)
+{
+ struct pasn_fils *fils = &pasn->fils;
+ struct wpabuf *buf = NULL;
+
+ if (!fils->erp_resp) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp");
+ return NULL;
+ }
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ return NULL;
+
+ /* Add the authentication algorithm */
+ wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
+
+ /* Authentication Transaction seq# */
+ wpabuf_put_le16(buf, 2);
+
+ /* Status Code */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ /* Own RSNE */
+ wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
+
+ /* FILS Nonce */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+ wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN);
+
+ /* FILS Session */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN);
+
+ /* Wrapped Data */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
+ wpabuf_put_buf(buf, fils->erp_resp);
+
+ return buf;
+}
+
+#endif /* CONFIG_FILS */
+
+static struct wpabuf * pasn_get_wrapped_data(struct pasn_data *pasn)
+{
+ switch (pasn->akmp) {
+ case WPA_KEY_MGMT_PASN:
+ /* no wrapped data */
+ return NULL;
+ case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+ return pasn_get_sae_wd(pasn);
+#else /* CONFIG_SAE */
+ wpa_printf(MSG_ERROR,
+ "PASN: SAE: Cannot derive wrapped data");
+ return NULL;
+#endif /* CONFIG_SAE */
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+#ifdef CONFIG_FILS
+ return pasn_get_fils_wd(pasn);
+#endif /* CONFIG_FILS */
+ /* fall through */
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ default:
+ wpa_printf(MSG_ERROR,
+ "PASN: TODO: Wrapped data for akmp=0x%x",
+ pasn->akmp);
+ return NULL;
+ }
+}
+
+
+static int
+pasn_derive_keys(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ const u8 *cached_pmk, size_t cached_pmk_len,
+ struct wpa_pasn_params_data *pasn_data,
+ struct wpabuf *wrapped_data,
+ struct wpabuf *secret)
+{
+ static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
+ u8 pmk[PMK_LEN_MAX];
+ u8 pmk_len;
+ int ret;
+
+ os_memset(pmk, 0, sizeof(pmk));
+ pmk_len = 0;
+
+ if (!cached_pmk || !cached_pmk_len)
+ wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry");
+
+ if (pasn->akmp == WPA_KEY_MGMT_PASN) {
+ wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
+
+ pmk_len = WPA_PASN_PMK_LEN;
+ os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk));
+ } else if (cached_pmk && cached_pmk_len) {
+ wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry");
+
+ pmk_len = cached_pmk_len;
+ os_memcpy(pmk, cached_pmk, cached_pmk_len);
+ } else {
+ switch (pasn->akmp) {
+#ifdef CONFIG_SAE
+ case WPA_KEY_MGMT_SAE:
+ if (pasn->sae.state == SAE_COMMITTED) {
+ pmk_len = PMK_LEN;
+ os_memcpy(pmk, pasn->sae.pmk, PMK_LEN);
+ break;
+ }
+#endif /* CONFIG_SAE */
+ /* fall through */
+ default:
+ /* TODO: Derive PMK based on wrapped data */
+ wpa_printf(MSG_DEBUG,
+ "PASN: Missing PMK derivation");
+ return -1;
+ }
+ }
+
+ ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr,
+ wpabuf_head(secret), wpabuf_len(secret),
+ &pasn->ptk, pasn->akmp,
+ pasn->cipher, pasn->kdk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
+ return -1;
+ }
+
+ if (pasn->secure_ltf) {
+ ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp,
+ pasn->cipher);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive LTF keyseed");
+ return -1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
+ return 0;
+}
+
+
+static void handle_auth_pasn_comeback(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ u16 group)
+{
+ struct wpabuf *buf, *comeback;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: Building comeback frame 2. Comeback after=%u",
+ pasn->comeback_after);
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ return;
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2,
+ WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY);
+
+ /*
+ * Do not include the group as a part of the token since it is not going
+ * to be used.
+ */
+ comeback = auth_build_token_req(&pasn->last_comeback_key_update,
+ pasn->comeback_key, pasn->comeback_idx,
+ pasn->comeback_pending_idx,
+ sizeof(u16) * COMEBACK_PENDING_IDX_SIZE,
+ 0, peer_addr, 0);
+ if (!comeback) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed sending auth with comeback");
+ wpabuf_free(buf);
+ return;
+ }
+
+ wpa_pasn_add_parameter_ie(buf, group,
+ WPA_PASN_WRAPPED_DATA_NO,
+ NULL, 0, comeback,
+ pasn->comeback_after);
+ wpabuf_free(comeback);
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: comeback: STA=" MACSTR, MAC2STR(peer_addr));
+
+ ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
+ wpabuf_len(buf), 0, 0, 0);
+ if (ret)
+ wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2");
+
+ wpabuf_free(buf);
+}
+
+
+int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr,
+ struct rsn_pmksa_cache_entry *pmksa, u16 status)
+{
+ struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+ struct wpabuf *rsn_buf = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len;
+ u8 *ptr;
+ const u8 *frame, *data, *rsn_ie, *rsnxe_ie;
+ u8 *data_buf = NULL;
+ size_t frame_len, data_len;
+ int ret;
+ const u8 *pmkid = NULL;
+
+ wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status);
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2,
+ status);
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto done;
+
+ if (pmksa && pasn->custom_pmkid_valid)
+ pmkid = pasn->custom_pmkid;
+ else if (pmksa) {
+ pmkid = pmksa->pmkid;
+#ifdef CONFIG_SAE
+ } else if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID");
+ pmkid = pasn->sae.pmkid;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ } else if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID");
+ pmkid = pasn->fils.erp_pmkid;
+#endif /* CONFIG_FILS */
+ }
+
+ if (wpa_pasn_add_rsne(buf, pmkid,
+ pasn->akmp, pasn->cipher) < 0)
+ goto fail;
+
+ /* No need to derive PMK if PMKSA is given */
+ if (!pmksa)
+ wrapped_data_buf = pasn_get_wrapped_data(pasn);
+ else
+ pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO;
+
+ /* Get public key */
+ pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
+ pubkey = wpabuf_zeropad(pubkey,
+ crypto_ecdh_prime_len(pasn->ecdh));
+ if (!pubkey) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
+ goto fail;
+ }
+
+ wpa_pasn_add_parameter_ie(buf, pasn->group,
+ pasn->wrapped_data_format,
+ pubkey, true, NULL, 0);
+
+ if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+ goto fail;
+
+ wpabuf_free(wrapped_data_buf);
+ wrapped_data_buf = NULL;
+ wpabuf_free(pubkey);
+ pubkey = NULL;
+
+ /* Add RSNXE if needed */
+ rsnxe_ie = pasn->rsnxe_ie;
+ if (rsnxe_ie)
+ wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
+
+ wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
+ /* Add the mic */
+ mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+ wpabuf_put_u8(buf, WLAN_EID_MIC);
+ wpabuf_put_u8(buf, mic_len);
+ ptr = wpabuf_put(buf, mic_len);
+
+ os_memset(ptr, 0, mic_len);
+
+ frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
+ frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
+
+ if (pasn->rsn_ie && pasn->rsn_ie_len) {
+ rsn_ie = pasn->rsn_ie;
+ } else {
+ /*
+ * Note: when pasn->rsn_ie is NULL, it is likely that Beacon
+ * frame RSNE is not initialized. This is possible in case of
+ * PASN authentication used for Wi-Fi Aware for which Beacon
+ * frame RSNE and RSNXE are same as RSNE and RSNXE in the
+ * Authentication frame.
+ */
+ rsn_buf = wpabuf_alloc(500);
+ if (!rsn_buf)
+ goto fail;
+
+ if (wpa_pasn_add_rsne(rsn_buf, pmkid,
+ pasn->akmp, pasn->cipher) < 0)
+ goto fail;
+
+ rsn_ie = wpabuf_head_u8(rsn_buf);
+ }
+
+ /*
+ * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also
+ * MDE, etc. Thus, do not use the returned length but instead use the
+ * length specified in the IE header.
+ */
+ data_len = rsn_ie[1] + 2;
+ if (rsnxe_ie) {
+ data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2);
+ if (!data_buf)
+ goto fail;
+
+ os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2);
+ os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2);
+ data_len += rsnxe_ie[1] + 2;
+ data = data_buf;
+ } else {
+ data = rsn_ie;
+ }
+
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ own_addr, peer_addr, data, data_len,
+ frame, frame_len, mic);
+ os_free(data_buf);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation");
+ goto fail;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (pasn->corrupt_mic) {
+ wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC");
+ mic[0] = ~mic[0];
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(ptr, mic, mic_len);
+
+done:
+ wpa_printf(MSG_DEBUG,
+ "PASN: Building frame 2: success; resp STA=" MACSTR,
+ MAC2STR(peer_addr));
+
+ ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
+ wpabuf_len(buf), 0, 0, 0);
+ if (ret)
+ wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
+
+ wpabuf_free(rsn_buf);
+ wpabuf_free(buf);
+ return ret;
+fail:
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(pubkey);
+ wpabuf_free(rsn_buf);
+ wpabuf_free(buf);
+ return -1;
+}
+
+
+int handle_auth_pasn_1(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsn_data;
+ struct wpa_pasn_params_data pasn_params;
+ struct rsn_pmksa_cache_entry *pmksa = NULL;
+ const u8 *cached_pmk = NULL;
+ size_t cached_pmk_len = 0;
+ struct wpabuf *wrapped_data = NULL, *secret = NULL;
+ const int *groups = pasn->pasn_groups;
+ static const int default_groups[] = { 19, 0 };
+ u16 status = WLAN_STATUS_SUCCESS;
+ int ret, inc_y;
+ bool derive_keys;
+ u32 i;
+
+ if (!groups)
+ groups = default_groups;
+
+ if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+ len - offsetof(struct ieee80211_mgmt,
+ u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed parsing Authentication frame");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+
+ if (!elems.rsn_ie) {
+ wpa_printf(MSG_DEBUG, "PASN: No RSNE");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto send_resp;
+ }
+
+ ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto send_resp;
+ }
+
+ ret = wpa_pasn_validate_rsne(&rsn_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto send_resp;
+ }
+
+ if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
+ !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto send_resp;
+ }
+
+ pasn->akmp = rsn_data.key_mgmt;
+ pasn->cipher = rsn_data.pairwise_cipher;
+
+ if (pasn->derive_kdk &&
+ ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_SECURE_LTF))
+ pasn->secure_ltf = true;
+
+ if (pasn->derive_kdk)
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+ else
+ pasn->kdk_len = 0;
+
+ wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+ if (!elems.pasn_params || !elems.pasn_params_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: No PASN Parameters element found");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto send_resp;
+ }
+
+ ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+ elems.pasn_params_len + 3,
+ false, &pasn_params);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed validation of PASN Parameters IE");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto send_resp;
+ }
+
+ for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++)
+ ;
+
+ if (!pasn_params.group || groups[i] != pasn_params.group) {
+ wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed",
+ pasn_params.group);
+ status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ goto send_resp;
+ }
+
+ if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
+ wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto send_resp;
+ }
+
+ if (pasn_params.comeback) {
+ wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token");
+
+ ret = check_comeback_token(pasn->comeback_key,
+ pasn->comeback_pending_idx,
+ peer_addr,
+ pasn_params.comeback,
+ pasn_params.comeback_len);
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto send_resp;
+ }
+ } else if (pasn->use_anti_clogging) {
+ wpa_printf(MSG_DEBUG, "PASN: Respond with comeback");
+ handle_auth_pasn_comeback(pasn, own_addr, peer_addr,
+ pasn_params.group);
+ return -1;
+ }
+
+ pasn->ecdh = crypto_ecdh_init(pasn_params.group);
+ if (!pasn->ecdh) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+
+ pasn->group = pasn_params.group;
+
+ if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
+ inc_y = 1;
+ } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
+ pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
+ inc_y = 0;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid first octet in pubkey=0x%x",
+ pasn_params.pubkey[0]);
+ status = WLAN_STATUS_INVALID_PUBLIC_KEY;
+ goto send_resp;
+ }
+
+ secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
+ pasn_params.pubkey + 1,
+ pasn_params.pubkey_len - 1);
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+
+ derive_keys = true;
+ if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+ wrapped_data = ieee802_11_defrag(&elems,
+ WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+ if (!wrapped_data) {
+ wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+
+#ifdef CONFIG_SAE
+ if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ ret = pasn_wd_handle_sae_commit(pasn, own_addr,
+ peer_addr,
+ wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing SAE commit");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+ }
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ if (!pasn->fils_wd_valid) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid FILS wrapped data");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Pending AS response");
+
+ /*
+ * With PASN/FILS, keys can be derived only after a
+ * response from the AS is processed.
+ */
+ derive_keys = false;
+ }
+#endif /* CONFIG_FILS */
+ }
+
+ pasn->wrapped_data_format = pasn_params.wrapped_data_format;
+
+ ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+ ((const u8 *) mgmt) + IEEE80211_HDRLEN,
+ len - IEEE80211_HDRLEN, pasn->hash);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+
+ if (!derive_keys) {
+ wpa_printf(MSG_DEBUG, "PASN: Storing secret");
+ pasn->secret = secret;
+ wpabuf_free(wrapped_data);
+ return 0;
+ }
+
+ if (rsn_data.num_pmkid) {
+ if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1");
+
+ if (!pasn->pmk_r1_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FT: Failed getting PMK-R1");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+ cached_pmk = pasn->pmk_r1;
+ cached_pmk_len = pasn->pmk_r1_len;
+#else /* CONFIG_IEEE80211R_AP */
+ wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+#endif /* CONFIG_IEEE80211R_AP */
+ } else {
+ wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry");
+
+ if (pasn->pmksa) {
+ const u8 *pmkid = NULL;
+
+ if (pasn->custom_pmkid_valid) {
+ ret = pasn->validate_custom_pmkid(
+ pasn->cb_ctx, peer_addr,
+ rsn_data.pmkid);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed custom PMKID validation");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+ } else {
+ pmkid = rsn_data.pmkid;
+ }
+
+ pmksa = pmksa_cache_auth_get(pasn->pmksa,
+ peer_addr,
+ pmkid);
+ if (pmksa) {
+ cached_pmk = pmksa->pmk;
+ cached_pmk_len = pmksa->pmk_len;
+ }
+ }
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "PASN: No PMKID specified");
+ }
+
+ ret = pasn_derive_keys(pasn, own_addr, peer_addr,
+ cached_pmk, cached_pmk_len,
+ &pasn_params, wrapped_data, secret);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys");
+ status = WLAN_STATUS_PASN_BASE_AKMP_FAILED;
+ goto send_resp;
+ }
+
+ ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+ ((const u8 *) mgmt) + IEEE80211_HDRLEN,
+ len - IEEE80211_HDRLEN, pasn->hash);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+send_resp:
+ ret = handle_auth_pasn_resp(pasn, own_addr, peer_addr, pmksa, status);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to send response");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Success handling transaction == 1");
+ }
+
+ wpabuf_free(secret);
+ wpabuf_free(wrapped_data);
+
+ if (status != WLAN_STATUS_SUCCESS)
+ return -1;
+
+ return 0;
+}
+
+
+int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct ieee802_11_elems elems;
+ struct wpa_pasn_params_data pasn_params;
+ struct wpabuf *wrapped_data = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len;
+ int ret;
+ u8 *copy = NULL;
+ size_t copy_len, mic_offset;
+
+ if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+ len - offsetof(struct ieee80211_mgmt,
+ u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed parsing Authentication frame");
+ goto fail;
+ }
+
+ /* Check that the MIC IE exists. Save it and zero out the memory. */
+ mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+ if (!elems.mic || elems.mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid MIC. Expecting len=%u", mic_len);
+ goto fail;
+ }
+ os_memcpy(mic, elems.mic, mic_len);
+
+ if (!elems.pasn_params || !elems.pasn_params_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: No PASN Parameters element found");
+ goto fail;
+ }
+
+ ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+ elems.pasn_params_len + 3,
+ false, &pasn_params);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed validation of PASN Parameters IE");
+ goto fail;
+ }
+
+ if (pasn_params.pubkey || pasn_params.pubkey_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Public key should not be included");
+ goto fail;
+ }
+
+ /* Verify the MIC */
+ copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+ mic_offset = elems.mic - (const u8 *) &mgmt->u.auth;
+ copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+ if (mic_offset + mic_len > copy_len)
+ goto fail;
+ copy = os_memdup(&mgmt->u.auth, copy_len);
+ if (!copy)
+ goto fail;
+ os_memset(copy + mic_offset, 0, mic_len);
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ peer_addr, own_addr,
+ pasn->hash, mic_len * 2,
+ copy, copy_len, out_mic);
+ os_free(copy);
+ copy = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
+ if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
+ goto fail;
+ }
+
+ if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+ wrapped_data = ieee802_11_defrag(&elems,
+ WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+
+ if (!wrapped_data) {
+ wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+ goto fail;
+ }
+
+#ifdef CONFIG_SAE
+ if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ ret = pasn_wd_handle_sae_confirm(pasn, peer_addr,
+ wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing SAE confirm");
+ wpabuf_free(wrapped_data);
+ goto fail;
+ }
+ }
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ if (wrapped_data) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Ignore wrapped data");
+ }
+ }
+#endif /* CONFIG_FILS */
+ wpabuf_free(wrapped_data);
+ }
+
+ wpa_printf(MSG_INFO,
+ "PASN: Success handling transaction == 3. Store PTK");
+ return 0;
+
+fail:
+ os_free(copy);
+ return -1;
+}
+
+#endif /* CONFIG_PASN */
diff --git a/src/radius/radius.c b/src/radius/radius.c
index a642280..be59a94 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -176,6 +176,7 @@
{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP },
+ { RADIUS_ATTR_FILTER_ID, "Filter-Id", RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 177c64a..571c159 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -62,6 +62,7 @@
RADIUS_ATTR_NAS_PORT = 5,
RADIUS_ATTR_SERVICE_TYPE = 6,
RADIUS_ATTR_FRAMED_IP_ADDRESS = 8,
+ RADIUS_ATTR_FILTER_ID = 11,
RADIUS_ATTR_FRAMED_MTU = 12,
RADIUS_ATTR_REPLY_MESSAGE = 18,
RADIUS_ATTR_STATE = 24,
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 93cc9a7..e7b4d54 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -30,6 +30,7 @@
enum pmksa_free_reason reason);
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx);
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
void *ctx;
};
@@ -47,16 +48,46 @@
struct rsn_pmksa_cache_entry *entry,
enum pmksa_free_reason reason)
{
- wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
- entry->pmkid,
- entry->fils_cache_id_set ? entry->fils_cache_id :
- NULL);
+ if (pmksa->sm)
+ 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);
+ if (pmksa->free_cb)
+ pmksa->free_cb(entry, pmksa->ctx, reason);
_pmksa_cache_free_entry(entry);
}
+void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *e;
+
+ e = pmksa->pmksa;
+ while (e) {
+ if (e == entry) {
+ pmksa->pmksa = entry->next;
+ break;
+ }
+ if (e->next == entry) {
+ e->next = entry->next;
+ break;
+ }
+ }
+
+ if (!e) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Could not remove PMKSA cache entry %p since it is not in the list",
+ entry);
+ return;
+ }
+
+ pmksa_cache_free_entry(pmksa, entry, PMKSA_FREE);
+}
+
+
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
@@ -66,7 +97,7 @@
os_get_reltime(&now);
while (entry && entry->expiration <= now.sec) {
- if (wpa_key_mgmt_sae(entry->akmp) &&
+ if (wpa_key_mgmt_sae(entry->akmp) && pmksa->is_current_cb &&
pmksa->is_current_cb(entry, pmksa->ctx)) {
/* Do not expire the currently used PMKSA entry for SAE
* since there is no convenient mechanism for
@@ -99,6 +130,10 @@
static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
+
+ if (!pmksa->sm)
+ return;
+
pmksa->sm->cur_pmksa = NULL;
eapol_sm_request_reauth(pmksa->sm->eapol);
}
@@ -119,6 +154,7 @@
if (sec < 0) {
sec = 0;
if (wpa_key_mgmt_sae(pmksa->pmksa->akmp) &&
+ pmksa->is_current_cb &&
pmksa->is_current_cb(pmksa->pmksa, pmksa->ctx)) {
/* Do not continue polling for the current PMKSA entry
* from SAE to expire every second. Use the expiration
@@ -139,8 +175,11 @@
}
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+ if (!pmksa->sm)
+ return;
+
entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
- pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0);
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, NULL, 0);
if (entry && !wpa_key_mgmt_sae(entry->akmp)) {
sec = pmksa->pmksa->reauth_time - now.sec;
if (sec < 0)
@@ -179,6 +218,8 @@
{
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
+ unsigned int pmk_lifetime = 43200;
+ unsigned int pmk_reauth_threshold = 70;
if (pmk_len > PMK_LEN_MAX)
return NULL;
@@ -200,15 +241,21 @@
else
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;
+ if (pmksa->sm) {
+ pmk_lifetime = pmksa->sm->dot11RSNAConfigPMKLifetime;
+ pmk_reauth_threshold =
+ pmksa->sm->dot11RSNAConfigPMKReauthThreshold;
+ }
+ entry->expiration = now.sec + pmk_lifetime;
+ entry->reauth_time = now.sec +
+ pmk_lifetime * pmk_reauth_threshold / 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);
+ os_memcpy(entry->spa, spa, ETH_ALEN);
entry->network_ctx = network_ctx;
return pmksa_cache_add_entry(pmksa, entry);
@@ -226,7 +273,8 @@
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
- if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) {
+ if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0 &&
+ os_memcmp(entry->spa, pos->spa, ETH_ALEN) == 0) {
if (pos->pmk_len == entry->pmk_len &&
os_memcmp_const(pos->pmk, entry->pmk,
entry->pmk_len) == 0 &&
@@ -269,7 +317,7 @@
/* Remove the oldest entry to make room for the new entry */
pos = pmksa->pmksa;
- if (pos == pmksa->sm->cur_pmksa) {
+ if (pmksa->sm && pos == pmksa->sm->cur_pmksa) {
/*
* Never remove the current PMKSA cache entry, since
* it's in use, and removing it triggers a needless
@@ -308,9 +356,16 @@
}
pmksa->pmksa_count++;
wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
- " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa),
+ " spa=" MACSTR " network_ctx=%p akmp=0x%x",
+ MAC2STR(entry->aa), MAC2STR(entry->spa),
entry->network_ctx, entry->akmp);
- wpas_notify_pmk_cache_added((struct wpa_supplicant *)pmksa->sm->ctx->ctx, entry);
+
+ if (!pmksa->sm)
+ return entry;
+
+ if (pmksa->notify_cb)
+ pmksa->notify_cb(entry, pmksa->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,
@@ -398,13 +453,15 @@
* 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 u8 *aa, const u8 *spa,
+ const u8 *pmkid,
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) &&
+ (!spa || os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
(pmkid == NULL ||
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
(!akmp || akmp == entry->akmp) &&
@@ -426,6 +483,9 @@
os_time_t old_reauth_time = old_entry->reauth_time;
const u8 *pmkid = NULL;
+ if (!pmksa->sm)
+ return NULL;
+
if (wpa_key_mgmt_sae(old_entry->akmp) ||
wpa_key_mgmt_fils(old_entry->akmp))
pmkid = old_entry->pmkid;
@@ -577,11 +637,11 @@
sm->cur_pmksa = NULL;
if (pmkid)
- sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
- network_ctx, akmp);
+ sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, sm->own_addr,
+ pmkid, network_ctx, akmp);
if (sm->cur_pmksa == NULL && bssid)
- sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
- network_ctx, akmp);
+ sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, sm->own_addr,
+ NULL, network_ctx, akmp);
if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
network_ctx,
@@ -700,6 +760,8 @@
void *ctx, enum pmksa_free_reason reason),
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx),
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm)
{
struct rsn_pmksa_cache *pmksa;
@@ -708,6 +770,7 @@
if (pmksa) {
pmksa->free_cb = free_cb;
pmksa->is_current_cb = is_current_cb;
+ pmksa->notify_cb = notify_cb;
pmksa->ctx = ctx;
pmksa->sm = sm;
}
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index b801268..48c9e04 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -20,6 +20,7 @@
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
u8 aa[ETH_ALEN];
+ u8 spa[ETH_ALEN];
/*
* If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA
@@ -61,10 +62,13 @@
void *ctx, enum pmksa_free_reason reason),
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx),
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm);
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 u8 *aa, const u8 *spa,
+ const u8 *pmkid,
const void *network_ctx,
int akmp);
int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
@@ -88,6 +92,8 @@
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, bool external_only);
+void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa);
#else /* IEEE8021X_EAPOL */
@@ -97,6 +103,8 @@
void *ctx, enum pmksa_free_reason reason),
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx),
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm)
{
return (void *) -1;
@@ -107,8 +115,8 @@
}
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, int akmp)
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+ const u8 *pmkid, const void *network_ctx, int akmp)
{
return NULL;
}
@@ -168,6 +176,11 @@
{
}
+static inline void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+}
+
static inline void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
{
}
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index a96655f..8f86820 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -330,7 +330,8 @@
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, 0);
+ p = pmksa_cache_get(sm->pmksa, candidate->bssid, sm->own_addr,
+ 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 "
@@ -491,7 +492,7 @@
if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
return;
- pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL, 0);
+ pmksa = pmksa_cache_get(sm->pmksa, bssid, sm->own_addr, 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 c26a63d..1531f51 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -180,7 +180,7 @@
static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
- if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
+ if (wpa_sm_set_key(sm, -1, WPA_ALG_NONE, peer->addr,
0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE) < 0) {
wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
"the driver");
@@ -230,7 +230,7 @@
wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR,
MAC2STR(peer->addr));
- if (wpa_sm_set_key(sm, alg, peer->addr, 0, 1, rsc, sizeof(rsc),
+ if (wpa_sm_set_key(sm, -1, alg, peer->addr, 0, 1, rsc, sizeof(rsc),
peer->tpk.tk, key_len,
KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
@@ -465,23 +465,26 @@
* wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
* @kck: TPK-KCK
* @lnkid: Pointer to the beginning of Link Identifier IE
- * @rsnie: Pointer to the beginning of RSN IE used for handshake
+ * @rsne: Pointer to the beginning of RSNE used for handshake
+ * @rsne_len: Length of RSNE in octets
* @timeoutie: Pointer to the beginning of Timeout IE used for handshake
- * @ftie: Pointer to the beginning of FT IE
+ * @fte: Pointer to the beginning of FTE
+ * @fre_len: Length of FTE in octets
* @mic: Pointer for writing MIC
*
* Calculate MIC for TDLS frame.
*/
static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
- const u8 *rsnie, const u8 *timeoutie,
- const u8 *ftie, u8 *mic)
+ const u8 *rsne, size_t rsne_len,
+ const u8 *timeoutie,
+ const u8 *fte, size_t fte_len, u8 *mic)
{
u8 *buf, *pos;
struct wpa_tdls_ftie *_ftie;
const struct wpa_tdls_lnkid *_lnkid;
int ret;
- int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
- 2 + timeoutie[1] + 2 + ftie[1];
+ int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + rsne_len +
+ 2 + timeoutie[1] + fte_len;
buf = os_zalloc(len);
if (!buf) {
wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
@@ -502,16 +505,16 @@
os_memcpy(pos, lnkid, 2 + lnkid[1]);
pos += 2 + lnkid[1];
/* 5) RSN IE */
- os_memcpy(pos, rsnie, 2 + rsnie[1]);
- pos += 2 + rsnie[1];
+ os_memcpy(pos, rsne, rsne_len);
+ pos += rsne_len;
/* 6) Timeout Interval IE */
os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
pos += 2 + timeoutie[1];
/* 7) FTIE, with the MIC field of the FTIE set to 0 */
- os_memcpy(pos, ftie, 2 + ftie[1]);
+ os_memcpy(pos, fte, fte_len);
_ftie = (struct wpa_tdls_ftie *) pos;
os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
- pos += 2 + ftie[1];
+ pos += fte_len;
wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
@@ -529,14 +532,15 @@
* @rcode: Reason code for Teardown
* @dtoken: Dialog Token used for that particular link
* @lnkid: Pointer to the beginning of Link Identifier IE
- * @ftie: Pointer to the beginning of FT IE
+ * @fte: Pointer to the beginning of FTE
+ * @fre_len: Length of FTE in octets
* @mic: Pointer for writing MIC
*
* Calculate MIC for TDLS frame.
*/
static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
u8 dtoken, const u8 *lnkid,
- const u8 *ftie, u8 *mic)
+ const u8 *fte, size_t fte_len, u8 *mic)
{
u8 *buf, *pos;
struct wpa_tdls_ftie *_ftie;
@@ -547,7 +551,7 @@
return -1;
len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
- sizeof(trans_seq) + 2 + ftie[1];
+ sizeof(trans_seq) + fte_len;
buf = os_zalloc(len);
if (!buf) {
@@ -567,10 +571,10 @@
/* 4) Transaction Sequence number */
*pos++ = trans_seq;
/* 7) FTIE, with the MIC field of the FTIE set to 0 */
- os_memcpy(pos, ftie, 2 + ftie[1]);
+ os_memcpy(pos, fte, fte_len);
_ftie = (struct wpa_tdls_ftie *) pos;
os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
- pos += 2 + ftie[1];
+ pos += fte_len;
wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
@@ -584,14 +588,15 @@
static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
struct wpa_tdls_peer *peer,
const u8 *lnkid, const u8 *timeoutie,
- const struct wpa_tdls_ftie *ftie)
+ const struct wpa_tdls_ftie *ftie,
+ size_t fte_len)
{
u8 mic[16];
if (peer->tpk_set) {
wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
- peer->rsnie_p, timeoutie, (u8 *) ftie,
- mic);
+ peer->rsnie_p, peer->rsnie_p_len, timeoutie,
+ (const u8 *) ftie, fte_len, mic);
if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
"dropping packet");
@@ -612,13 +617,14 @@
static int wpa_supplicant_verify_tdls_mic_teardown(
u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
- const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
+ const u8 *lnkid, const struct wpa_tdls_ftie *ftie, size_t fte_len)
{
u8 mic[16];
if (peer->tpk_set) {
wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
- dtoken, lnkid, (u8 *) ftie, mic);
+ dtoken, lnkid, (const u8 *) ftie,
+ fte_len, mic);
if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
"dropping packet");
@@ -830,7 +836,8 @@
/* compute MIC before sending */
wpa_tdls_linkid(sm, peer, &lnkid);
wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
- dialog_token, (u8 *) &lnkid, (u8 *) ftie,
+ dialog_token, (const u8 *) &lnkid,
+ (const u8 *) ftie, 2 + ftie->ie_len,
ftie->mic);
skip_ies:
@@ -1000,7 +1007,8 @@
/* Process MIC check to see if TDLS Teardown is right */
if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
peer->dtoken, peer,
- (u8 *) lnkid, ftie) < 0) {
+ (const u8 *) lnkid,
+ ftie, kde.ftie_len) < 0) {
wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
"Teardown Request from " MACSTR, MAC2STR(src_addr));
return -1;
@@ -1320,8 +1328,9 @@
lifetime);
/* compute MIC before sending */
- wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
- (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+ wpa_tdls_ftie_mic(peer->tpk.kck, 2, (const u8 *) lnkid, peer->rsnie_p,
+ peer->rsnie_p_len, (const u8 *) &timeoutie,
+ (const u8 *) ftie, 2 + ftie->ie_len, ftie->mic);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
@@ -1410,8 +1419,9 @@
lifetime);
/* compute MIC before sending */
- wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
- (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+ wpa_tdls_ftie_mic(peer->tpk.kck, 3, (const u8 *) lnkid, peer->rsnie_p,
+ peer->rsnie_p_len, (const u8 *) &timeoutie,
+ (const u8 *) ftie, 2 + ftie->ie_len, ftie->mic);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
@@ -2483,8 +2493,9 @@
wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
/* Process MIC check to see if TPK M2 is right */
- if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
- (u8 *) timeoutie, ftie) < 0) {
+ if (wpa_supplicant_verify_tdls_mic(2, peer, (const u8 *) lnkid,
+ (const u8 *) timeoutie, ftie,
+ kde.ftie_len) < 0) {
/* Discard the frame */
wpa_tdls_del_key(sm, peer);
wpa_tdls_disable_peer_link(sm, peer);
@@ -2657,8 +2668,9 @@
goto error;
}
- if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
- (u8 *) timeoutie, ftie) < 0) {
+ if (wpa_supplicant_verify_tdls_mic(3, peer, (const u8 *) lnkid,
+ (const u8 *) timeoutie, ftie,
+ kde.ftie_len) < 0) {
wpa_tdls_del_key(sm, peer);
goto error;
}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 21f8b5b..3a39886 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -38,6 +38,45 @@
static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static void _wpa_hexdump_link(int level, u8 link_id, const char *title,
+ const void *buf, size_t len, bool key)
+{
+ char *link_title = NULL;
+
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ goto out;
+
+ link_title = os_malloc(os_strlen(title) + 20);
+ if (!link_title)
+ goto out;
+
+ os_snprintf(link_title, os_strlen(title) + 20, "MLO link[%u]: %s",
+ link_id, title);
+
+out:
+ if (key)
+ wpa_hexdump_key(level, link_title ? link_title : title, buf,
+ len);
+ else
+ wpa_hexdump(level, link_title ? link_title : title, buf, len);
+ os_free(link_title);
+}
+
+
+static void wpa_hexdump_link(int level, u8 link_id, const char *title,
+ const void *buf, size_t len)
+{
+ _wpa_hexdump_link(level, link_id, title, buf, len, false);
+}
+
+
+static void wpa_hexdump_link_key(int level, u8 link_id, const char *title,
+ const void *buf, size_t len)
+{
+ _wpa_hexdump_link(level, link_id, title, buf, len, true);
+}
+
+
/**
* wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -280,7 +319,8 @@
* not have enough time to get the association information
* 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,
+ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr,
+ sm->own_addr, pmkid,
NULL, 0);
if (sm->cur_pmksa) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -396,8 +436,8 @@
fils_cache_id);
}
if (!sm->cur_pmksa && pmkid &&
- pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL,
- 0)) {
+ pmksa_cache_get(sm->pmksa, src_addr, sm->own_addr,
+ pmkid, NULL, 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: the new PMK matches with the "
"PMKID");
@@ -621,7 +661,7 @@
kdk_len = 0;
ret = wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
- sm->own_addr, sm->bssid, sm->snonce,
+ sm->own_addr, wpa_sm_get_auth_addr(sm), sm->snonce,
key->key_nonce, ptk, akmp,
sm->pairwise_cipher, z, z_len,
kdk_len);
@@ -685,6 +725,139 @@
}
+static u8 * rsn_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 size_t wpa_mlo_link_kde_len(struct wpa_sm *sm)
+{
+ int i;
+ unsigned int num_links = 0;
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (sm->mlo.assoc_link_id != i && (sm->mlo.req_links & BIT(i)))
+ num_links++;
+ }
+
+ return num_links * (RSN_SELECTOR_LEN + 1 + ETH_ALEN + 2);
+}
+
+
+static u8 * wpa_mlo_link_kde(struct wpa_sm *sm, u8 *pos)
+{
+ int i;
+ u8 hdr[1 + ETH_ALEN];
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (sm->mlo.assoc_link_id == i || !(sm->mlo.req_links & BIT(i)))
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "MLO: Add MLO Link %d KDE in EAPOL-Key 2/4", i);
+ hdr[0] = i & 0xF; /* LinkID; no RSNE or RSNXE */
+ os_memcpy(&hdr[1], sm->mlo.links[i].addr, ETH_ALEN);
+ pos = rsn_add_kde(pos, RSN_KEY_DATA_MLO_LINK, hdr, sizeof(hdr));
+ }
+
+ return pos;
+}
+
+
+static bool is_valid_ap_mld_mac_kde(struct wpa_sm *sm, const u8 *mac_kde)
+{
+ return mac_kde &&
+ os_memcmp(mac_kde, sm->mlo.ap_mld_addr, ETH_ALEN) == 0;
+}
+
+
+static void wpas_swap_tkip_mic_keys(struct wpa_ptk *ptk)
+{
+ u8 buf[8];
+
+ /* Supplicant: swap tx/rx Mic keys */
+ os_memcpy(buf, &ptk->tk[16], 8);
+ os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
+ os_memcpy(&ptk->tk[24], buf, 8);
+ forced_memzero(buf, sizeof(buf));
+}
+
+
+static void wpa_supplicant_process_1_of_4_wpa(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len,
+ enum frame_encryption encrypted)
+{
+ struct wpa_eapol_ie_parse ie;
+ struct wpa_ptk *ptk;
+ int res;
+
+ if (wpa_sm_get_network_ctx(sm) == NULL) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: No SSID info found (msg 1 of 4)");
+ return;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: RX message 1 of 4-Way Handshake from " MACSTR
+ " (ver=%d)", MAC2STR(src_addr), ver);
+
+ os_memset(&ie, 0, sizeof(ie));
+
+ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
+ if (res == -2) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Do not reply to msg 1/4 - requesting full EAP authentication");
+ return;
+ }
+ if (res)
+ goto failed;
+
+ wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+
+ if (sm->renew_snonce) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to get random data for SNonce");
+ goto failed;
+ }
+ sm->renew_snonce = 0;
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+ sm->snonce, WPA_NONCE_LEN);
+ }
+
+ /* Calculate PTK which will be stored as a temporary PTK until it has
+ * been verified when processing message 3/4. */
+ ptk = &sm->tptk;
+ if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
+ goto failed;
+ if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+ wpas_swap_tkip_mic_keys(ptk);
+ sm->tptk_set = 1;
+
+ if (wpa_supplicant_send_2_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+ sm->snonce, sm->assoc_wpa_ie,
+ sm->assoc_wpa_ie_len, ptk) < 0)
+ goto failed;
+
+ os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
+ return;
+
+failed:
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
const unsigned char *src_addr,
const struct wpa_eapol_key *key,
@@ -697,6 +870,7 @@
int res;
u8 *kde, *kde_buf = NULL;
size_t kde_len;
+ size_t mlo_kde_len = 0;
if (encrypted == FRAME_NOT_ENCRYPTED && sm->tk_set &&
wpa_sm_pmf_enabled(sm)) {
@@ -724,19 +898,22 @@
os_memset(&ie, 0, sizeof(ie));
- if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
- /* RSN: msg 1/4 should contain PMKID for the selected PMK */
- 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) {
- wpa_printf(MSG_DEBUG,
- "RSN: Discard EAPOL-Key msg 1/4 with invalid IEs/KDEs");
- return;
- }
- if (ie.pmkid) {
- wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
- "Authenticator", ie.pmkid, PMKID_LEN);
- }
+ /* RSN: msg 1/4 should contain PMKID for the selected PMK */
+ 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) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Discard EAPOL-Key msg 1/4 with invalid IEs/KDEs");
+ return;
+ }
+ if (ie.pmkid) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID from Authenticator",
+ ie.pmkid, PMKID_LEN);
+ }
+
+ if (sm->mlo.valid_links && !is_valid_ap_mld_mac_kde(sm, ie.mac_addr)) {
+ wpa_printf(MSG_INFO,
+ "RSN: Discard EAPOL-Key msg 1/4 with invalid AP MLD MAC address KDE");
+ return;
}
res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
@@ -766,23 +943,23 @@
ptk = &sm->tptk;
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 */
- os_memcpy(buf, &ptk->tk[16], 8);
- os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
- os_memcpy(&ptk->tk[24], buf, 8);
- forced_memzero(buf, sizeof(buf));
- }
+ if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+ wpas_swap_tkip_mic_keys(ptk);
sm->tptk_set = 1;
+ /* Add MLO Link KDE and MAC KDE in M2 for ML connection */
+ if (sm->mlo.valid_links)
+ mlo_kde_len = wpa_mlo_link_kde_len(sm) +
+ RSN_SELECTOR_LEN + ETH_ALEN + 2;
+
kde = sm->assoc_wpa_ie;
kde_len = sm->assoc_wpa_ie_len;
kde_buf = os_malloc(kde_len +
2 + RSN_SELECTOR_LEN + 3 +
sm->assoc_rsnxe_len +
2 + RSN_SELECTOR_LEN + 1 +
- 2 + RSN_SELECTOR_LEN + 2);
+ 2 + RSN_SELECTOR_LEN + 2 + mlo_kde_len);
+
if (!kde_buf)
goto failed;
os_memcpy(kde_buf, kde, kde_len);
@@ -856,8 +1033,23 @@
}
#endif /* CONFIG_DPP2 */
- if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
- kde, kde_len, ptk) < 0)
+ if (sm->mlo.valid_links) {
+ u8 *pos;
+
+ /* Add MAC KDE */
+ wpa_printf(MSG_DEBUG, "MLO: Add MAC KDE into EAPOL-Key 2/4");
+ pos = kde + kde_len;
+ pos = rsn_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->own_addr,
+ ETH_ALEN);
+
+ /* Add MLO Link KDE */
+ wpa_printf(MSG_DEBUG, "Add MLO Link KDE(s) into EAPOL-Key 2/4");
+ pos = wpa_mlo_link_kde(sm, pos);
+ kde_len = pos - kde;
+ }
+
+ if (wpa_supplicant_send_2_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+ sm->snonce, kde, kde_len, ptk) < 0)
goto failed;
os_free(kde_buf);
@@ -979,13 +1171,13 @@
wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
}
- if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, key_rsc,
- rsclen, sm->ptk.tk, keylen,
- KEY_FLAG_PAIRWISE | key_flag) < 0) {
+ if (wpa_sm_set_key(sm, -1, alg, wpa_sm_get_auth_addr(sm),
+ sm->keyidx_active, 1, key_rsc, rsclen, sm->ptk.tk,
+ keylen, KEY_FLAG_PAIRWISE | key_flag) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+ "WPA: Failed to set PTK to the driver (alg=%d keylen=%d auth_addr="
MACSTR " idx=%d key_flag=0x%x)",
- alg, keylen, MAC2STR(sm->bssid),
+ alg, keylen, MAC2STR(wpa_sm_get_auth_addr(sm)),
sm->keyidx_active, key_flag);
return -1;
}
@@ -1025,14 +1217,16 @@
static int wpa_supplicant_activate_ptk(struct wpa_sm *sm)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
- "WPA: Activate PTK (idx=%d bssid=" MACSTR ")",
- sm->keyidx_active, MAC2STR(sm->bssid));
+ "WPA: Activate PTK (idx=%d auth_addr=" MACSTR ")",
+ sm->keyidx_active, MAC2STR(wpa_sm_get_auth_addr(sm)));
- if (wpa_sm_set_key(sm, 0, sm->bssid, sm->keyidx_active, 0, NULL, 0,
- NULL, 0, KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) {
+ if (wpa_sm_set_key(sm, -1, 0, wpa_sm_get_auth_addr(sm),
+ sm->keyidx_active, 0, NULL, 0, NULL, 0,
+ KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Failed to activate PTK for TX (idx=%d bssid="
- MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid));
+ "WPA: Failed to activate PTK for TX (idx=%d auth_addr="
+ MACSTR ")", sm->keyidx_active,
+ MAC2STR(wpa_sm_get_auth_addr(sm)));
return -1;
}
return 0;
@@ -1107,7 +1301,7 @@
_gtk = gtk_buf;
}
if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
- if (wpa_sm_set_key(sm, gd->alg, NULL,
+ if (wpa_sm_set_key(sm, -1, gd->alg, NULL,
gd->keyidx, 1, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len,
KEY_FLAG_GROUP_RX_TX_DEFAULT) < 0) {
@@ -1117,7 +1311,7 @@
forced_memzero(gtk_buf, sizeof(gtk_buf));
return -1;
}
- } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
+ } else if (wpa_sm_set_key(sm, -1, gd->alg, broadcast_ether_addr,
gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1142,6 +1336,56 @@
}
+static int wpa_supplicant_install_mlo_gtk(struct wpa_sm *sm, u8 link_id,
+ const struct wpa_gtk_data *gd,
+ const u8 *key_rsc, int wnm_sleep)
+{
+ const u8 *gtk = gd->gtk;
+
+ /* Detect possible key reinstallation */
+ if ((sm->mlo.links[link_id].gtk.gtk_len == (size_t) gd->gtk_len &&
+ os_memcmp(sm->mlo.links[link_id].gtk.gtk, gd->gtk,
+ sm->mlo.links[link_id].gtk.gtk_len) == 0) ||
+ (sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len ==
+ (size_t) gd->gtk_len &&
+ os_memcmp(sm->mlo.links[link_id].gtk_wnm_sleep.gtk, gd->gtk,
+ sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len) == 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Not reinstalling already in-use GTK to the driver (link_id=%d keyidx=%d tx=%d len=%d)",
+ link_id, gd->keyidx, gd->tx, gd->gtk_len);
+ return 0;
+ }
+
+ wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: Group Key", gd->gtk,
+ gd->gtk_len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Installing GTK to the driver (link_id=%d keyidx=%d tx=%d len=%d)",
+ link_id, gd->keyidx, gd->tx, gd->gtk_len);
+ wpa_hexdump_link(MSG_DEBUG, link_id, "RSN: RSC",
+ key_rsc, gd->key_rsc_len);
+ if (wpa_sm_set_key(sm, link_id, gd->alg, broadcast_ether_addr,
+ gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, gtk,
+ gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Failed to set GTK to the driver (link_id=%d alg=%d keylen=%d keyidx=%d)",
+ link_id, gd->alg, gd->gtk_len, gd->keyidx);
+ return -1;
+ }
+
+ if (wnm_sleep) {
+ sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len = gd->gtk_len;
+ os_memcpy(sm->mlo.links[link_id].gtk_wnm_sleep.gtk, gd->gtk,
+ sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len);
+ } else {
+ sm->mlo.links[link_id].gtk.gtk_len = gd->gtk_len;
+ os_memcpy(sm->mlo.links[link_id].gtk.gtk, gd->gtk,
+ sm->mlo.links[link_id].gtk.gtk_len);
+ }
+
+ return 0;
+}
+
+
static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
int tx)
{
@@ -1190,6 +1434,84 @@
}
+static int wpa_supplicant_mlo_gtk(struct wpa_sm *sm, u8 link_id, const u8 *gtk,
+ size_t gtk_len, int key_info)
+{
+ struct wpa_gtk_data gd;
+ const u8 *key_rsc;
+ int ret;
+
+ /*
+ * MLO GTK KDE format:
+ * KeyID[bits 0-1], Tx [bit 2], Reserved [bit 3], link id [4-7]
+ * PN
+ * GTK
+ */
+ os_memset(&gd, 0, sizeof(gd));
+ wpa_hexdump_link_key(MSG_DEBUG, link_id,
+ "RSN: received GTK in pairwise handshake",
+ gtk, gtk_len);
+
+ if (gtk_len < RSN_MLO_GTK_KDE_PREFIX_LENGTH ||
+ gtk_len - RSN_MLO_GTK_KDE_PREFIX_LENGTH > sizeof(gd.gtk))
+ return -1;
+
+ gd.keyidx = gtk[0] & 0x3;
+ gtk += 1;
+ gtk_len -= 1;
+
+ key_rsc = gtk;
+
+ gtk += 6;
+ gtk_len -= 6;
+
+ os_memcpy(gd.gtk, gtk, gtk_len);
+ gd.gtk_len = gtk_len;
+
+ ret = 0;
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len,
+ gtk_len, &gd.key_rsc_len,
+ &gd.alg) ||
+ wpa_supplicant_install_mlo_gtk(sm, link_id, &gd, key_rsc, 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Failed to install GTK for MLO Link ID %u",
+ link_id);
+ ret = -1;
+ goto out;
+ }
+
+out:
+ forced_memzero(&gd, sizeof(gd));
+ return ret;
+}
+
+
+static int wpa_supplicant_pairwise_mlo_gtk(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ struct wpa_eapol_ie_parse *ie,
+ int key_info)
+{
+ u8 i;
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (!(sm->mlo.valid_links & BIT(i)))
+ continue;
+
+ if (!ie->mlo_gtk[i]) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "MLO RSN: GTK not found for link ID %u", i);
+ return -1;
+ }
+
+ if (wpa_supplicant_mlo_gtk(sm, i, ie->mlo_gtk[i],
+ ie->mlo_gtk_len[i], key_info))
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
const u8 *gtk, size_t gtk_len,
@@ -1270,7 +1592,7 @@
"WPA: Invalid IGTK KeyID %d", keyidx);
return -1;
}
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
igtk->igtk, len, KEY_FLAG_GROUP_RX) < 0) {
@@ -1339,7 +1661,7 @@
"WPA: Invalid BIGTK KeyID %d", keyidx);
return -1;
}
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, bigtk->pn, sizeof(bigtk->pn),
bigtk->bigtk, len, KEY_FLAG_GROUP_RX) < 0) {
@@ -1361,6 +1683,180 @@
}
+static int wpa_supplicant_install_mlo_igtk(struct wpa_sm *sm, u8 link_id,
+ const struct rsn_mlo_igtk_kde *igtk,
+ int wnm_sleep)
+{
+ size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ u16 keyidx = WPA_GET_LE16(igtk->keyid);
+
+ /* Detect possible key reinstallation */
+ if ((sm->mlo.links[link_id].igtk.igtk_len == len &&
+ os_memcmp(sm->mlo.links[link_id].igtk.igtk, igtk->igtk,
+ sm->mlo.links[link_id].igtk.igtk_len) == 0) ||
+ (sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len == len &&
+ os_memcmp(sm->mlo.links[link_id].igtk_wnm_sleep.igtk, igtk->igtk,
+ sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len) == 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Not reinstalling already in-use IGTK to the driver (link_id=%d keyidx=%d)",
+ link_id, keyidx);
+ return 0;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: MLO Link %u IGTK keyid %d pn " COMPACT_MACSTR,
+ link_id, keyidx, MAC2STR(igtk->pn));
+ wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: IGTK", igtk->igtk, len);
+ if (keyidx > 4095) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Invalid MLO Link %d IGTK KeyID %d", link_id,
+ keyidx);
+ return -1;
+ }
+ if (wpa_sm_set_key(sm, link_id,
+ wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr, keyidx, 0, igtk->pn,
+ sizeof(igtk->pn), igtk->igtk, len,
+ KEY_FLAG_GROUP_RX) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Failed to configure MLO Link %d IGTK to the driver",
+ link_id);
+ return -1;
+ }
+
+ if (wnm_sleep) {
+ sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len = len;
+ os_memcpy(sm->mlo.links[link_id].igtk_wnm_sleep.igtk,
+ igtk->igtk,
+ sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len);
+ } else {
+ sm->mlo.links[link_id].igtk.igtk_len = len;
+ os_memcpy(sm->mlo.links[link_id].igtk.igtk, igtk->igtk,
+ sm->mlo.links[link_id].igtk.igtk_len);
+ }
+
+ return 0;
+}
+
+
+static int
+wpa_supplicant_install_mlo_bigtk(struct wpa_sm *sm, u8 link_id,
+ const struct rsn_mlo_bigtk_kde *bigtk,
+ int wnm_sleep)
+{
+ size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ u16 keyidx = WPA_GET_LE16(bigtk->keyid);
+
+ /* Detect possible key reinstallation */
+ if ((sm->mlo.links[link_id].bigtk.bigtk_len == len &&
+ os_memcmp(sm->mlo.links[link_id].bigtk.bigtk, bigtk->bigtk,
+ sm->mlo.links[link_id].bigtk.bigtk_len) == 0) ||
+ (sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len == len &&
+ os_memcmp(sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk,
+ bigtk->bigtk,
+ sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len) ==
+ 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Not reinstalling already in-use BIGTK to the driver (link_id=%d keyidx=%d)",
+ link_id, keyidx);
+ return 0;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: MLO Link %u BIGTK keyid %d pn " COMPACT_MACSTR,
+ link_id, keyidx, MAC2STR(bigtk->pn));
+ wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: BIGTK", bigtk->bigtk,
+ len);
+ if (keyidx < 6 || keyidx > 7) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Invalid MLO Link %d BIGTK KeyID %d", link_id,
+ keyidx);
+ return -1;
+ }
+ if (wpa_sm_set_key(sm, link_id,
+ wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr, keyidx, 0, bigtk->pn,
+ sizeof(bigtk->pn), bigtk->bigtk, len,
+ KEY_FLAG_GROUP_RX) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Failed to configure MLO Link %d BIGTK to the driver",
+ link_id);
+ return -1;
+ }
+
+ if (wnm_sleep) {
+ sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len = len;
+ os_memcpy(sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk,
+ bigtk->bigtk,
+ sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len);
+ } else {
+ sm->mlo.links[link_id].bigtk.bigtk_len = len;
+ os_memcpy(sm->mlo.links[link_id].bigtk.bigtk, bigtk->bigtk,
+ sm->mlo.links[link_id].bigtk.bigtk_len);
+ }
+
+ return 0;
+}
+
+
+static int _mlo_ieee80211w_set_keys(struct wpa_sm *sm, u8 link_id,
+ struct wpa_eapol_ie_parse *ie)
+{
+ size_t len;
+
+ if (ie->mlo_igtk[link_id]) {
+ len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ if (ie->mlo_igtk_len[link_id] !=
+ RSN_MLO_IGTK_KDE_PREFIX_LENGTH + len)
+ return -1;
+
+ if (wpa_supplicant_install_mlo_igtk(
+ sm, link_id,
+ (const struct rsn_mlo_igtk_kde *)
+ ie->mlo_igtk[link_id],
+ 0) < 0)
+ return -1;
+ }
+
+ if (ie->mlo_bigtk[link_id] && sm->beacon_prot) {
+ len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ if (ie->mlo_bigtk_len[link_id] !=
+ RSN_MLO_BIGTK_KDE_PREFIX_LENGTH + len)
+ return -1;
+
+ if (wpa_supplicant_install_mlo_bigtk(
+ sm, link_id,
+ (const struct rsn_mlo_bigtk_kde *)
+ ie->mlo_bigtk[link_id],
+ 0) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int mlo_ieee80211w_set_keys(struct wpa_sm *sm,
+ struct wpa_eapol_ie_parse *ie)
+{
+ u8 i;
+
+ if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) ||
+ sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED)
+ return 0;
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (!(sm->mlo.valid_links & BIT(i)))
+ continue;
+
+ if (_mlo_ieee80211w_set_keys(sm, i, ie))
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int ieee80211w_set_keys(struct wpa_sm *sm,
struct wpa_eapol_ie_parse *ie)
{
@@ -1664,13 +2160,32 @@
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
+ u8 *kde = NULL;
+ size_t kde_len = 0;
+
+ if (sm->mlo.valid_links) {
+ u8 *pos;
+
+ kde = os_malloc(RSN_SELECTOR_LEN + ETH_ALEN + 2);
+ if (!kde)
+ return -1;
+
+ /* Add MAC KDE */
+ wpa_printf(MSG_DEBUG, "MLO: Add MAC KDE into EAPOL-Key 4/4");
+ pos = kde;
+ pos = rsn_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->own_addr,
+ ETH_ALEN);
+ kde_len = pos - kde;
+ }
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)
+ hdrlen + kde_len, &rlen, (void *) &reply);
+ if (!rbuf) {
+ os_free(kde);
return -1;
+ }
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
@@ -1690,7 +2205,11 @@
WPA_REPLAY_COUNTER_LEN);
key_mic = (u8 *) (reply + 1);
- WPA_PUT_BE16(key_mic + mic_len, 0);
+ WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data length */
+ if (kde) {
+ os_memcpy(key_mic + mic_len + 2, kde, kde_len); /* Key Data */
+ os_free(kde);
+ }
wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key 4/4");
return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
@@ -1698,6 +2217,201 @@
}
+static int wpa_supplicant_validate_link_kde(struct wpa_sm *sm, u8 link_id,
+ const u8 *link_kde,
+ size_t link_kde_len)
+{
+ size_t rsne_len = 0, rsnxe_len = 0;
+ const u8 *rsne = NULL, *rsnxe = NULL;
+
+ if (!link_kde ||
+ link_kde_len < RSN_MLO_LINK_KDE_LINK_MAC_INDEX + ETH_ALEN) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: MLO Link KDE is not found for link ID %d",
+ link_id);
+ return -1;
+ }
+
+ if (os_memcmp(sm->mlo.links[link_id].bssid,
+ &link_kde[RSN_MLO_LINK_KDE_LINK_MAC_INDEX],
+ ETH_ALEN) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: MLO Link %u MAC address (" MACSTR
+ ") not matching association response (" MACSTR ")",
+ link_id,
+ MAC2STR(&link_kde[RSN_MLO_LINK_KDE_LINK_MAC_INDEX]),
+ MAC2STR(sm->mlo.links[link_id].bssid));
+ return -1;
+ }
+
+ if (link_kde[0] & RSN_MLO_LINK_KDE_LI_RSNE_INFO) {
+ rsne = link_kde + RSN_MLO_LINK_KDE_FIXED_LENGTH;
+ if (link_kde_len < RSN_MLO_LINK_KDE_FIXED_LENGTH + 2 ||
+ link_kde_len <
+ (size_t) (RSN_MLO_LINK_KDE_FIXED_LENGTH + 2 + rsne[1])) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No room for link %u RSNE in MLO Link KDE",
+ link_id);
+ return -1;
+ }
+
+ rsne_len = rsne[1] + 2;
+ }
+
+ if (!rsne) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: RSNE not present in MLO Link %u KDE", link_id);
+ return -1;
+ }
+
+ if (link_kde[0] & RSN_MLO_LINK_KDE_LI_RSNXE_INFO) {
+ rsnxe = link_kde + RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len;
+ if (link_kde_len <
+ (RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len + 2) ||
+ link_kde_len <
+ (RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len + 2 + rsnxe[1])) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No room for link %u RSNXE in MLO Link KDE",
+ link_id);
+ return -1;
+ }
+
+ rsnxe_len = rsnxe[1] + 2;
+ }
+
+ if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
+ sm->mlo.links[link_id].ap_rsne,
+ sm->mlo.links[link_id].ap_rsne_len,
+ rsne, rsne_len)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN MLO: IE in 3/4 msg does not match with IE in Beacon/ProbeResp for link ID %u",
+ link_id);
+ wpa_hexdump(MSG_INFO, "RSNE in Beacon/ProbeResp",
+ sm->mlo.links[link_id].ap_rsne,
+ sm->mlo.links[link_id].ap_rsne_len);
+ wpa_hexdump(MSG_INFO, "RSNE in EAPOL-Key msg 3/4",
+ rsne, rsne_len);
+ return -1;
+ }
+
+ if ((sm->mlo.links[link_id].ap_rsnxe && !rsnxe) ||
+ (!sm->mlo.links[link_id].ap_rsnxe && rsnxe) ||
+ (sm->mlo.links[link_id].ap_rsnxe && rsnxe &&
+ (sm->mlo.links[link_id].ap_rsnxe_len != rsnxe_len ||
+ os_memcmp(sm->mlo.links[link_id].ap_rsnxe, rsnxe,
+ sm->mlo.links[link_id].ap_rsnxe_len) != 0))) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN MLO: RSNXE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4 for link ID %u",
+ link_id);
+ wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp",
+ sm->mlo.links[link_id].ap_rsnxe,
+ sm->mlo.links[link_id].ap_rsnxe_len);
+ wpa_hexdump(MSG_INFO, "RSNXE in EAPOL-Key msg 3/4",
+ rsnxe, rsnxe_len);
+ wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_validate_mlo_ieee80211w_kdes(struct wpa_sm *sm,
+ u8 link_id,
+ struct wpa_eapol_ie_parse *ie)
+{
+ if (ie->mlo_igtk[link_id] &&
+ ie->mlo_igtk_len[link_id] != RSN_MLO_IGTK_KDE_PREFIX_LENGTH +
+ (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN MLO: Invalid IGTK KDE length %lu for link ID %u",
+ (unsigned long) ie->mlo_igtk_len, link_id);
+ return -1;
+ }
+
+ if (!sm->beacon_prot)
+ return 0;
+
+ if (ie->mlo_bigtk[link_id] &&
+ ie->mlo_bigtk_len[link_id] != RSN_MLO_BIGTK_KDE_PREFIX_LENGTH +
+ (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN MLO: Invalid BIGTK KDE length %lu for link ID %u",
+ (unsigned long) ie->mlo_bigtk_len, link_id);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_3_of_4_wpa(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
+{
+ u16 key_info, keylen;
+ struct wpa_eapol_ie_parse ie;
+
+ wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: RX message 3 of 4-Way Handshake from " MACSTR
+ " (ver=%d)", MAC2STR(sm->bssid), ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+ goto failed;
+
+ if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+ goto failed;
+
+ if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: ANonce from message 1 of 4-Way Handshake differs from 3 of 4-Way Handshake - drop packet (src="
+ MACSTR ")", MAC2STR(sm->bssid));
+ goto failed;
+ }
+
+ keylen = WPA_GET_BE16(key->key_length);
+ if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid %s key length %d (src=" MACSTR ")",
+ wpa_cipher_txt(sm->pairwise_cipher), keylen,
+ MAC2STR(sm->bssid));
+ goto failed;
+ }
+
+ if (wpa_supplicant_send_4_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+ key_info, &sm->ptk) < 0)
+ goto failed;
+
+ /* SNonce was successfully used in msg 3/4, so mark it to be renewed
+ * for the next 4-Way Handshake. If msg 3 is received again, the old
+ * SNonce will still be used to avoid changing PTK. */
+ sm->renew_snonce = 1;
+
+ if ((key_info & WPA_KEY_INFO_INSTALL) &&
+ wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX_TX))
+ goto failed;
+
+ if (key_info & WPA_KEY_INFO_SECURE) {
+ wpa_sm_mlme_setprotection(
+ sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ eapol_sm_notify_portValid(sm->eapol, true);
+ }
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ sm->msg_3_of_4_ok = 1;
+ return;
+
+failed:
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
u16 ver, const u8 *key_data,
@@ -1705,28 +2419,83 @@
{
u16 key_info, keylen;
struct wpa_eapol_ie_parse ie;
+ bool mlo = sm->mlo.valid_links;
+ int i;
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
- wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: RX message 3 of 4-Way "
- "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: RX message 3 of 4-Way Handshake from " MACSTR
+ " (ver=%d)%s", MAC2STR(sm->bssid), ver, mlo ? " (MLO)" : "");
key_info = WPA_GET_BE16(key->key_info);
wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
goto failed;
- if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+
+ if (mlo && !ie.valid_mlo_gtks) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: No GTK KDE included in EAPOL-Key msg 3/4");
+ goto failed;
+ }
+ if (mlo &&
+ (key_info &
+ (WPA_KEY_INFO_ENCR_KEY_DATA | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_SECURE)) !=
+ (WPA_KEY_INFO_ENCR_KEY_DATA | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_SECURE)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN MLO: Invalid key info (0x%x) in EAPOL-Key msg 3/4",
+ key_info);
+ goto failed;
+ }
+
+ if (mlo && !is_valid_ap_mld_mac_kde(sm, ie.mac_addr)) {
+ wpa_printf(MSG_DEBUG, "RSN: Invalid AP MLD MAC address KDE");
+ goto failed;
+ }
+
+ for (i = 0; mlo && i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(sm->mlo.req_links & BIT(i)))
+ continue;
+
+ if (wpa_supplicant_validate_link_kde(sm, i, ie.mlo_link[i],
+ ie.mlo_link_len[i]) < 0)
+ goto failed;
+
+ if (!(sm->mlo.valid_links & BIT(i)))
+ continue;
+
+ if (!ie.mlo_gtk[i]) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "RSN: GTK not found for link ID %u", i);
+ goto failed;
+ }
+
+ if (sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED &&
+ wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
+ wpa_validate_mlo_ieee80211w_kdes(sm, i, &ie) < 0)
+ goto failed;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (mlo && wpa_key_mgmt_ft(sm->key_mgmt) &&
+ wpa_supplicant_validate_ie_ft(sm, sm->bssid, &ie) < 0)
+ goto failed;
+#endif /* CONFIG_IEEE80211R */
+
+ if (!mlo && ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: GTK IE in unencrypted key data");
goto failed;
}
- if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (!mlo && ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: IGTK KDE in unencrypted key data");
goto failed;
}
- if (ie.igtk &&
+ if (!mlo && ie.igtk &&
sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED &&
wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN +
@@ -1737,7 +2506,7 @@
goto failed;
}
- if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+ if (!mlo && wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
goto failed;
if (wpa_handle_ext_key_id(sm, &ie))
@@ -1807,10 +2576,9 @@
wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
goto failed;
- if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
- &sm->ptk) < 0) {
+ if (wpa_supplicant_send_4_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+ key_info, &sm->ptk) < 0)
goto failed;
- }
/* SNonce was successfully used in msg 3/4, so mark it to be renewed
* for the next 4-Way Handshake. If msg 3 is received again, the old
@@ -1837,7 +2605,14 @@
}
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
- if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
+ if (mlo) {
+ if (wpa_supplicant_pairwise_mlo_gtk(sm, key, &ie,
+ key_info) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: Failed to configure MLO GTKs");
+ goto failed;
+ }
+ } else if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
/* No GTK to be set to the driver */
} else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1851,17 +2626,18 @@
goto failed;
}
- if (ieee80211w_set_keys(sm, &ie) < 0) {
+ if ((mlo && mlo_ieee80211w_set_keys(sm, &ie) < 0) ||
+ (!mlo && ieee80211w_set_keys(sm, &ie) < 0)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Failed to configure IGTK");
goto failed;
}
- if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
+ if (mlo || sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
wpa_supplicant_key_neg_complete(sm, sm->bssid,
key_info & WPA_KEY_INFO_SECURE);
- if (ie.gtk)
+ if (mlo || ie.gtk)
wpa_sm_set_rekey_offload(sm);
/* Add PMKSA cache entry for Suite B AKMs here since PMKID can be
@@ -1875,7 +2651,7 @@
sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
sm->ptk.kck, sm->ptk.kck_len,
- sm->bssid, sm->own_addr,
+ wpa_sm_get_auth_addr(sm), sm->own_addr,
sm->network_ctx, sm->key_mgmt, NULL);
if (!sm->cur_pmksa)
sm->cur_pmksa = sa;
@@ -1891,173 +2667,6 @@
}
-static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
- const u8 *keydata,
- size_t keydatalen,
- u16 key_info,
- struct wpa_gtk_data *gd)
-{
- int maxkeylen;
- struct wpa_eapol_ie_parse ie;
- u16 gtk_len;
-
- 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)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: GTK IE in unencrypted key data");
- return -1;
- }
- if (ie.gtk == NULL) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: No GTK IE in Group Key msg 1/2");
- return -1;
- }
- gtk_len = ie.gtk_len;
- if (gtk_len < 2) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2",
- gtk_len);
- return -1;
- }
- gtk_len -= 2;
- if (gtk_len > sizeof(gd->gtk)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "RSN: Too long GTK in GTK KDE (len=%u)", gtk_len);
- return -1;
- }
- maxkeylen = gd->gtk_len = gtk_len;
-
-#ifdef CONFIG_OCV
- if (wpa_sm_ocv_enabled(sm)) {
- struct wpa_channel_info ci;
-
- if (wpa_sm_channel_info(sm, &ci) != 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
- return -1;
- }
-
- if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
- channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != OCI_SUCCESS) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
- "addr=" MACSTR " frame=eapol-key-g1 error=%s",
- MAC2STR(sm->bssid), ocv_errorstr);
- return -1;
- }
- }
-#endif /* CONFIG_OCV */
-
- if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
- gtk_len, maxkeylen,
- &gd->key_rsc_len, &gd->alg))
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
- ie.gtk, 2 + gtk_len);
- gd->keyidx = ie.gtk[0] & 0x3;
- gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
- !!(ie.gtk[0] & BIT(2)));
- os_memcpy(gd->gtk, ie.gtk + 2, gtk_len);
-
- if (ieee80211w_set_keys(sm, &ie) < 0)
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "RSN: Failed to configure IGTK");
-
- return 0;
-}
-
-
-static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
- const struct wpa_eapol_key *key,
- const u8 *key_data,
- size_t key_data_len, u16 key_info,
- u16 ver, struct wpa_gtk_data *gd)
-{
- size_t maxkeylen;
- u16 gtk_len;
-
- gtk_len = WPA_GET_BE16(key->key_length);
- maxkeylen = key_data_len;
- if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- if (maxkeylen < 8) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Too short maxkeylen (%lu)",
- (unsigned long) maxkeylen);
- return -1;
- }
- maxkeylen -= 8;
- }
-
- if (gtk_len > maxkeylen ||
- wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
- gtk_len, maxkeylen,
- &gd->key_rsc_len, &gd->alg))
- return -1;
-
- gd->gtk_len = gtk_len;
- gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
- WPA_KEY_INFO_KEY_INDEX_SHIFT;
- if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
-#ifdef CONFIG_NO_RC4
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: RC4 not supported in the build");
- return -1;
-#else /* CONFIG_NO_RC4 */
- u8 ek[32];
- if (key_data_len > sizeof(gd->gtk)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: RC4 key data too long (%lu)",
- (unsigned long) key_data_len);
- return -1;
- }
- os_memcpy(ek, key->key_iv, 16);
- os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
- os_memcpy(gd->gtk, key_data, key_data_len);
- if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
- forced_memzero(ek, sizeof(ek));
- wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
- "WPA: RC4 failed");
- return -1;
- }
- forced_memzero(ek, sizeof(ek));
-#endif /* CONFIG_NO_RC4 */
- } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- if (maxkeylen % 8) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Unsupported AES-WRAP len %lu",
- (unsigned long) maxkeylen);
- return -1;
- }
- if (maxkeylen > sizeof(gd->gtk)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: AES-WRAP key data "
- "too long (keydatalen=%lu maxkeylen=%lu)",
- (unsigned long) key_data_len,
- (unsigned long) maxkeylen);
- return -1;
- }
- if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
- key_data, gd->gtk)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: AES unwrap failed - could not decrypt "
- "GTK");
- return -1;
- }
- } else {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Unsupported key_info type %d", ver);
- return -1;
- }
- gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
- sm, !!(key_info & WPA_KEY_INFO_TXRX));
- return 0;
-}
-
-
static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
int ver, u16 key_info)
@@ -2141,19 +2750,121 @@
}
-static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
- const unsigned char *src_addr,
- const struct wpa_eapol_key *key,
- const u8 *key_data,
- size_t key_data_len, u16 ver)
+static void wpa_supplicant_process_mlo_1_of_2(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ const u8 *key_data,
+ size_t key_data_len, u16 ver)
{
u16 key_info;
- int rekey, ret;
- struct wpa_gtk_data gd;
- const u8 *key_rsc;
+ u8 i;
+ struct wpa_eapol_ie_parse ie;
if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: Group Key Handshake started prior to completion of 4-way handshake");
+ goto failed;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "MLO RSN: RX message 1 of Group "
+ "Key Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr),
+ ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ wpa_hexdump_key(MSG_DEBUG, "MLO RSN: msg 1/2 key data", key_data,
+ key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+ goto failed;
+
+ if (!ie.valid_mlo_gtks) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: No MLO GTK KDE in Group Key msg 1/2");
+ goto failed;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "MLO RSN: MLO GTK KDE in unencrypted key data");
+ goto failed;
+ }
+
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
+ goto failed;
+ }
+
+ if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+ "addr=" MACSTR " frame=eapol-key-g1 error=%s",
+ MAC2STR(sm->bssid), ocv_errorstr);
+ goto failed;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ if (mlo_ieee80211w_set_keys(sm, &ie) < 0)
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: Failed to configure MLO IGTK");
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (!(sm->mlo.valid_links & BIT(i)))
+ continue;
+
+ /*
+ * AP may send group keys for subset of the all links during
+ * rekey
+ */
+ if (!ie.mlo_gtk[i])
+ continue;
+
+ if (wpa_supplicant_mlo_gtk(sm, i, ie.mlo_gtk[i],
+ ie.mlo_gtk_len[i], key_info))
+ goto failed;
+ }
+
+ if (wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
+ goto failed;
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "MLO RSN: Group rekeying completed "
+ "with " MACSTR " [GTK=%s]", MAC2STR(sm->mlo.ap_mld_addr),
+ wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
+
+ wpa_sm_set_rekey_offload(sm);
+
+ return;
+
+failed:
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static void wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ const u8 *key_data,
+ size_t key_data_len, u16 ver)
+{
+ u16 key_info;
+ int rekey;
+ struct wpa_gtk_data gd;
+ const u8 *key_rsc;
+ size_t maxkeylen;
+ u16 gtk_len;
+
+ if (!sm->msg_3_of_4_ok) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Group Key Handshake started prior to completion of 4-way handshake");
goto failed;
}
@@ -2161,25 +2872,88 @@
os_memset(&gd, 0, sizeof(gd));
rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
- wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
- "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: RX message 1 of Group Key Handshake from " MACSTR
+ " (ver=%d)", MAC2STR(src_addr), ver);
key_info = WPA_GET_BE16(key->key_info);
- if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
- ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
- key_data_len, key_info,
- &gd);
- } else {
- ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
- key_data_len,
- key_info, ver, &gd);
+ gtk_len = WPA_GET_BE16(key->key_length);
+ maxkeylen = key_data_len;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (maxkeylen < 8) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Too short maxkeylen (%lu)",
+ (unsigned long) maxkeylen);
+ goto failed;
+ }
+ maxkeylen -= 8;
}
+ if (gtk_len > maxkeylen ||
+ wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gtk_len, maxkeylen,
+ &gd.key_rsc_len, &gd.alg))
+ goto failed;
+
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
- if (ret)
+ gd.gtk_len = gtk_len;
+ gd.keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#if defined(CONFIG_NO_RC4) || defined(CONFIG_FIPS)
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 not supported in the build");
goto failed;
+#else /* CONFIG_NO_RC4 || CONFIG_FIPS */
+ u8 ek[32];
+ if (key_data_len > sizeof(gd.gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 key data too long (%lu)",
+ (unsigned long) key_data_len);
+ goto failed;
+ }
+ os_memcpy(ek, key->key_iv, 16);
+ os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+ os_memcpy(gd.gtk, key_data, key_data_len);
+ if (rc4_skip(ek, 32, 256, gd.gtk, key_data_len)) {
+ forced_memzero(ek, sizeof(ek));
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: RC4 failed");
+ goto failed;
+ }
+ forced_memzero(ek, sizeof(ek));
+#endif /* CONFIG_NO_RC4 || CONFIG_FIPS */
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (maxkeylen % 8) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported AES-WRAP len %lu",
+ (unsigned long) maxkeylen);
+ goto failed;
+ }
+ if (maxkeylen > sizeof(gd.gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES-WRAP key data "
+ "too long (keydatalen=%lu maxkeylen=%lu)",
+ (unsigned long) key_data_len,
+ (unsigned long) maxkeylen);
+ goto failed;
+ }
+ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
+ key_data, gd.gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES unwrap failed - could not decrypt "
+ "GTK");
+ goto failed;
+ }
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported key_info type %d", ver);
+ goto failed;
+ }
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
+ sm, !!(key_info & WPA_KEY_INFO_TXRX));
key_rsc = key->key_rsc;
if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
@@ -2191,15 +2965,15 @@
forced_memzero(&gd, sizeof(gd));
if (rekey) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
- "completed with " MACSTR " [GTK=%s]",
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Group rekeying completed with " MACSTR
+ " [GTK=%s]",
MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
wpa_sm_cancel_auth_timeout(sm);
wpa_sm_set_state(sm, WPA_COMPLETED);
} else {
wpa_supplicant_key_neg_complete(sm, sm->bssid,
- key_info &
- WPA_KEY_INFO_SECURE);
+ key_info & WPA_KEY_INFO_SECURE);
}
wpa_sm_set_rekey_offload(sm);
@@ -2212,6 +2986,127 @@
}
+static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ const u8 *key_data,
+ size_t key_data_len, u16 ver)
+{
+ u16 key_info;
+ struct wpa_gtk_data gd;
+ const u8 *key_rsc;
+ int maxkeylen;
+ struct wpa_eapol_ie_parse ie;
+ u16 gtk_len;
+
+ if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Group Key Handshake started prior to completion of 4-way handshake");
+ goto failed;
+ }
+
+ os_memset(&gd, 0, sizeof(gd));
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: RX message 1 of Group Key Handshake from " MACSTR
+ " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data",
+ key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+ goto failed;
+
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: GTK KDE in unencrypted key data");
+ goto failed;
+ }
+ if (!ie.gtk) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No GTK KDE in Group Key msg 1/2");
+ goto failed;
+ }
+ gtk_len = ie.gtk_len;
+ if (gtk_len < 2) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2",
+ gtk_len);
+ goto failed;
+ }
+ gtk_len -= 2;
+ if (gtk_len > sizeof(gd.gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Too long GTK in GTK KDE (len=%u)", gtk_len);
+ goto failed;
+ }
+ maxkeylen = gd.gtk_len = gtk_len;
+
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
+ goto failed;
+ }
+
+ if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+ "addr=" MACSTR " frame=eapol-key-g1 error=%s",
+ MAC2STR(sm->bssid), ocv_errorstr);
+ goto failed;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gtk_len, maxkeylen,
+ &gd.key_rsc_len, &gd.alg))
+ goto failed;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
+ ie.gtk, 2 + gtk_len);
+ gd.keyidx = ie.gtk[0] & 0x3;
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(ie.gtk[0] & BIT(2)));
+ os_memcpy(gd.gtk, ie.gtk + 2, gtk_len);
+
+ if (ieee80211w_set_keys(sm, &ie) < 0)
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure IGTK");
+
+ key_rsc = key->key_rsc;
+ if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+ key_rsc = null_rsc;
+
+ if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) ||
+ wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
+ goto failed;
+ forced_memzero(&gd, sizeof(gd));
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Group rekeying completed with " MACSTR " [GTK=%s]",
+ MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
+
+ wpa_sm_set_rekey_offload(sm);
+
+ return;
+
+failed:
+ forced_memzero(&gd, sizeof(gd));
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
struct wpa_eapol_key *key,
u16 ver,
@@ -2310,11 +3205,11 @@
/* Decrypt key data here so that this operation does not need
* to be implemented separately for each message type. */
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
-#ifdef CONFIG_NO_RC4
+#if defined(CONFIG_NO_RC4) || defined(CONFIG_FIPS)
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: RC4 not supported in the build");
return -1;
-#else /* CONFIG_NO_RC4 */
+#else /* CONFIG_NO_RC4 || CONFIG_FIPS */
u8 ek[32];
wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4");
@@ -2327,7 +3222,7 @@
return -1;
}
forced_memzero(ek, sizeof(ek));
-#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_NO_RC4 || CONFIG_FIPS */
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
wpa_use_aes_key_wrap(sm->key_mgmt)) {
@@ -2513,6 +3408,88 @@
#endif /* CONFIG_FILS */
+static int wpa_sm_rx_eapol_wpa(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key,
+ enum frame_encryption encrypted,
+ const u8 *tmp, size_t data_len,
+ u8 *key_data, size_t key_data_len)
+{
+ u16 key_info, ver;
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ if (key->type != EAPOL_KEY_TYPE_WPA) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported EAPOL-Key type %d", key->type);
+ return -1;
+ }
+
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported EAPOL-Key descriptor version %d",
+ ver);
+ return -1;
+ }
+
+ if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: CCMP is used, but EAPOL-Key descriptor version (%d) is not 2",
+ ver);
+ if (sm->group_cipher != WPA_CIPHER_CCMP &&
+ !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ /* Earlier versions of IEEE 802.11i did not explicitly
+ * require version 2 descriptor for all EAPOL-Key
+ * packets, so allow group keys to use version 1 if
+ * CCMP is not used for them. */
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Backwards compatibility: allow invalid version for non-CCMP group keys");
+ } else
+ return -1;
+ }
+
+ if ((key_info & WPA_KEY_INFO_MIC) &&
+ wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
+ return -1;
+
+ if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Ignored EAPOL-Key (Pairwise) with non-zero key index");
+ return -1;
+ }
+ if (key_info & (WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ /* 3/4 4-Way Handshake */
+ wpa_supplicant_process_3_of_4_wpa(sm, key, ver,
+ key_data,
+ key_data_len);
+ } else {
+ /* 1/4 4-Way Handshake */
+ wpa_supplicant_process_1_of_4_wpa(sm, src_addr, key,
+ ver, key_data,
+ key_data_len,
+ encrypted);
+ }
+ } else {
+ if (key_info & WPA_KEY_INFO_MIC) {
+ /* 1/2 Group Key Handshake */
+ wpa_supplicant_process_1_of_2_wpa(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/Encr bit - dropped");
+ }
+ }
+
+ return 1;
+}
+
+
/**
* wpa_sm_rx_eapol - Process received WPA EAPOL frames
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -2624,19 +3601,77 @@
goto out;
}
+ 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,
+ "WPA: EAPOL-Key Replay Counter did not increase - dropping packet");
+ goto out;
+ }
+
eapol_sm_notify_lower_layer_success(sm->eapol, 0);
+
key_info = WPA_GET_BE16(key->key_info);
+
+ 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;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: EAPOL-Key with Request bit - dropped");
+ goto out;
+ }
+
+ if (sm->proto == WPA_PROTO_WPA) {
+ ret = wpa_sm_rx_eapol_wpa(sm, src_addr, key, encrypted,
+ tmp, data_len,
+ key_data, key_data_len);
+ goto out;
+ }
+
+ if (key->type != EAPOL_KEY_TYPE_RSN) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Unsupported EAPOL-Key type %d", key->type);
+ goto out;
+ }
+
ver = key_info & WPA_KEY_INFO_TYPE_MASK;
if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
!wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Unsupported EAPOL-Key descriptor version %d",
+ "RSN: Unsupported EAPOL-Key descriptor version %d",
ver);
goto out;
}
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+ sm->pairwise_cipher != WPA_CIPHER_TKIP) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: EAPOL-Key descriptor version %d not allowed without TKIP as the pairwise cipher",
+ ver);
+ goto out;
+ }
+
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+ (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
+ sm->key_mgmt != WPA_KEY_MGMT_PSK)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: EAPOL-Key descriptor version %d not allowed due to negotiated AKM (0x%x)",
+ ver, sm->key_mgmt);
+ goto out;
+ }
+
if (wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -2660,63 +3695,28 @@
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
!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");
+ "RSN: AP did not use the negotiated AES-128-CMAC");
goto out;
}
} else if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
!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 "
- "descriptor version (%d) is not 2", ver);
- if (sm->group_cipher != WPA_CIPHER_CCMP &&
- !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
- /* Earlier versions of IEEE 802.11i did not explicitly
- * require version 2 descriptor for all EAPOL-Key
- * packets, so allow group keys to use version 1 if
- * CCMP is not used for them. */
+ "RSN: CCMP is used, but EAPOL-Key descriptor version (%d) is not 2", ver);
+ if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Backwards compatibility: allow invalid "
- "version for non-CCMP group keys");
- } else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ "RSN: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
+ } else {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
- } else
+ "RSN: Unexpected descriptor version %u", ver);
goto out;
+ }
} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
!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 "
- "descriptor version (%d) is not 2", ver);
- goto out;
- }
-
- 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,
- "WPA: EAPOL-Key Replay Counter did not increase - "
- "dropping packet");
- goto out;
- }
-
- 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;
- }
-
- if (key_info & WPA_KEY_INFO_REQUEST) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: EAPOL-Key with Request bit - dropped");
+ "RSN: GCMP is used, but EAPOL-Key descriptor version (%d) is not 2",
+ ver);
goto out;
}
@@ -2753,8 +3753,7 @@
if (key_info & WPA_KEY_INFO_KEY_TYPE) {
if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Ignored EAPOL-Key (Pairwise) with "
- "non-zero key index");
+ "RSN: Ignored EAPOL-Key (Pairwise) with non-zero key index");
goto out;
}
if (key_info & (WPA_KEY_INFO_MIC |
@@ -2773,13 +3772,19 @@
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);
+ if (sm->mlo.valid_links)
+ wpa_supplicant_process_mlo_1_of_2(sm, src_addr,
+ key, key_data,
+ key_data_len,
+ ver);
+ else
+ 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/Encr bit - "
- "dropped");
+ "RSN: EAPOL-Key (Group) without Mic/Encr bit - dropped");
}
}
@@ -2965,6 +3970,15 @@
}
+static void wpa_sm_pmksa_notify_cb(struct rsn_pmksa_cache_entry *entry,
+ void *ctx)
+{
+ struct wpa_sm *sm = ctx;
+
+ wpa_sm_notify_pmksa_cache_entry(sm, entry);
+}
+
+
/**
* wpa_sm_init - Initialize WPA state machine
* @ctx: Context pointer for callbacks; this needs to be an allocated buffer
@@ -2989,7 +4003,8 @@
sm->dot11RSNAConfigSATimeout = 60;
sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb,
- wpa_sm_pmksa_is_current_cb, sm, sm);
+ wpa_sm_pmksa_is_current_cb,
+ wpa_sm_pmksa_notify_cb, sm, sm);
if (sm->pmksa == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"RSN: PMKSA cache initialization failed");
@@ -3007,6 +4022,8 @@
*/
void wpa_sm_deinit(struct wpa_sm *sm)
{
+ int i;
+
if (sm == NULL)
return;
pmksa_cache_deinit(sm->pmksa);
@@ -3017,6 +4034,10 @@
os_free(sm->ap_wpa_ie);
os_free(sm->ap_rsn_ie);
os_free(sm->ap_rsnxe);
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ os_free(sm->mlo.links[i].ap_rsne);
+ os_free(sm->mlo.links[i].ap_rsnxe);
+ }
wpa_sm_drop_sa(sm);
os_free(sm->ctx);
#ifdef CONFIG_IEEE80211R
@@ -3041,6 +4062,32 @@
}
+static void wpa_sm_clear_ptk(struct wpa_sm *sm)
+{
+ int i;
+
+ sm->ptk_set = 0;
+ os_memset(&sm->ptk, 0, sizeof(sm->ptk));
+ sm->tptk_set = 0;
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+ os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
+ os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+ os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+ sm->tk_set = false;
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ os_memset(&sm->mlo.links[i].gtk, 0,
+ sizeof(sm->mlo.links[i].gtk));
+ os_memset(&sm->mlo.links[i].gtk_wnm_sleep, 0,
+ sizeof(sm->mlo.links[i].gtk_wnm_sleep));
+ os_memset(&sm->mlo.links[i].igtk, 0,
+ sizeof(sm->mlo.links[i].igtk));
+ os_memset(&sm->mlo.links[i].igtk_wnm_sleep, 0,
+ sizeof(sm->mlo.links[i].igtk_wnm_sleep));
+ }
+}
+
+
/**
* wpa_sm_notify_assoc - Notify WPA state machine about association
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -3100,15 +4147,7 @@
* this is not part of a Fast BSS Transition.
*/
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
- sm->ptk_set = 0;
- os_memset(&sm->ptk, 0, sizeof(sm->ptk));
- sm->tptk_set = 0;
- os_memset(&sm->tptk, 0, sizeof(sm->tptk));
- os_memset(&sm->gtk, 0, sizeof(sm->gtk));
- os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
- os_memset(&sm->igtk, 0, sizeof(sm->igtk));
- os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
- sm->tk_set = false;
+ wpa_sm_clear_ptk(sm);
}
#ifdef CONFIG_TDLS
@@ -3305,6 +4344,85 @@
}
+int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo)
+{
+ int i;
+
+ if (!sm)
+ return -1;
+
+ os_memcpy(sm->mlo.ap_mld_addr, mlo->ap_mld_addr, ETH_ALEN);
+ sm->mlo.assoc_link_id = mlo->assoc_link_id;
+ sm->mlo.valid_links = mlo->valid_links;
+ sm->mlo.req_links = mlo->req_links;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ const u8 *ie;
+ size_t len;
+
+ if (sm->mlo.req_links & BIT(i)) {
+ if (!mlo->links[i].ap_rsne ||
+ mlo->links[i].ap_rsne_len == 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No RSNE for AP MLO link %d with BSSID "
+ MACSTR,
+ i, MAC2STR(mlo->links[i].bssid));
+ return -1;
+
+ }
+ os_memcpy(sm->mlo.links[i].addr, mlo->links[i].addr,
+ ETH_ALEN);
+ os_memcpy(sm->mlo.links[i].bssid, mlo->links[i].bssid,
+ ETH_ALEN);
+ }
+
+ ie = mlo->links[i].ap_rsne;
+ len = mlo->links[i].ap_rsne_len;
+ os_free(sm->mlo.links[i].ap_rsne);
+ if (!ie || len == 0) {
+ if (sm->mlo.links[i].ap_rsne)
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Clearing MLO link[%u] AP RSNE",
+ i);
+ sm->mlo.links[i].ap_rsne = NULL;
+ sm->mlo.links[i].ap_rsne_len = 0;
+ } else {
+ wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNE",
+ ie, len);
+ sm->mlo.links[i].ap_rsne = os_memdup(ie, len);
+ if (!sm->mlo.links[i].ap_rsne) {
+ sm->mlo.links[i].ap_rsne_len = 0;
+ return -1;
+ }
+ sm->mlo.links[i].ap_rsne_len = len;
+ }
+
+ ie = mlo->links[i].ap_rsnxe;
+ len = mlo->links[i].ap_rsnxe_len;
+ os_free(sm->mlo.links[i].ap_rsnxe);
+ if (!ie || len == 0) {
+ if (sm->mlo.links[i].ap_rsnxe)
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Clearing MLO link[%u] AP RSNXE",
+ i);
+ sm->mlo.links[i].ap_rsnxe = NULL;
+ sm->mlo.links[i].ap_rsnxe_len = 0;
+ } else {
+ wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNXE", ie,
+ len);
+ sm->mlo.links[i].ap_rsnxe = os_memdup(ie, len);
+ if (!sm->mlo.links[i].ap_rsnxe) {
+ sm->mlo.links[i].ap_rsnxe_len = 0;
+ return -1;
+ }
+ sm->mlo.links[i].ap_rsnxe_len = len;
+ }
+ }
+
+ return 0;
+}
+
+
/**
* wpa_sm_set_own_addr - Set own MAC address
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -3874,10 +4992,11 @@
}
-int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const u8 *own_addr,
const void *network_ctx)
{
- return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL;
+ return pmksa_cache_get(sm->pmksa, bssid, own_addr, NULL, network_ctx,
+ 0) != NULL;
}
@@ -3887,24 +5006,25 @@
const void *network_ctx,
int akmp)
{
- return pmksa_cache_get(sm->pmksa, aa, pmkid, network_ctx, akmp);
+ return pmksa_cache_get(sm->pmksa, aa, sm->own_addr, pmkid, network_ctx,
+ akmp);
+}
+
+
+void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ if (sm && sm->pmksa)
+ pmksa_cache_remove(sm->pmksa, entry);
}
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->tk_set = false;
+ wpa_sm_clear_ptk(sm);
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));
- os_memset(&sm->gtk, 0, sizeof(sm->gtk));
- os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
- os_memset(&sm->igtk, 0, sizeof(sm->igtk));
- os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
#ifdef CONFIG_IEEE80211R
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
sm->xxkey_len = 0;
@@ -3921,12 +5041,21 @@
}
-int wpa_sm_has_ptk(struct wpa_sm *sm)
+#ifdef CONFIG_IEEE80211R
+bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md)
{
- if (sm == NULL)
- return 0;
+ if (!sm)
+ return false;
+ if (!wpa_key_mgmt_ft(sm->key_mgmt) ||
+ os_memcmp(md, sm->key_mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ /* Do not allow FT protocol to be used even if we were to have
+ * an PTK since the mobility domain has changed. */
+ return false;
+ }
return sm->ptk_set;
}
+#endif /* CONFIG_IEEE80211R */
int wpa_sm_has_ptk_installed(struct wpa_sm *sm)
@@ -3958,7 +5087,7 @@
void wpa_sm_install_pmk(struct wpa_sm *sm)
{
/* In case the driver wants to handle re-assocs, pass it down the PMK. */
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->pairwise_cipher), NULL, 0, 0, NULL, 0,
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->pairwise_cipher), NULL, 0, 0, NULL, 0,
(u8*)sm->pmk, sm->pmk_len, KEY_FLAG_PMK) < 0) {
wpa_hexdump(MSG_DEBUG, "PSK: Install PMK to the driver for driver reassociations",
(u8*)sm->pmk, sm->pmk_len);
@@ -4127,6 +5256,12 @@
}
+const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm)
+{
+ return sm->mlo.valid_links ? sm->mlo.ap_mld_addr : sm->bssid;
+}
+
+
#ifdef CONFIG_FILS
struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
@@ -4362,7 +5497,7 @@
}
if (wpa_ft_parse_ies(pos, end - pos, &parse,
- wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) {
+ sm->key_mgmt) < 0) {
wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
goto fail;
}
@@ -4509,7 +5644,8 @@
else
kdk_len = 0;
- if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
+ if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr,
+ wpa_sm_get_auth_addr(sm),
sm->fils_nonce, sm->fils_anonce,
dh_ss ? wpabuf_head(dh_ss) : NULL,
dh_ss ? wpabuf_len(dh_ss) : 0,
@@ -4651,21 +5787,27 @@
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) {
+ sm->pmk_r0, sm->pmk_r0_name, sm->key_mgmt) < 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;
+ if (wpa_key_mgmt_sae_ext_key(sm->key_mgmt))
+ sm->pmk_r0_len = sm->fils_ft_len;
+ else
+ sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_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,
- sm->pmk_r1_name, use_sha384) < 0) {
+ sm->pmk_r1_name, sm->fils_ft_len) < 0) {
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
return -1;
}
os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
/* Management Group Cipher Suite */
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
@@ -5071,12 +6213,13 @@
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,
+ if (wpa_sm_set_key(sm, -1, alg, wpa_sm_get_auth_addr(sm), 0, 1,
+ null_rsc, rsclen,
sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+ "FILS: Failed to set PTK to the driver (alg=%d keylen=%d auth_addr="
MACSTR ")",
- alg, keylen, MAC2STR(sm->bssid));
+ alg, keylen, MAC2STR(wpa_sm_get_auth_addr(sm)));
goto fail;
}
@@ -5360,15 +6503,6 @@
#ifdef CONFIG_PASN
-void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
- const u8 *pmkid, const u8 *bssid, int key_mgmt)
-{
- sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
- bssid, sm->own_addr, NULL,
- key_mgmt, 0);
-}
-
-
void wpa_pasn_sm_set_caps(struct wpa_sm *sm, unsigned int flags2)
{
if (flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
@@ -5387,3 +6521,17 @@
if (sm)
pmksa_cache_reconfig(sm->pmksa);
}
+
+
+struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm)
+{
+ return sm ? sm->pmksa : NULL;
+}
+
+
+void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ if (sm)
+ sm->cur_pmksa = entry;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 411f328..e3c7892 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -19,6 +19,7 @@
struct wpa_config_blob;
struct hostapd_freq_params;
struct wpa_channel_info;
+struct rsn_pmksa_cache_entry;
enum frame_encryption;
struct wpa_sm_ctx {
@@ -29,7 +30,7 @@
enum wpa_states (*get_state)(void *ctx);
void (*deauthenticate)(void * ctx, u16 reason_code);
void (*reconnect)(void *ctx);
- int (*set_key)(void *ctx, enum wpa_alg alg,
+ int (*set_key)(void *ctx, int link_id, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len, enum key_flag key_flag);
@@ -98,6 +99,8 @@
const u8 *peer_addr, size_t ltf_keyseed_len,
const u8 *ltf_keyseed);
#endif /* CONFIG_PASN */
+ void (*notify_pmksa_cache_entry)(void *ctx,
+ struct rsn_pmksa_cache_entry *entry);
};
@@ -145,6 +148,27 @@
bool force_kdk_derivation;
};
+struct wpa_sm_link {
+ u8 addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 *ap_rsne, *ap_rsnxe;
+ size_t ap_rsne_len, ap_rsnxe_len;
+ struct wpa_gtk gtk;
+ struct wpa_gtk gtk_wnm_sleep;
+ struct wpa_igtk igtk;
+ struct wpa_igtk igtk_wnm_sleep;
+ struct wpa_bigtk bigtk;
+ struct wpa_bigtk bigtk_wnm_sleep;
+};
+
+struct wpa_sm_mlo {
+ u8 ap_mld_addr[ETH_ALEN];
+ u8 assoc_link_id;
+ u16 valid_links; /* bitmap of accepted links */
+ u16 req_links; /* bitmap of requested links */
+ struct wpa_sm_link links[MAX_NUM_MLD_LINKS];
+};
+
#ifndef CONFIG_NO_WPA
struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
@@ -200,7 +224,7 @@
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,
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const u8 *own_addr,
const void *network_ctx);
void wpa_sm_drop_sa(struct wpa_sm *sm);
struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm,
@@ -208,7 +232,9 @@
const u8 *pmkid,
const void *network_ctx,
int akmp);
-int wpa_sm_has_ptk(struct wpa_sm *sm);
+void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry);
+bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md);
int wpa_sm_has_ptk_installed(struct wpa_sm *sm);
void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
@@ -229,6 +255,7 @@
const u8 *ptk_kek, size_t ptk_kek_len);
int wpa_fils_is_completed(struct wpa_sm *sm);
void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm);
+int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo);
#else /* CONFIG_NO_WPA */
@@ -443,6 +470,12 @@
{
}
+static inline int wpa_sm_set_mlo_params(struct wpa_sm *sm,
+ const struct wpa_sm_mlo *mlo)
+{
+ return 0;
+}
+
#endif /* CONFIG_NO_WPA */
#ifdef CONFIG_IEEE80211R
@@ -461,7 +494,7 @@
int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
size_t ies_len, const u8 *src_addr);
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
- const u8 *mdie);
+ const u8 *mdie, bool force);
#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
int wpa_ft_is_ft_protocol(struct wpa_sm *sm);
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
@@ -529,8 +562,9 @@
#ifdef CONFIG_PASN
-int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
- u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
+static inline int
+wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
+ u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
{
return -1;
}
@@ -585,8 +619,11 @@
void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z);
-void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
- const u8 *pmkid, const u8 *bssid, int key_mgmt);
void wpa_pasn_sm_set_caps(struct wpa_sm *sm, unsigned int flags2);
+struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm);
+
+void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry);
+const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm);
#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 96d293f..4d52542 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "crypto/aes_wrap.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"
@@ -55,11 +56,14 @@
return -1;
}
- sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+ if (wpa_key_mgmt_sae_ext_key(sm->key_mgmt))
+ sm->pmk_r0_len = mpmk_len;
+ else
+ sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
if (wpa_derive_pmk_r0(mpmk, mpmk_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)
+ sm->pmk_r0, sm->pmk_r0_name, sm->key_mgmt) < 0)
return -1;
sm->pmk_r1_len = sm->pmk_r0_len;
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
@@ -77,7 +81,7 @@
kdk_len = 0;
ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
- anonce, sm->own_addr, sm->bssid,
+ anonce, sm->own_addr, wpa_sm_get_auth_addr(sm),
sm->pmk_r1_name, ptk, ptk_name, sm->key_mgmt,
sm->pairwise_cipher, kdk_len);
if (ret) {
@@ -85,6 +89,9 @@
return ret;
}
+ os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+
#ifdef CONFIG_PASN
if (sm->secure_ltf &&
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
@@ -105,7 +112,6 @@
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;
@@ -121,8 +127,7 @@
return 0;
}
- use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
- if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0)
+ if (wpa_ft_parse_ies(ies, ies_len, &ft, sm->key_mgmt) < 0)
return -1;
if (ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
@@ -184,7 +189,7 @@
* @len: Buffer for returning the length of the IEs
* @anonce: ANonce or %NULL if not yet available
* @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
- * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @kck: KCK for MIC or %NULL if no MIC is used
* @kck_len: KCK length in octets
* @target_ap: Target AP address
* @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
@@ -211,12 +216,13 @@
size_t rsnxe_len;
int rsnxe_used;
int res;
+ u8 mic_control;
sm->ft_completed = 0;
sm->ft_reassoc_completed = 0;
buf_len = 2 + sizeof(struct rsn_mdie) + 2 +
- sizeof(struct rsn_ftie_sha384) +
+ sizeof(struct rsn_ftie_sha512) +
2 + sm->r0kh_id_len + ric_ies_len + 100;
buf = os_zalloc(buf_len);
if (buf == NULL)
@@ -334,7 +340,8 @@
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ftie_len = pos++;
rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) && anonce &&
- (sm->sae_pwe == 1 || sm->sae_pwe == 2);
+ (sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ sm->sae_pwe == SAE_PWE_BOTH);
#ifdef CONFIG_TESTING_OPTIONS
if (anonce && sm->ft_rsnxe_used) {
rsnxe_used = sm->ft_rsnxe_used == 1;
@@ -342,11 +349,28 @@
rsnxe_used);
}
#endif /* CONFIG_TESTING_OPTIONS */
- if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+ mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r0_len == SHA512_MAC_LEN) {
+ struct rsn_ftie_sha512 *ftie;
+
+ ftie = (struct rsn_ftie_sha512 *) pos;
+ mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ ftie->mic_control[0] = mic_control;
+ 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 if ((sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r0_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->key_mgmt)) {
struct rsn_ftie_sha384 *ftie;
ftie = (struct rsn_ftie_sha384 *) pos;
- ftie->mic_control[0] = !!rsnxe_used;
+ mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ ftie->mic_control[0] = mic_control;
fte_mic = ftie->mic;
elem_count = &ftie->mic_control[1];
pos += sizeof(*ftie);
@@ -357,7 +381,8 @@
struct rsn_ftie *ftie;
ftie = (struct rsn_ftie *) pos;
- ftie->mic_control[0] = !!rsnxe_used;
+ mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ ftie->mic_control[0] = mic_control;
fte_mic = ftie->mic;
elem_count = &ftie->mic_control[1];
pos += sizeof(*ftie);
@@ -441,7 +466,8 @@
*elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len);
if (rsnxe_len)
*elem_count += 1;
- if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
+ if (wpa_ft_mic(sm->key_mgmt, 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,
@@ -476,7 +502,8 @@
alg = wpa_cipher_to_alg(sm->pairwise_cipher);
keylen = wpa_cipher_key_len(sm->pairwise_cipher);
- if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
+ /* TODO: AP MLD address for MLO */
+ if (wpa_sm_set_key(sm, -1, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
(u8 *) sm->ptk.tk, keylen,
KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
@@ -563,8 +590,6 @@
const u8 *bssid;
const u8 *kck;
size_t kck_len, kdk_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);
@@ -590,7 +615,7 @@
return -1;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
@@ -603,34 +628,15 @@
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 (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTE");
+ return -1;
}
- if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- snonce, WPA_NONCE_LEN);
+ parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
return -1;
@@ -675,8 +681,8 @@
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", anonce, WPA_NONCE_LEN);
- os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: ANonce", parse.fte_anonce, WPA_NONCE_LEN);
+ os_memcpy(sm->anonce, parse.fte_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)
@@ -694,13 +700,17 @@
else
kdk_len = 0;
+ /* TODO: AP MLD address for MLO */
if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
- anonce, sm->own_addr, bssid,
+ parse.fte_anonce, sm->own_addr, bssid,
sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
sm->pairwise_cipher,
kdk_len) < 0)
return -1;
+ os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+
#ifdef CONFIG_PASN
if (sm->secure_ltf &&
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
@@ -717,7 +727,7 @@
kck = sm->ptk.kck;
kck_len = sm->ptk.kck_len;
}
- ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce,
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, parse.fte_anonce,
sm->pmk_r1_name,
kck, kck_len, bssid,
ric_ies, ric_ies_len,
@@ -866,7 +876,7 @@
os_memcpy(gtk + 16, gtk + 24, 8);
os_memcpy(gtk + 24, tmp, 8);
}
- if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
+ if (wpa_sm_set_key(sm, -1, alg, broadcast_ether_addr, keyidx, 0,
gtk_elem + 3, rsc_len, gtk, keylen,
KEY_FLAG_GROUP_RX) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
@@ -933,7 +943,7 @@
wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
igtk_len);
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr, keyidx, 0,
igtk_elem + 2, 6, igtk, igtk_len,
KEY_FLAG_GROUP_RX) < 0) {
@@ -1001,7 +1011,7 @@
wpa_hexdump_key(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk,
bigtk_len);
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr, keyidx, 0,
bigtk_elem + 2, 6, bigtk, bigtk_len,
KEY_FLAG_GROUP_RX) < 0) {
@@ -1025,10 +1035,8 @@
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;
- int own_rsnxe_used, rsnxe_used;
+ int own_rsnxe_used;
+ size_t mic_len;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
@@ -1043,7 +1051,7 @@
return 0;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
@@ -1056,49 +1064,37 @@
return -1;
}
- if (use_sha384) {
- struct rsn_ftie_sha384 *ftie;
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA512_MAC_LEN)
+ mic_len = 32;
+ else if ((sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->key_mgmt))
+ mic_len = 24;
+ else
+ mic_len = 16;
- 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;
- rsnxe_used = ftie->mic_control[0] & 0x01;
- 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;
- rsnxe_used = ftie->mic_control[0] & 0x01;
- fte_elem_count = ftie->mic_control[1];
- fte_mic = ftie->mic;
+ if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+ parse.fte_mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+ parse.fte_mic_len, mic_len);
+ return -1;
}
- if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- snonce, WPA_NONCE_LEN);
+ parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
return -1;
}
- if (os_memcmp(anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- anonce, WPA_NONCE_LEN);
+ parse.fte_anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->anonce, WPA_NONCE_LEN);
return -1;
@@ -1145,10 +1141,10 @@
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (parse.rsnxe)
count++;
- if (fte_elem_count != count) {
+ if (parse.fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- fte_elem_count, count);
+ parse.fte_elem_count, count);
return -1;
}
@@ -1160,7 +1156,7 @@
kck_len = sm->ptk.kck_len;
}
- if (wpa_ft_mic(kck, kck_len, sm->own_addr, src_addr, 6,
+ if (wpa_ft_mic(sm->key_mgmt, 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,
@@ -1172,14 +1168,15 @@
return -1;
}
- if (os_memcmp_const(mic, fte_mic, 16) != 0) {
+ if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
- wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16);
- wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+ parse.fte_mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
return -1;
}
- if (rsnxe_used && !sm->ap_rsnxe) {
+ if (parse.fte_rsnxe_used && !sm->ap_rsnxe) {
wpa_printf(MSG_INFO,
"FT: FTE indicated that AP uses RSNXE, but RSNXE was not included in Beacon/Probe Response frames");
return -1;
@@ -1213,7 +1210,8 @@
}
own_rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) &&
- (sm->sae_pwe == 1 || sm->sae_pwe == 2);
+ (sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ sm->sae_pwe == SAE_PWE_BOTH);
if ((sm->ap_rsnxe && !parse.rsnxe && own_rsnxe_used) ||
(!sm->ap_rsnxe && parse.rsnxe) ||
(sm->ap_rsnxe && parse.rsnxe &&
@@ -1285,14 +1283,24 @@
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @target_ap: Target AP Address
* @mdie: Mobility Domain IE from the target AP
+ * @force: Whether to force the request and ignore mobility domain differences
+ * (only for testing purposes)
* Returns: 0 on success, -1 on failure
*/
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
- const u8 *mdie)
+ const u8 *mdie, bool force)
{
u8 *ft_ies;
size_t ft_ies_len;
+ if (!force &&
+ (!mdie || mdie[1] < 3 || !wpa_sm_has_ft_keys(sm, mdie + 2))) {
+ wpa_printf(MSG_DEBUG, "FT: Cannot use over-the DS with " MACSTR
+ " - no keys matching the mobility domain",
+ MAC2STR(target_ap));
+ return -1;
+ }
+
wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
MAC2STR(target_ap));
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 3811c3b..ed43cc1 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -106,7 +106,7 @@
int rsn_enabled; /* Whether RSN is enabled in configuration */
int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
int ocv; /* Operating Channel Validation */
- int sae_pwe; /* SAE PWE generation options */
+ enum sae_pwe sae_pwe; /* SAE PWE generation options */
unsigned int sae_pk:1; /* whether SAE-PK is used */
unsigned int secure_ltf:1;
@@ -150,6 +150,7 @@
size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 key_mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
size_t r0kh_id_len;
u8 r1kh_id[FT_R1KH_ID_LEN];
@@ -218,6 +219,7 @@
struct wpabuf *dpp_z;
int dpp_pfs;
#endif /* CONFIG_DPP2 */
+ struct wpa_sm_mlo mlo;
};
@@ -239,15 +241,15 @@
sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
}
-static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
- const u8 *addr, int key_idx, int set_tx,
- const u8 *seq, size_t seq_len,
+static inline int wpa_sm_set_key(struct wpa_sm *sm, int link_id,
+ enum wpa_alg alg, const u8 *addr, int key_idx,
+ int set_tx, const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
enum key_flag key_flag)
{
WPA_ASSERT(sm->ctx->set_key);
- return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
- seq, seq_len, key, key_len, key_flag);
+ return sm->ctx->set_key(sm->ctx->ctx, link_id, alg, addr, key_idx,
+ set_tx, seq, seq_len, key, key_len, key_flag);
}
static inline void wpa_sm_reconnect(struct wpa_sm *sm)
@@ -495,6 +497,14 @@
}
#endif /* CONFIG_PASN */
+static inline void
+wpa_sm_notify_pmksa_cache_entry(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ if (sm->ctx->notify_pmksa_cache_entry)
+ sm->ctx->notify_pmksa_cache_entry(sm->ctx->ctx, entry);
+}
+
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);
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index c4e660f..50bd2b2 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -362,7 +362,8 @@
size_t flen;
if (wpa_key_mgmt_sae(sm->key_mgmt) &&
- (sm->sae_pwe == 1 || sm->sae_pwe == 2 || sm->sae_pk)) {
+ (sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ sm->sae_pwe == SAE_PWE_BOTH || sm->sae_pk)) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (sm->sae_pk)
@@ -375,7 +376,7 @@
if (sm->secure_rtt)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (sm->prot_range_neg)
- capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 3825a73..9df56c2 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -771,7 +771,8 @@
hlen = tls_key_x_server_params_hash(
conn->rl.tls_version, conn->client_random,
conn->server_random, server_params,
- server_params_end - server_params, hash);
+ server_params_end - server_params, hash,
+ sizeof(hash));
}
if (hlen < 0)
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index e178915..0dd8e27 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -378,7 +378,7 @@
int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
const u8 *server_random,
const u8 *server_params,
- size_t server_params_len, u8 *hash)
+ size_t server_params_len, u8 *hash, size_t hsz)
{
u8 *hpos;
size_t hlen;
@@ -393,6 +393,8 @@
crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
crypto_hash_update(ctx, server_params, server_params_len);
hlen = MD5_MAC_LEN;
+ if (hsz < hlen)
+ return -1;
if (crypto_hash_finish(ctx, hash, &hlen) < 0)
return -1;
hpos += hlen;
@@ -403,7 +405,7 @@
crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
crypto_hash_update(ctx, server_params, server_params_len);
- hlen = hash + sizeof(hash) - hpos;
+ hlen = hsz - hlen;
if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
return -1;
hpos += hlen;
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index e30b15a..4cfdc2d 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -267,7 +267,8 @@
int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
const u8 *server_random,
const u8 *server_params,
- size_t server_params_len, u8 *hash);
+ size_t server_params_len,
+ u8 *hash, size_t hsz);
int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
const u8 *data, size_t data_len,
const u8 *pos, size_t len, u8 *alert);
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index 8d36cf1..545abae 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -620,7 +620,7 @@
hlen = tls_key_x_server_params_hash(
conn->rl.tls_version, conn->client_random,
conn->server_random, server_params,
- pos - server_params, hash);
+ pos - server_params, hash, sizeof(hash));
}
if (hlen < 0) {
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 9875b0e..4469712 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -596,13 +596,21 @@
}
-void wpa_debug_close_file(void)
+void wpa_debug_stop_log(void)
{
#ifdef CONFIG_DEBUG_FILE
if (!out_file)
return;
fclose(out_file);
out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_debug_close_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+ wpa_debug_stop_log();
os_free(last_path);
last_path = NULL;
#endif /* CONFIG_DEBUG_FILE */
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 29fc48a..291aa07 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -49,6 +49,7 @@
int wpa_debug_reopen_file(void);
void wpa_debug_close_file(void);
void wpa_debug_setup_stdout(void);
+void wpa_debug_stop_log(void);
/**
* wpa_debug_printf_timestamp - Print timestamp for debug output
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index bb3c055..63f0d52 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -63,12 +63,18 @@
} else
record->id_length = 0;
+ if (record->type_length > data + size - pos)
+ return -1;
record->type = record->type_length == 0 ? NULL : pos;
pos += record->type_length;
+ if (record->id_length > data + size - pos)
+ return -1;
record->id = record->id_length == 0 ? NULL : pos;
pos += record->id_length;
+ if (record->payload_length > (size_t) (data + size - pos))
+ return -1;
record->payload = record->payload_length == 0 ? NULL : pos;
pos += record->payload_length;
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index be79765..dd4423a 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -279,6 +279,8 @@
"src/ap/ap_config.c",
"src/ap/ap_drv_ops.c",
"src/ap/ap_list.c",
+ "src/ap/comeback_token.c",
+ "src/pasn/pasn_responder.c",
"src/ap/ap_mlme.c",
"src/ap/authsrv.c",
"src/ap/beacon.c",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 33dabf3..03dc209 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -108,6 +108,7 @@
INCLUDES += $(LOCAL_PATH)/src/tls
INCLUDES += $(LOCAL_PATH)/src/utils
INCLUDES += $(LOCAL_PATH)/src/wps
+INCLUDES += $(LOCAL_PATH)/src/pasn
INCLUDES += system/security/keystore/include
ifdef CONFIG_DRIVER_NL80211
ifneq ($(wildcard external/libnl),)
@@ -420,6 +421,7 @@
NEED_SHA256=y
NEED_SHA384=y
OBJS += src/common/ptksa_cache.c
+OBJS += src/pasn/pasn_initiator.c
OBJS += pasn_supplicant.c
endif
@@ -985,6 +987,8 @@
ifdef NEED_AP_MLME
OBJS += src/ap/wmm.c
OBJS += src/ap/ap_list.c
+OBJS += src/ap/comeback_token.c
+OBJS += src/pasn/pasn_responder.c
OBJS += src/ap/ieee802_11.c
OBJS += src/ap/hw_features.c
OBJS += src/ap/dfs.c
@@ -1757,6 +1761,142 @@
LDO=$(CC)
endif
+PASNOBJS =
+PASNOBJS += src/utils/$(CONFIG_ELOOP).c
+PASNOBJS += src/utils/wpa_debug.c
+PASNOBJS += src/utils/wpabuf.c
+PASNOBJS += src/utils/os_$(CONFIG_OS).c
+PASNOBJS += src/utils/config.c
+PASNOBJS += src/utils/common.c
+
+ifdef NEED_BASE64
+PASNOBJS += src/utils/base64.c
+endif
+
+ifdef CONFIG_WPA_TRACE
+PASNOBJS += src/utils/trace.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_FILE
+PASNOBJS += src/utils/ext_password_file.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+PASNOBJS += src/utils/ext_password_test.c
+endif
+
+ifdef NEED_EXT_PASSWORD
+PASNOBJS += src/utils/ext_password.c
+endif
+
+ifdef CONFIG_SAE
+PASNOBJS += src/common/sae.c
+endif
+
+ifdef CONFIG_SAE_PK
+PASNOBJS += src/common/sae_pk.c
+endif
+
+ifndef CONFIG_NO_WPA
+PASNOBJS += src/common/wpa_common.c
+endif
+
+PASNOBJS += src/common/ieee802_11_common.c
+
+ifdef NEED_DRAGONFLY
+PASNOBJS += src/common/dragonfly.c
+endif
+
+PASNOBJS += src/common/ptksa_cache.c
+
+ifndef CONFIG_NO_WPA
+PASNOBJS += src/rsn_supp/pmksa_cache.c
+PASNOBJS += src/rsn_supp/wpa_ie.c
+endif
+
+PASNOBJS += src/ap/comeback_token.c
+PASNOBJS += src/ap/pmksa_cache_auth.c
+
+ifdef NEED_EAP_COMMON
+PASNOBJS += src/eap_common/eap_common.c
+endif
+
+ifdef CHAP
+PASNOBJS += src/eap_common/chap.c
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+PASNOBJS += src/eap_peer/eap.c
+PASNOBJS += src/eap_peer/eap_methods.c
+PASNOBJS += src/eapol_supp/eapol_supp_sm.c
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+PASNOBJS += src/crypto/crypto_openssl.c
+ifdef TLS_FUNCS
+PASNOBJS += src/crypto/tls_openssl.c
+#PASNOBJS += -lssl -lcrypto
+NEED_TLS_PRF_SHA256=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+PASNOBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c
+ifdef TLS_FUNCS
+PASNOBJS += src/crypto/tls_gnutls.c
+PASNOBJS += -lgnutls -lgpg-error
+PASNOBJS += -lgcrypt
+endif
+endif
+
+ifdef NEED_TLS_PRF_SHA256
+PASNOBJS += src/crypto/sha256-tlsprf.c
+endif
+
+ifdef NEED_SHA512
+PASNOBJS += src/crypto/sha512-prf.c
+endif
+
+ifdef NEED_SHA384
+PASNOBJS += src/crypto/sha384-prf.c
+endif
+
+PASNOBJS += src/crypto/sha256-prf.c
+
+ifdef NEED_HMAC_SHA512_KDF
+PASNOBJS += src/crypto/sha512-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA384_KDF
+PASNOBJS += src/crypto/sha384-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA256_KDF
+PASNOBJS += src/crypto/sha256-kdf.c
+endif
+
+ifdef NEED_DH_GROUPS
+PASNOBJS += src/crypto/dh_groups.c
+endif
+
+ifdef NEED_AES_SIV
+PASNOBJS += src/crypto/aes-siv.c
+endif
+
+ifdef NEED_AES_CTR
+PASNOBJS += src/crypto/aes-ctr.c
+endif
+
+ifdef NEED_SHA1
+PASNOBJS += src/crypto/sha1-prf.c
+ifdef NEED_TLS_PRF
+PASNOBJS += src/crypto/sha1-tlsprf.c
+endif
+endif
+
+PASNOBJS += src/pasn/pasn_initiator.c
+PASNOBJS += src/pasn/pasn_responder.c
+
########################
include $(CLEAR_VARS)
@@ -1897,3 +2037,14 @@
$(LOCAL_PATH)/aidl
include $(BUILD_STATIC_LIBRARY)
endif # WPA_SUPPLICANT_USE_AIDL == y
+
+#include $(CLEAR_VARS)
+#LOCAL_MODULE = libpasn
+#LOCAL_CFLAGS = $(L_CFLAGS)
+#LOCAL_SRC_FILES = $(PASNOBJS)
+#LOCAL_C_INCLUDES = $(INCLUDES)
+#LOCAL_SHARED_LIBRARIES := libc libcutils liblog
+#ifeq ($(CONFIG_TLS), openssl)
+#LOCAL_SHARED_LIBRARIES := libcrypto libssl
+#endif
+#include $(BUILD_SHARED_LIBRARY)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 0a71558..c682f73 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -12,6 +12,12 @@
CONFIG_FILE=.config
include ../src/build.rules
+ifdef CONFIG_BUILD_PASN_SO
+# add the dependency this way to allow CONFIG_BUILD_PASN_SO
+# being set in the config which is read by build.rules
+_all: libpasn.so
+endif
+
ifdef CONFIG_BUILD_WPA_CLIENT_SO
# add the dependency this way to allow CONFIG_BUILD_WPA_CLIENT_SO
# being set in the config which is read by build.rules
@@ -76,6 +82,11 @@
ifndef CONFIG_NO_WPA_PASSPHRASE
install -D wpa_passphrase $(DESTDIR)/$(BINDIR)/wpa_passphrase
endif
+
+ifdef CONFIG_BUILD_PASN_SO
+ install -m 0644 -D libpasn.so $(DESTDIR)/$(LIBDIR)/libpasn.so
+endif
+
ifdef CONFIG_BUILD_WPA_CLIENT_SO
install -m 0644 -D libwpa_client.so $(DESTDIR)/$(LIBDIR)/libwpa_client.so
install -m 0644 -D ../src/common/wpa_ctrl.h $(DESTDIR)/$(INCDIR)/wpa_ctrl.h
@@ -418,6 +429,7 @@
NEED_SHA256=y
NEED_SHA384=y
OBJS += ../src/common/ptksa_cache.o
+OBJS += ../src/pasn/pasn_initiator.o
OBJS += pasn_supplicant.o
endif
@@ -987,6 +999,8 @@
ifdef NEED_AP_MLME
OBJS += ../src/ap/wmm.o
OBJS += ../src/ap/ap_list.o
+OBJS += ../src/ap/comeback_token.o
+OBJS += ../src/pasn/pasn_responder.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
@@ -2093,6 +2107,157 @@
lcov -c -d $(BUILDDIR) > lcov.info
genhtml lcov.info --output-directory lcov-html
+PASN_CFLAGS := $(CFLAGS)
+PASN_CFLAGS += -DCONFIG_PASN
+
+LIBPASNSO := ../src/utils/$(CONFIG_ELOOP).c
+LIBPASNSO += ../src/utils/wpa_debug.c
+LIBPASNSO += ../src/utils/wpabuf.c
+LIBPASNSO += ../src/utils/os_$(CONFIG_OS).c
+LIBPASNSO += ../src/utils/config.c
+LIBPASNSO += ../src/utils/common.c
+
+ifdef NEED_BASE64
+LIBPASNSO += ../src/utils/base64.c
+endif
+
+ifdef CONFIG_WPA_TRACE
+LIBPASNSO += ../src/utils/trace.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_FILE
+LIBPASNSO += ../src/utils/ext_password_file.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+LIBPASNSO += ../src/utils/ext_password_test.c
+endif
+
+ifdef NEED_EXT_PASSWORD
+LIBPASNSO += ../src/utils/ext_password.c
+endif
+
+ifdef CONFIG_SAE
+LIBPASNSO += ../src/common/sae.c
+endif
+
+ifdef CONFIG_SAE_PK
+LIBPASNSO += ../src/common/sae_pk.c
+endif
+
+ifndef CONFIG_NO_WPA
+LIBPASNSO += ../src/common/wpa_common.c
+endif
+
+LIBPASNSO += ../src/common/ieee802_11_common.c
+
+ifdef NEED_DRAGONFLY
+LIBPASNSO += ../src/common/dragonfly.c
+endif
+
+LIBPASNSO += ../src/common/ptksa_cache.c
+
+ifndef CONFIG_NO_WPA
+LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
+LIBPASNSO += ../src/rsn_supp/wpa_ie.c
+endif
+
+LIBPASNSO += ../src/ap/comeback_token.c
+LIBPASNSO += ../src/ap/pmksa_cache_auth.c
+
+ifdef NEED_EAP_COMMON
+LIBPASNSO += ../src/eap_common/eap_common.c
+endif
+
+ifdef CHAP
+LIBPASNSO += ../src/eap_common/chap.c
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+LIBPASNSO += ../src/eap_peer/eap.c
+LIBPASNSO += ../src/eap_peer/eap_methods.c
+LIBPASNSO += ../src/eapol_supp/eapol_supp_sm.c
+endif
+
+ifeq ($(CONFIG_TLS), wolfssl)
+LIBPASNSO += ../src/crypto/crypto_wolfssl.c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_wolfssl.c
+NEED_TLS_PRF_SHA256=y
+LIBPASNSO += -lwolfssl -lm
+endif
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+LIBPASNSO += ../src/crypto/crypto_openssl.c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_openssl.c
+LIBPASNSO += -lssl -lcrypto
+NEED_TLS_PRF_SHA256=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+LIBPASNSO += ../src/crypto/crypto_$(CONFIG_CRYPTO).c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_gnutls.c
+LIBPASNSO += -lgnutls -lgpg-error
+LIBPASNSO += -lgcrypt
+endif
+endif
+
+ifdef NEED_TLS_PRF_SHA256
+LIBPASNSO += ../src/crypto/sha256-tlsprf.c
+endif
+
+ifdef NEED_SHA512
+LIBPASNSO += ../src/crypto/sha512-prf.c
+endif
+
+ifdef NEED_SHA384
+LIBPASNSO += ../src/crypto/sha384-prf.c
+endif
+
+LIBPASNSO += ../src/crypto/sha256-prf.c
+
+ifdef NEED_HMAC_SHA512_KDF
+LIBPASNSO += ../src/crypto/sha512-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA384_KDF
+LIBPASNSO += ../src/crypto/sha384-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA256_KDF
+LIBPASNSO += ../src/crypto/sha256-kdf.c
+endif
+
+ifdef NEED_DH_GROUPS
+LIBPASNSO += ../src/crypto/dh_groups.c
+endif
+
+ifdef NEED_AES_SIV
+LIBPASNSO += ../src/crypto/aes-siv.c
+endif
+
+ifdef NEED_AES_CTR
+LIBPASNSO += ../src/crypto/aes-ctr.c
+endif
+
+ifdef NEED_SHA1
+LIBPASNSO += ../src/crypto/sha1-prf.c
+ifdef NEED_TLS_PRF
+LIBPASNSO += ../src/crypto/sha1-tlsprf.c
+endif
+endif
+
+LIBPASNSO += ../src/pasn/pasn_initiator.c
+LIBPASNSO += ../src/pasn/pasn_responder.c
+
+libpasn.so: $(LIBPASNSO)
+ @$(E) " CC $@ ($^)"
+ $(Q)$(CC) $(LDFLAGS) -o $@ $(PASN_CFLAGS) -shared -fPIC -lcrypto $^
+
clean: common-clean
$(MAKE) -C ../src clean
$(MAKE) -C dbus clean
@@ -2103,6 +2268,7 @@
rm -f lcov.info
rm -rf lcov-html
rm -f libwpa_client.a
+ rm -f libpasn.so
rm -f libwpa_client.so
rm -f libwpa_test1 libwpa_test2
rm -f wpa_passphrase
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index 0cc5f39..7d30e23 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -199,7 +199,26 @@
# be used to configure alternative FQDNs that will be considered home
# networks.
#
+# home_ois: Home OI(s)
+# This string field contains one or more comma delimited OIs (hexdump)
+# identifying the access the access points that support authentication
+# with this credential. There are an alternative to the use of the realm
+# parameter. When using Home OIs to match the network, the EAP parameters
+# need to be pre-configured with the credentials since the NAI Realm
+# information may not be available or fetched.
+# A successful authentication with the access point is possible as soon
+# as at least one Home OI from the list matches an OI in the Roaming
+# Consortium advertised by the access point.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
+#
+# required_home_ois: Required Home OI(s)
+# This string field contains the set of Home OI(s) (hexdump) that are
+# required to be advertised by the AP for the credential to be considered
+# matching.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
+#
# roaming_consortium: Roaming Consortium OI
+# Deprecated: use home_ois instead.
# If roaming_consortium_len is non-zero, this field contains the
# Roaming Consortium OI that can be used to determine which access
# points support authentication with this credential. This is an
@@ -209,6 +228,7 @@
# may not be available or fetched.
#
# required_roaming_consortium: Required Roaming Consortium OI
+# Deprecated: use required_home_ois instead.
# 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.
@@ -325,7 +345,7 @@
# password="password"
# ca_cert="/etc/wpa_supplicant/ca.pem"
# domain="example.com"
-# roaming_consortium=223344
+# home_ois="223344"
# roaming_consortiums="112233,4455667788,aabbcc"
# eap=TTLS
# phase2="auth=MSCHAPV2"
diff --git a/wpa_supplicant/aidl/aidl_manager.cpp b/wpa_supplicant/aidl/aidl_manager.cpp
index f644a39..e2523d4 100644
--- a/wpa_supplicant/aidl/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/aidl_manager.cpp
@@ -472,7 +472,7 @@
// Enable randomized source MAC address for GAS/ANQP
// Set the lifetime to 0, guarantees a unique address for each GAS
// session
- wpa_s->conf->gas_rand_mac_addr = 1;
+ wpa_s->conf->gas_rand_mac_addr = WPAS_MAC_ADDR_STYLE_RANDOM;
wpa_s->conf->gas_rand_addr_lifetime = 0;
}
diff --git a/wpa_supplicant/aidl/misc_utils.h b/wpa_supplicant/aidl/misc_utils.h
index c529e3d..d0330e0 100644
--- a/wpa_supplicant/aidl/misc_utils.h
+++ b/wpa_supplicant/aidl/misc_utils.h
@@ -88,6 +88,7 @@
ss.write((char *) pmksa_entry->pmk, pmksa_entry->pmk_len);
ss.write((char *) pmksa_entry->pmkid, PMKID_LEN);
ss.write((char *) pmksa_entry->aa, ETH_ALEN);
+ ss.write((char *) pmksa_entry->spa, ETH_ALEN);
// Omit wpa_ssid field because the network is created on connecting to a access point.
ss.write((char *) &pmksa_entry->akmp, sizeof(pmksa_entry->akmp));
ss.write((char *) &pmksa_entry->reauth_time, sizeof(pmksa_entry->reauth_time));
@@ -110,7 +111,7 @@
ss.read((char *) &pmksa_entry->pmk_len, sizeof(pmksa_entry->pmk_len));
if ((pmksa_entry->pmk_len > PMK_LEN_MAX) ||
(ss.str().size() < (sizeof(pmksa_entry->pmk_len) + pmksa_entry->pmk_len +
- PMKID_LEN + ETH_ALEN + sizeof(pmksa_entry->akmp) +
+ PMKID_LEN + ETH_ALEN + ETH_ALEN + sizeof(pmksa_entry->akmp) +
sizeof(pmksa_entry->reauth_time) + sizeof(pmksa_entry->expiration) +
sizeof(pmksa_entry->opportunistic) + 1 /* fils_cache_id_set */)))
return -1;
@@ -118,6 +119,7 @@
ss.read((char *) pmksa_entry->pmk, pmksa_entry->pmk_len);
ss.read((char *) pmksa_entry->pmkid, PMKID_LEN);
ss.read((char *) pmksa_entry->aa, ETH_ALEN);
+ ss.read((char *) pmksa_entry->spa, ETH_ALEN);
// Omit wpa_ssid field because the network is created on connecting to a access point.
ss.read((char *) &pmksa_entry->akmp, sizeof(pmksa_entry->akmp));
ss.read((char *) &pmksa_entry->reauth_time, sizeof(pmksa_entry->reauth_time));
diff --git a/wpa_supplicant/aidl/p2p_iface.cpp b/wpa_supplicant/aidl/p2p_iface.cpp
index 9927ba4..2afd75b 100644
--- a/wpa_supplicant/aidl/p2p_iface.cpp
+++ b/wpa_supplicant/aidl/p2p_iface.cpp
@@ -1652,6 +1652,10 @@
"Passphrase is invalid.");
}
+ wpa_printf(MSG_DEBUG,
+ "P2P: Add group with config Role: %s network name: %s freq: %d",
+ joinExistingGroup ? "CLIENT" : "GO",
+ wpa_ssid_txt(ssid.data(), ssid.size()), freq);
if (!joinExistingGroup) {
struct p2p_data *p2p = wpa_s->global->p2p;
os_memcpy(p2p->ssid, ssid.data(), ssid.size());
diff --git a/wpa_supplicant/aidl/sta_iface.cpp b/wpa_supplicant/aidl/sta_iface.cpp
index e8c1927..029b99a 100644
--- a/wpa_supplicant/aidl/sta_iface.cpp
+++ b/wpa_supplicant/aidl/sta_iface.cpp
@@ -2070,18 +2070,18 @@
SignalPollResult result;
result.linkId = 0;
- result.currentRssiDbm = mlo_si.links[i].current_signal;
- result.txBitrateMbps = mlo_si.links[i].current_txrate / 1000;
- result.rxBitrateMbps = mlo_si.links[i].current_rxrate / 1000;
+ result.currentRssiDbm = mlo_si.links[i].data.signal;
+ result.txBitrateMbps = mlo_si.links[i].data.current_tx_rate / 1000;
+ result.rxBitrateMbps = mlo_si.links[i].data.current_rx_rate / 1000;
result.frequencyMhz = mlo_si.links[i].frequency;
results.push_back(result);
}
} else if (wpa_drv_signal_poll(wpa_s, &si) == 0) {
SignalPollResult result;
result.linkId = 0;
- result.currentRssiDbm = si.current_signal;
- result.txBitrateMbps = si.current_txrate / 1000;
- result.rxBitrateMbps = si.current_rxrate / 1000;
+ result.currentRssiDbm = si.data.signal;
+ result.txBitrateMbps = si.data.current_tx_rate / 1000;
+ result.rxBitrateMbps = si.data.current_rx_rate / 1000;
result.frequencyMhz = si.frequency;
results.push_back(result);
}
diff --git a/wpa_supplicant/aidl/sta_network.cpp b/wpa_supplicant/aidl/sta_network.cpp
index 5a83b05..4d816e4 100644
--- a/wpa_supplicant/aidl/sta_network.cpp
+++ b/wpa_supplicant/aidl/sta_network.cpp
@@ -2617,13 +2617,13 @@
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
switch (mode) {
case SaeH2eMode::DISABLED:
- wpa_s->conf->sae_pwe = 0;
+ wpa_s->conf->sae_pwe = SAE_PWE_HUNT_AND_PECK;
break;
case SaeH2eMode::H2E_MANDATORY:
- wpa_s->conf->sae_pwe = 1;
+ wpa_s->conf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
break;
case SaeH2eMode::H2E_OPTIONAL:
- wpa_s->conf->sae_pwe = 2;
+ wpa_s->conf->sae_pwe = SAE_PWE_BOTH;
break;
}
resetInternalStateAfterParamsUpdate();
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 16d6c90..f4c3811 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -706,8 +706,12 @@
bss->wpa_group_rekey = 86400;
}
- if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT)
+ if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT) {
bss->ieee80211w = ssid->ieee80211w;
+ } else if (wpa_s->conf->pmf != MGMT_FRAME_PROTECTION_DEFAULT) {
+ if (ssid->mode == WPAS_MODE_AP)
+ bss->ieee80211w = wpa_s->conf->pmf;
+ }
#ifdef CONFIG_OCV
bss->ocv = ssid->ocv;
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index cb732f7..75bdec1 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -422,7 +422,7 @@
/* Poll for signal info to set initial scan interval */
struct wpa_signal_info siginfo;
if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
- siginfo.current_signal >= data->signal_threshold)
+ siginfo.data.signal >= data->signal_threshold)
data->scan_interval = data->long_interval;
}
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index 41a26df..5a8f97c 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -137,7 +137,7 @@
/* Poll for signal info to set initial scan interval */
struct wpa_signal_info siginfo;
if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
- siginfo.current_signal >= data->signal_threshold)
+ siginfo.data.signal >= data->signal_threshold)
data->scan_interval = data->long_interval;
}
wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d",
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index dcc28be..0f986bb 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -993,6 +993,14 @@
if (wpa_bss_in_use(wpa_s, bss))
continue;
+ if (wpa_s->reassoc_same_ess &&
+ wpa_s->wpa_state != WPA_COMPLETED &&
+ wpa_s->last_ssid &&
+ bss->ssid_len == wpa_s->last_ssid->ssid_len &&
+ os_memcmp(bss->ssid, wpa_s->last_ssid->ssid,
+ bss->ssid_len) == 0)
+ continue;
+
if (os_reltime_before(&bss->last_update, &t)) {
wpa_bss_remove(wpa_s, bss, __func__);
} else
@@ -1438,3 +1446,21 @@
return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),
capab);
}
+
+
+/**
+ * wpa_bss_defrag_mle - Get a buffer holding a de-fragmented ML element
+ * @bss: BSS table entry
+ * @type: ML control type
+ */
+struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type)
+{
+ struct ieee802_11_elems elems;
+ const u8 *pos = wpa_bss_ie_ptr(bss);
+ size_t len = bss->ie_len;
+
+ if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
+ return NULL;
+
+ return ieee802_11_defrag_mle(&elems, type);
+}
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index b68fc87..611da88 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -201,4 +201,6 @@
unsigned int age_ms,
struct os_reltime *update_time);
+struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type);
+
#endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 2b1bdeb..f3e2edb 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2345,6 +2345,50 @@
#endif /* NO_CONFIG_WRITE */
+static int wpa_config_parse_mac_value(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ u8 mac_value[ETH_ALEN];
+
+ if (hwaddr_aton(value, mac_value) == 0) {
+ if (os_memcmp(mac_value, ssid->mac_value, ETH_ALEN) == 0)
+ return 1;
+ os_memcpy(ssid->mac_value, mac_value, ETH_ALEN);
+ return 0;
+ }
+
+ wpa_printf(MSG_ERROR, "Line %d: Invalid MAC address '%s'",
+ line, value);
+ return -1;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_mac_value(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ const size_t size = 3 * ETH_ALEN;
+ char *value;
+ int res;
+
+ if (ssid->mac_addr != WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS)
+ return NULL;
+
+ value = os_malloc(size);
+ if (!value)
+ return NULL;
+ res = os_snprintf(value, size, MACSTR, MAC2STR(ssid->mac_value));
+ if (os_snprintf_error(size, res)) {
+ os_free(value);
+ return NULL;
+ }
+ value[size - 1] = '\0';
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
/* Helper macros for network block parser */
#ifdef OFFSET
@@ -2644,7 +2688,8 @@
{ 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(mac_addr, 0, 3) },
+ { FUNC_KEY(mac_value) },
{ INT_RANGE(pbss, 0, 2) },
{ INT_RANGE(wps_disabled, 0, 1) },
{ INT_RANGE(fils_dh_group, 0, 65535) },
@@ -2665,6 +2710,7 @@
{ INT_RANGE(beacon_prot, 0, 1) },
{ INT_RANGE(transition_disable, 0, 255) },
{ INT_RANGE(sae_pk, 0, 2) },
+ { INT_RANGE(disable_eht, 0, 1)},
};
#undef OFFSET
@@ -3181,7 +3227,7 @@
#ifdef CONFIG_MACSEC
ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
#endif /* CONFIG_MACSEC */
- ssid->mac_addr = -1;
+ ssid->mac_addr = WPAS_MAC_ADDR_STYLE_NOT_SET;
ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
}
@@ -3532,53 +3578,62 @@
}
-static int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred,
- const char *value)
+static int
+wpa_config_set_cred_ois(u8 cred_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN],
+ size_t cred_ois_len[MAX_ROAMING_CONS],
+ unsigned int *cred_num_ois,
+ 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;
+ u8 ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
+ size_t ois_len[MAX_ROAMING_CONS];
+ unsigned int num_ois = 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));
+ len = os_strlen(value);
+ if (len / 2 < 3) {
+ wpa_printf(MSG_ERROR,
+ "Invalid organisation identifier (OI) list: %s",
+ value);
+ return -1;
+ }
+
+ os_memset(ois, 0, sizeof(ois));
+ os_memset(ois_len, 0, sizeof(ois_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 ||
+ if (len / 2 < 3 || (len & 1) != 0 ||
len / 2 > MAX_ROAMING_CONS_OI_LEN ||
hexstr2bin(pos,
- roaming_consortiums[num_roaming_consortiums],
+ ois[num_ois],
len / 2) < 0) {
wpa_printf(MSG_INFO,
- "Invalid roaming_consortiums entry: %s",
+ "Invalid organisation identifier (OI) entry: %s",
pos);
return -1;
}
- roaming_consortiums_len[num_roaming_consortiums] = len / 2;
- num_roaming_consortiums++;
+ ois_len[num_ois] = len / 2;
+ num_ois++;
if (!end)
break;
- if (num_roaming_consortiums >= MAX_ROAMING_CONS) {
+ if (num_ois >= MAX_ROAMING_CONS) {
wpa_printf(MSG_INFO,
- "Too many roaming_consortiums OIs");
+ "Too many 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;
+ os_memcpy(cred_ois, ois, sizeof(ois));
+ os_memcpy(cred_ois_len, ois_len, sizeof(ois_len));
+ *cred_num_ois = num_ois;
return 0;
}
@@ -3813,36 +3868,70 @@
}
if (os_strcmp(var, "roaming_consortium") == 0) {
- if (len < 3 || len > sizeof(cred->roaming_consortium)) {
+ if (len < 3 || len > sizeof(cred->home_ois[0])) {
wpa_printf(MSG_ERROR, "Line %d: invalid "
"roaming_consortium length %d (3..15 "
"expected)", line, (int) len);
os_free(val);
return -1;
}
- os_memcpy(cred->roaming_consortium, val, len);
- cred->roaming_consortium_len = len;
+ wpa_printf(MSG_WARNING,
+ "Line %d: option roaming_consortium is deprecated and will be removed in the future",
+ line);
+ os_memcpy(cred->home_ois[0], val, len);
+ cred->home_ois_len[0] = len;
+ cred->num_home_ois = 1;
os_free(val);
return 0;
}
if (os_strcmp(var, "required_roaming_consortium") == 0) {
- if (len < 3 || len > sizeof(cred->required_roaming_consortium))
- {
+ if (len < 3 || len > sizeof(cred->required_home_ois[0])) {
wpa_printf(MSG_ERROR, "Line %d: invalid "
"required_roaming_consortium length %d "
"(3..15 expected)", line, (int) len);
os_free(val);
return -1;
}
- os_memcpy(cred->required_roaming_consortium, val, len);
- cred->required_roaming_consortium_len = len;
+ wpa_printf(MSG_WARNING,
+ "Line %d: option required_roaming_consortium is deprecated and will be removed in the future",
+ line);
+ os_memcpy(cred->required_home_ois[0], val, len);
+ cred->required_home_ois_len[0] = len;
+ cred->num_required_home_ois = 1;
os_free(val);
return 0;
}
+ if (os_strcmp(var, "home_ois") == 0) {
+ res = wpa_config_set_cred_ois(cred->home_ois,
+ cred->home_ois_len,
+ &cred->num_home_ois,
+ val);
+ if (res < 0)
+ wpa_printf(MSG_ERROR, "Line %d: invalid home_ois",
+ line);
+ os_free(val);
+ return res;
+ }
+
+ if (os_strcmp(var, "required_home_ois") == 0) {
+ res = wpa_config_set_cred_ois(cred->required_home_ois,
+ cred->required_home_ois_len,
+ &cred->num_required_home_ois,
+ val);
+ if (res < 0)
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid required_home_ois", line);
+ os_free(val);
+ return res;
+ }
+
if (os_strcmp(var, "roaming_consortiums") == 0) {
- res = wpa_config_set_cred_roaming_consortiums(cred, val);
+ res = wpa_config_set_cred_ois(cred->roaming_consortiums,
+ cred->roaming_consortiums_len,
+ &cred->num_roaming_consortiums,
+ val);
if (res < 0)
wpa_printf(MSG_ERROR,
"Line %d: invalid roaming_consortiums",
@@ -4162,14 +4251,14 @@
size_t buflen;
char *buf;
- if (!cred->roaming_consortium_len)
+ if (!cred->num_home_ois || !cred->home_ois_len[0])
return NULL;
- buflen = cred->roaming_consortium_len * 2 + 1;
+ buflen = cred->home_ois_len[0] * 2 + 1;
buf = os_malloc(buflen);
if (buf == NULL)
return NULL;
- wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
- cred->roaming_consortium_len);
+ wpa_snprintf_hex(buf, buflen, cred->home_ois[0],
+ cred->home_ois_len[0]);
return buf;
}
@@ -4177,14 +4266,64 @@
size_t buflen;
char *buf;
- if (!cred->required_roaming_consortium_len)
+ if (!cred->num_required_home_ois ||
+ !cred->required_home_ois_len[0])
return NULL;
- buflen = cred->required_roaming_consortium_len * 2 + 1;
+ buflen = cred->required_home_ois_len[0] * 2 + 1;
buf = os_malloc(buflen);
if (buf == NULL)
return NULL;
- wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
- cred->required_roaming_consortium_len);
+ wpa_snprintf_hex(buf, buflen, cred->required_home_ois[0],
+ cred->required_home_ois_len[0]);
+ return buf;
+ }
+
+ if (os_strcmp(var, "home_ois") == 0) {
+ size_t buflen;
+ char *buf, *pos;
+ size_t i;
+
+ if (!cred->num_home_ois)
+ return NULL;
+ buflen = cred->num_home_ois * MAX_ROAMING_CONS_OI_LEN * 2 + 1;
+ buf = os_malloc(buflen);
+ if (!buf)
+ return NULL;
+ pos = buf;
+ for (i = 0; i < cred->num_home_ois; i++) {
+ if (i > 0)
+ *pos++ = ',';
+ pos += wpa_snprintf_hex(
+ pos, buf + buflen - pos,
+ cred->home_ois[i],
+ cred->home_ois_len[i]);
+ }
+ *pos = '\0';
+ return buf;
+ }
+
+ if (os_strcmp(var, "required_home_ois") == 0) {
+ size_t buflen;
+ char *buf, *pos;
+ size_t i;
+
+ if (!cred->num_required_home_ois)
+ return NULL;
+ buflen = cred->num_required_home_ois *
+ MAX_ROAMING_CONS_OI_LEN * 2 + 1;
+ buf = os_malloc(buflen);
+ if (!buf)
+ return NULL;
+ pos = buf;
+ for (i = 0; i < cred->num_required_home_ois; i++) {
+ if (i > 0)
+ *pos++ = ',';
+ pos += wpa_snprintf_hex(
+ pos, buf + buflen - pos,
+ cred->required_home_ois[i],
+ cred->required_home_ois_len[i]);
+ }
+ *pos = '\0';
return buf;
}
@@ -5288,6 +5427,7 @@
{ INT_RANGE(auto_interworking, 0, 1), 0 },
{ INT(okc), 0 },
{ INT(pmf), 0 },
+ { INT_RANGE(sae_check_mfp, 0, 1), 0 },
{ FUNC(sae_groups), 0 },
{ INT_RANGE(sae_pwe, 0, 3), 0 },
{ INT_RANGE(sae_pmkid_in_assoc, 0, 1), 0 },
@@ -5306,9 +5446,9 @@
{ STR(osu_dir), 0 },
{ STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
{ INT(p2p_search_delay), 0},
- { INT(mac_addr), 0 },
+ { INT_RANGE(mac_addr, 0, 2), 0 },
{ INT(rand_addr_lifetime), 0 },
- { INT(preassoc_mac_addr), 0 },
+ { INT_RANGE(preassoc_mac_addr, 0, 2), 0 },
{ INT(key_mgmt_offload), 0},
{ INT(passive_scan), 0 },
{ INT(reassoc_same_bss_optim), 0 },
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 0316e9b..dbb79dd 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -272,36 +272,50 @@
size_t num_domain;
/**
- * roaming_consortium - Roaming Consortium OI
+ * home_ois - Home OIs
*
- * If roaming_consortium_len is non-zero, this field contains the
- * Roaming Consortium OI that can be used to determine which access
- * points support authentication with this credential. This is an
- * alternative to the use of the realm parameter. When using Roaming
- * Consortium to match the network, the EAP parameters need to be
- * pre-configured with the credential since the NAI Realm information
- * may not be available or fetched.
+ * If num_home_ois is non-zero, this field contains the set of Home OIs
+ * that can be use to determine which access points support
+ * authentication with this credential. These are an alternative to the
+ * use of the realm parameter. When using Home OIs to match the network,
+ * the EAP parameters need to be pre-configured with the credentials
+ * since the NAI Realm information may not be available or fetched.
+ * A successful authentication with the access point is possible as soon
+ * as at least one Home OI from the list matches an OI in the Roaming
+ * Consortium list advertised by the access point.
+ * (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
*/
- u8 roaming_consortium[15];
+ u8 home_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
/**
- * roaming_consortium_len - Length of roaming_consortium
+ * home_ois_len - Length of home_ois[i]
*/
- size_t roaming_consortium_len;
+ size_t home_ois_len[MAX_ROAMING_CONS];
/**
- * required_roaming_consortium - Required Roaming Consortium OI
+ * num_home_ois - Number of entries in home_ois
+ */
+ unsigned int num_home_ois;
+
+ /**
+ * required_home_ois - Required Home OI(s)
*
- * 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.
+ * If required_home_ois_len is non-zero, this field contains the set of
+ * Home OI(s) that are required to be advertised by the AP for the
+ * credential to be considered matching.
+ * (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
*/
- u8 required_roaming_consortium[15];
+ u8 required_home_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
/**
- * required_roaming_consortium_len - Length of required_roaming_consortium
+ * required_home_ois_len - Length of required_home_ois
*/
- size_t required_roaming_consortium_len;
+ size_t required_home_ois_len[MAX_ROAMING_CONS];
+
+ /**
+ * num_required_home_ois - Number of entries in required_home_ois
+ */
+ unsigned int num_required_home_ois;
/**
* roaming_consortiums - Roaming Consortium OI(s) memberships
@@ -1253,6 +1267,25 @@
enum mfp_options pmf;
/**
+ * sae_check_mfp - Whether to limit SAE based on PMF capabilities
+ *
+ * With this check SAE key_mgmt will not be selected if PMF is
+ * not enabled.
+ * Scenarios where enabling this check will limit SAE:
+ * 1) ieee8011w=0 is set for the network.
+ * 2) The AP does not have PMF enabled.
+ * 3) ieee8011w for the network is the default(3), pmf=1 is enabled
+ * globally and the device does not support the BIP cipher.
+ *
+ * Useful to allow the BIP cipher check that occurs for ieee80211w=3
+ * and pmf=1 to also avoid using SAE key_mgmt.
+ * Useful when hardware does not support BIP to still to allow
+ * connecting to sae_require_mfp=1 WPA2+WPA3-Personal transition mode
+ *access points by automatically selecting PSK instead of SAE.
+ */
+ int sae_check_mfp;
+
+ /**
* sae_groups - Preference list of enabled groups for SAE
*
* By default (if this parameter is not set), the mandatory group 19
@@ -1268,7 +1301,7 @@
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
- int sae_pwe;
+ enum sae_pwe sae_pwe;
/**
* sae_pmkid_in_assoc - Whether to include PMKID in SAE Assoc Req
@@ -1388,7 +1421,7 @@
* the per-network mac_addr parameter. Global mac_addr=1 can be used to
* change this default behavior.
*/
- int mac_addr;
+ enum wpas_mac_addr_style mac_addr;
/**
* rand_addr_lifetime - Lifetime of random MAC address in seconds
@@ -1402,7 +1435,7 @@
* 1 = use random MAC address
* 2 = like 1, but maintain OUI (with local admin bit set)
*/
- int preassoc_mac_addr;
+ enum wpas_mac_addr_style preassoc_mac_addr;
/**
* key_mgmt_offload - Use key management offload
@@ -1611,7 +1644,7 @@
* 1 = use random MAC address
* 2 = like 1, but maintain OUI (with local admin bit set)
*/
- int gas_rand_mac_addr;
+ enum wpas_mac_addr_style gas_rand_mac_addr;
/**
* dpp_config_processing - How to process DPP configuration
@@ -1836,6 +1869,7 @@
* @name: Name of the configuration (e.g., path and file name for the
* configuration file)
* @cfgp: Pointer to previously allocated configuration data or %NULL if none
+ * @ro: Whether to mark networks from this configuration as read-only
* Returns: Pointer to allocated configuration data or %NULL on failure
*
* This function reads configuration data, parses its contents, and allocates
@@ -1844,7 +1878,8 @@
*
* Each configuration backend needs to implement this function.
*/
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp);
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ bool ro);
/**
* wpa_config_write - Write or update configuration data
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 372a57b..1b8526b 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -53,6 +53,13 @@
ssid->group_cipher &= ~WPA_CIPHER_CCMP;
}
+ if (is_6ghz_freq(ssid->frequency) && ssid->mode == WPAS_MODE_MESH &&
+ ssid->key_mgmt == WPA_KEY_MGMT_NONE) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: key_mgmt for mesh network in 6 GHz should be SAE",
+ line);
+ errors++;
+ }
if (ssid->mode == WPAS_MODE_MESH &&
(ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
ssid->key_mgmt != WPA_KEY_MGMT_SAE)) {
@@ -289,7 +296,8 @@
#endif /* CONFIG_NO_CONFIG_BLOBS */
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ bool ro)
{
FILE *f;
char buf[512], *pos;
@@ -331,6 +339,7 @@
while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
if (os_strcmp(pos, "network={") == 0) {
ssid = wpa_config_read_network(f, &line, id++);
+ ssid->ro = ro;
if (ssid == NULL) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
"parse network block.", line);
@@ -880,6 +889,7 @@
#ifdef CONFIG_HE_OVERRIDES
INT(disable_he);
#endif /* CONFIG_HE_OVERRIDES */
+ INT(disable_eht);
#undef STR
#undef INT
@@ -921,12 +931,6 @@
if (cred->domain_suffix_match)
fprintf(f, "\tdomain_suffix_match=\"%s\"\n",
cred->domain_suffix_match);
- if (cred->roaming_consortium_len) {
- fprintf(f, "\troaming_consortium=");
- for (i = 0; i < cred->roaming_consortium_len; i++)
- fprintf(f, "%02x", cred->roaming_consortium[i]);
- fprintf(f, "\n");
- }
if (cred->eap_method) {
const char *name;
name = eap_get_name(cred->eap_method[0].vendor,
@@ -1002,12 +1006,32 @@
}
}
- if (cred->required_roaming_consortium_len) {
- fprintf(f, "\trequired_roaming_consortium=");
- for (i = 0; i < cred->required_roaming_consortium_len; i++)
- fprintf(f, "%02x",
- cred->required_roaming_consortium[i]);
- fprintf(f, "\n");
+ if (cred->num_home_ois) {
+ size_t j;
+
+ fprintf(f, "\thome_ois=\"");
+ for (i = 0; i < cred->num_home_ois; i++) {
+ if (i > 0)
+ fprintf(f, ",");
+ for (j = 0; j < cred->home_ois_len[i]; j++)
+ fprintf(f, "%02x",
+ cred->home_ois[i][j]);
+ }
+ fprintf(f, "\"\n");
+ }
+
+ if (cred->num_required_home_ois) {
+ size_t j;
+
+ fprintf(f, "\trequired_home_ois=\"");
+ for (i = 0; i < cred->num_required_home_ois; i++) {
+ if (i > 0)
+ fprintf(f, ",");
+ for (j = 0; j < cred->required_home_ois_len[i]; j++)
+ fprintf(f, "%02x",
+ cred->required_home_ois[i][j]);
+ }
+ fprintf(f, "\"\n");
}
if (cred->num_roaming_consortiums) {
@@ -1358,6 +1382,9 @@
if (config->beacon_int)
fprintf(f, "beacon_int=%d\n", config->beacon_int);
+ if (config->sae_check_mfp)
+ fprintf(f, "sae_check_mfp=%d\n", config->sae_check_mfp);
+
if (config->sae_groups) {
int i;
fprintf(f, "sae_groups=");
@@ -1634,7 +1661,8 @@
}
for (ssid = config->ssid; ssid; ssid = ssid->next) {
- if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary ||
+ ssid->ro)
continue; /* do not save temporary networks */
if (wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt) &&
!ssid->psk_set && !ssid->passphrase)
diff --git a/wpa_supplicant/config_none.c b/wpa_supplicant/config_none.c
index 0bc977e..01e7aad 100644
--- a/wpa_supplicant/config_none.c
+++ b/wpa_supplicant/config_none.c
@@ -17,7 +17,8 @@
#include "base64.h"
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ bool ro)
{
struct wpa_config *config;
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index adc1a06..33b46e5 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -46,7 +46,7 @@
#define DEFAULT_MAX_OPER_CHWIDTH -1
/* Consider global sae_pwe for SAE mechanism for PWE derivation */
-#define DEFAULT_SAE_PWE 4
+#define DEFAULT_SAE_PWE SAE_PWE_NOT_SET
struct psk_list_entry {
struct dl_list list;
@@ -70,6 +70,14 @@
SAE_PK_MODE_DISABLED = 2,
};
+enum wpas_mac_addr_style {
+ WPAS_MAC_ADDR_STYLE_NOT_SET = -1,
+ WPAS_MAC_ADDR_STYLE_PERMANENT = 0,
+ WPAS_MAC_ADDR_STYLE_RANDOM = 1,
+ WPAS_MAC_ADDR_STYLE_RANDOM_SAME_OUI = 2,
+ WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS = 3,
+};
+
/**
* struct wpa_ssid - Network configuration data
*
@@ -107,6 +115,21 @@
int id;
/**
+ * ro - Whether a network is declared as read-only
+ *
+ * Every network which is defined in a config file that is passed to
+ * wpa_supplicant using the -I option will be marked as read-only
+ * using this flag. It has the effect that it won't be written to
+ * /etc/wpa_supplicant.conf (from -c argument) if, e.g., wpa_gui tells
+ * the daemon to save all changed configs.
+ *
+ * This is necessary because networks from /etc/wpa_supplicant.conf
+ * have a higher priority and changes from an alternative file would be
+ * silently overwritten without this.
+ */
+ bool ro;
+
+ /**
* priority - Priority group
*
* By default, all networks will get same priority group (0). If some
@@ -837,6 +860,14 @@
struct os_reltime disabled_until;
/**
+ * disabled_due_to - BSSID of the disabling failure
+ *
+ * This identifies the BSS that failed the connection attempt that
+ * resulted in the network being temporarily disabled.
+ */
+ u8 disabled_due_to[ETH_ALEN];
+
+ /**
* parent_cred - Pointer to parent wpa_cred entry
*
* This pointer can be used to delete temporary networks when a wpa_cred
@@ -965,12 +996,21 @@
* 0 = use permanent MAC address
* 1 = use random MAC address for each ESS connection
* 2 = like 1, but maintain OUI (with local admin bit set)
+ * 3 = use dedicated/pregenerated MAC address (see mac_value)
*
* Internally, special value -1 is used to indicate that the parameter
* was not specified in the configuration (i.e., default behavior is
* followed).
*/
- int mac_addr;
+ enum wpas_mac_addr_style mac_addr;
+
+ /**
+ * mac_value - Specific MAC address to be used
+ *
+ * When mac_addr policy is equal to 3 this is the value of the MAC
+ * address that should be used.
+ */
+ u8 mac_value[ETH_ALEN];
/**
* no_auto_peer - Do not automatically peer with compatible mesh peers
@@ -1190,7 +1230,15 @@
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
- int sae_pwe;
+ enum sae_pwe sae_pwe;
+
+ /**
+ * disable_eht - Disable EHT (IEEE 802.11be) for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_eht;
};
#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index b27c6cf..05e6e37 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -446,7 +446,8 @@
}
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ bool ro)
{
TCHAR buf[256];
int errors = 0;
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 7586feb..27397e9 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -833,8 +833,6 @@
wpa_s->sae_commit_override = wpabuf_parse_bin(value);
} else if (os_strcasecmp(cmd, "driver_signal_override") == 0) {
ret = wpas_ctrl_iface_set_dso(wpa_s, value);
- } else if (os_strcasecmp(cmd, "force_hunting_and_pecking_pwe") == 0) {
- wpa_s->force_hunting_and_pecking_pwe = (atoi(value) != 0) ? 1 : 0;
} else if (os_strcasecmp(cmd, "disable_scs_support") == 0) {
wpa_s->disable_scs_support = !!atoi(value);
} else if (os_strcasecmp(cmd, "disable_mscs_support") == 0) {
@@ -1319,6 +1317,7 @@
u8 target_ap[ETH_ALEN];
struct wpa_bss *bss;
const u8 *mdie;
+ bool force = os_strstr(addr, " force") != NULL;
if (hwaddr_aton(addr, target_ap)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
@@ -1334,7 +1333,7 @@
else
mdie = NULL;
- return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie);
+ return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie, force);
}
#endif /* CONFIG_IEEE80211R */
@@ -5002,6 +5001,250 @@
#endif /* CONFIG_FILS */
+static int print_rnr(struct wpa_bss *bss, char *pos, char *end)
+{
+ char *start = pos;
+ const u8 *ie, *ie_end;
+ unsigned int n = 0;
+ int ret;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
+ if (!ie)
+ return 0;
+
+ ie_end = ie + 2 + ie[1];
+ ie += 2;
+
+ while (ie < ie_end) {
+ const struct ieee80211_neighbor_ap_info *info =
+ (const struct ieee80211_neighbor_ap_info *) ie;
+ const u8 *tbtt_start;
+ size_t left = ie_end - ie;
+
+ if (left < sizeof(struct ieee80211_neighbor_ap_info))
+ return 0;
+
+ left -= sizeof(struct ieee80211_neighbor_ap_info);
+ if (left < info->tbtt_info_len)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos,
+ "ap_info[%u]: tbtt_info: hdr=0x%x, len=%u, op_c=%u, channel=%u, ",
+ n, *ie, info->tbtt_info_len,
+ info->op_class, info->channel);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie += sizeof(struct ieee80211_neighbor_ap_info);
+ tbtt_start = ie;
+ if (info->tbtt_info_len >= 1) {
+ ret = os_snprintf(pos, end - pos,
+ "tbtt_offset=%u, ", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie++;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 7) {
+ ret = os_snprintf(pos, end - pos,
+ "bssid=" MACSTR ", ",
+ MAC2STR(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie += ETH_ALEN;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 11) {
+ ret = os_snprintf(pos, end - pos,
+ "short SSID=0x%x, ",
+ WPA_GET_LE32(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie += 4;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 12) {
+ ret = os_snprintf(pos, end - pos,
+ "bss_params=0x%x, ", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie++;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 13) {
+ ret = os_snprintf(pos, end - pos,
+ "PSD=0x%x, ", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie++;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 16) {
+ ret = os_snprintf(pos, end - pos,
+ "mld ID=%u, link ID=%u",
+ *ie, *(ie + 1) & 0xF);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie += 3;
+ pos += ret;
+ }
+
+ ie = tbtt_start + info->tbtt_info_len;
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ n++;
+ }
+
+ return pos - start;
+}
+
+
+static int print_ml(struct wpa_bss *bss, char *pos, char *end)
+{
+ const struct ieee80211_eht_ml *ml;
+ char *start = pos;
+ const u8 *ie, *ie_end;
+ u16 ml_control;
+ u8 common_info_length;
+ int ret;
+
+ ie = get_ml_ie(wpa_bss_ie_ptr(bss), bss->ie_len,
+ MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!ie)
+ return 0;
+
+ ie_end = ie + 2 + ie[1];
+ ie += 3;
+ ml = (const struct ieee80211_eht_ml *) ie;
+
+ /* control + common info length + MLD MAC Address */
+ if (ie_end - ie < 2 + 1 + ETH_ALEN)
+ return 0;
+
+ ml_control = le_to_host16(ml->ml_control);
+
+ common_info_length = *(ie + 2);
+ ret = os_snprintf(pos, end - pos,
+ "multi-link: control=0x%x, common info len=%u",
+ ml_control, common_info_length);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie += 2;
+ if (ie_end - ie < common_info_length)
+ return 0;
+
+ ie++;
+ common_info_length--;
+
+ if (common_info_length < ETH_ALEN)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", MLD addr=" MACSTR, MAC2STR(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie += ETH_ALEN;
+ common_info_length -= ETH_ALEN;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+ if (common_info_length < 1)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", link ID=%u", *ie & 0x0f);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie++;
+ common_info_length--;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+ if (common_info_length < 1)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos,
+ ", BSS change parameters=0x%x", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie++;
+ common_info_length--;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+ if (common_info_length < 2)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", MSD Info=0x%x",
+ WPA_GET_LE16(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 2;
+ common_info_length -= 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ if (common_info_length < 2)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", EML capabilities=0x%x",
+ WPA_GET_LE16(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 2;
+ common_info_length -= 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+ if (common_info_length < 2)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", MLD capabilities=0x%x",
+ WPA_GET_LE16(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 2;
+ common_info_length -= 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID) {
+ if (common_info_length < 1)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", MLD ID=0x%x\n", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 1;
+ common_info_length--;
+ }
+
+ return pos - start;
+}
+
+
static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
unsigned long mask, char *buf, size_t buflen)
{
@@ -5452,6 +5695,12 @@
pos += ret;
}
+ if (mask & WPA_BSS_MASK_RNR)
+ pos += print_rnr(bss, pos, end);
+
+ if (mask & WPA_BSS_MASK_ML)
+ pos += print_ml(bss, pos, end);
+
if (mask & WPA_BSS_MASK_DELIM) {
ret = os_snprintf(pos, end - pos, "====\n");
if (os_snprintf_error(end - pos, ret))
@@ -5657,23 +5906,23 @@
{
wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
/* MLME-DELETEKEYS.request */
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
- 0, KEY_FLAG_PAIRWISE);
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0,
+ NULL, 0, KEY_FLAG_PAIRWISE);
if (wpa_sm_ext_key_id(wpa_s->wpa))
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 1, 0,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 1, 0,
NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
/* MLME-SETPROTECTION.request(None) */
wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
@@ -8102,9 +8351,9 @@
pos = buf;
end = buf + buflen;
- ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n"
+ ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%lu\n"
"NOISE=%d\nFREQUENCY=%u\n",
- si.current_signal, si.current_txrate / 1000,
+ si.data.signal, si.data.current_tx_rate / 1000,
si.current_noise, si.frequency);
if (os_snprintf_error(end - pos, ret))
return -1;
@@ -8134,17 +8383,18 @@
pos += ret;
}
- if (si.avg_signal) {
+ if (si.data.avg_signal) {
ret = os_snprintf(pos, end - pos,
- "AVG_RSSI=%d\n", si.avg_signal);
+ "AVG_RSSI=%d\n", si.data.avg_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
- if (si.avg_beacon_signal) {
+ if (si.data.avg_beacon_signal) {
ret = os_snprintf(pos, end - pos,
- "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
+ "AVG_BEACON_RSSI=%d\n",
+ si.data.avg_beacon_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
@@ -9998,14 +10248,15 @@
/* 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,
+ if (wpa_drv_set_key(wpa_s, -1, 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,
KEY_FLAG_PAIRWISE_RX_TX) < 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,
+ return wpa_drv_set_key(wpa_s, -1, 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,
KEY_FLAG_PAIRWISE_RX_TX);
@@ -10700,6 +10951,7 @@
entry->reauth_time = now.sec + reauth_time;
entry->network_ctx = ssid;
+ os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
entry->external = true;
@@ -11564,10 +11816,10 @@
continue;
ret = os_snprintf(pos, end - pos,
- "LINK_ID=%d\nRSSI=%d\nLINKSPEED=%d\n"
+ "LINK_ID=%d\nRSSI=%d\nLINKSPEED=%lu\n"
"NOISE=%d\nFREQUENCY=%u\n",
- i, mlo_si.links[i].current_signal,
- mlo_si.links[i].current_txrate / 1000,
+ i, mlo_si.links[i].data.signal,
+ mlo_si.links[i].data.current_tx_rate / 1000,
mlo_si.links[i].current_noise,
mlo_si.links[i].frequency);
if (os_snprintf_error(end - pos, ret))
@@ -11599,19 +11851,19 @@
pos += ret;
}
- if (mlo_si.links[i].avg_signal) {
+ if (mlo_si.links[i].data.avg_signal) {
ret = os_snprintf(pos, end - pos,
"AVG_RSSI=%d\n",
- mlo_si.links[i].avg_signal);
+ mlo_si.links[i].data.avg_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
- if (mlo_si.links[i].avg_beacon_signal) {
- ret = os_snprintf(pos, end - pos,
- "AVG_BEACON_RSSI=%d\n",
- mlo_si.links[i].avg_beacon_signal);
+ if (mlo_si.links[i].data.avg_beacon_signal) {
+ ret = os_snprintf(
+ pos, end - pos, "AVG_BEACON_RSSI=%d\n",
+ mlo_si.links[i].data.avg_beacon_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
@@ -12818,8 +13070,11 @@
return 0;
fail:
- if (create_iface)
+ if (create_iface) {
+ /* wpa_supplicant does not create multi-BSS AP, so collapse to
+ * WPA_IF_STATION to avoid unwanted clean up in the driver. */
wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
+ }
return -1;
}
@@ -12841,6 +13096,8 @@
if (!ret && delete_iface) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
cmd);
+ /* wpa_supplicant does not create multi-BSS AP, so collapse to
+ * WPA_IF_STATION to avoid unwanted clean up in the driver. */
ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
}
return ret;
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 1178f40..1cbf7fa 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -337,9 +337,6 @@
else
reply_len = 2;
} else {
- sockaddr_print(wpas_ctrl_cmd_debug_level(buf),
- "Control interface recv command from:",
- &from, fromlen);
reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
&reply_len);
}
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index e4e9b8d..0e47534 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -299,6 +299,25 @@
/**
+ * Add a 64-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ * wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 64-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+ const char *key,
+ const dbus_uint64_t value)
+{
+ return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64,
+ &value);
+}
+
+
+/**
* Add a DBus object path entry to the dict.
*
* @param iter_dict A valid DBusMessageIter returned from
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index 94a0efd..44685ea 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -46,6 +46,10 @@
const char *key,
const dbus_uint32_t value);
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+ const char *key,
+ const dbus_uint64_t value);
+
dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
const char *key,
const char *value);
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 9279ae4..9c23588 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -2344,6 +2344,12 @@
case WPAS_DBUS_PROP_BSS_TM_STATUS:
prop = "BSSTMStatus";
break;
+ case WPAS_DBUS_PROP_MAC_ADDRESS:
+ prop = "MACAddress";
+ break;
+ case WPAS_DBUS_PROP_SIGNAL_CHANGE:
+ prop = "SignalChange";
+ break;
default:
wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
__func__, property);
@@ -3939,6 +3945,11 @@
wpas_dbus_setter_mac_address_randomization_mask,
NULL
},
+ { "MACAddress", WPAS_DBUS_NEW_IFACE_INTERFACE, "ay",
+ wpas_dbus_getter_mac_address,
+ NULL,
+ NULL,
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -4521,6 +4532,11 @@
NULL,
NULL
},
+ { "SignalChange", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
+ wpas_dbus_getter_signal_change,
+ NULL,
+ NULL
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 26bdcb5..ca8506f 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -38,6 +38,8 @@
WPAS_DBUS_PROP_ROAM_COMPLETE,
WPAS_DBUS_PROP_SESSION_LENGTH,
WPAS_DBUS_PROP_BSS_TM_STATUS,
+ WPAS_DBUS_PROP_MAC_ADDRESS,
+ WPAS_DBUS_PROP_SIGNAL_CHANGE,
};
enum wpas_dbus_bss_prop {
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 26f7738..67ce970 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -152,7 +152,7 @@
#ifdef CONFIG_INTERWORKING
"roaming_consortium", "required_roaming_consortium",
#endif /* CONFIG_INTERWORKING */
- NULL
+ "mac_value", NULL
};
static dbus_bool_t should_quote_opt(const char *key)
@@ -206,6 +206,8 @@
struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
DBusMessageIter iter_dict;
char *value = NULL;
+ bool mac_addr3_set = false;
+ bool mac_value_set = false;
if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
return FALSE;
@@ -315,12 +317,30 @@
else if (os_strcmp(entry.key, "priority") == 0)
wpa_config_update_prio_list(wpa_s->conf);
+ /*
+ * MAC address policy "3" needs to come with mac_value in
+ * the message so make sure that it is present (checked after
+ * the loop - here we just note what has been supplied).
+ */
+ if (os_strcmp(entry.key, "mac_addr") == 0 &&
+ atoi(value) == 3)
+ mac_addr3_set = true;
+ if (os_strcmp(entry.key, "mac_value") == 0)
+ mac_value_set = true;
+
skip_update:
os_free(value);
value = NULL;
wpa_dbus_dict_entry_clear(&entry);
}
+ if (mac_addr3_set && !mac_value_set) {
+ wpa_printf(MSG_INFO, "dbus: Invalid mac_addr policy config");
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "Invalid mac_addr policy config");
+ return FALSE;
+ }
+
return TRUE;
error:
@@ -332,6 +352,118 @@
}
+static int set_cred_property(struct wpa_cred *cred,
+ struct wpa_dbus_dict_entry *entry)
+{
+ size_t size;
+ int ret;
+ char *value;
+
+ if (entry->type == DBUS_TYPE_ARRAY &&
+ entry->array_type == DBUS_TYPE_STRING) {
+ dbus_uint32_t i;
+
+ if (entry->array_len <= 0)
+ return -1;
+
+ for (i = 0; i < entry->array_len; i++) {
+ if (should_quote_opt(entry->key)) {
+ size = os_strlen(entry->strarray_value[i]);
+
+ size += 3;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = os_snprintf(value, size, "\"%s\"",
+ entry->strarray_value[i]);
+ if (os_snprintf_error(size, ret)) {
+ os_free(value);
+ return -1;
+ }
+ } else {
+ value = os_strdup(entry->strarray_value[i]);
+ if (!value)
+ return -1;
+ }
+
+ ret = wpa_config_set_cred(cred, entry->key, value, 0);
+ os_free(value);
+ if (ret < 0)
+ return -1;
+ }
+ return 0;
+ }
+
+ if (entry->type == DBUS_TYPE_ARRAY &&
+ entry->array_type == DBUS_TYPE_BYTE) {
+ if (entry->array_len <= 0)
+ return -1;
+
+ size = entry->array_len * 2 + 1;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = wpa_snprintf_hex(value, size,
+ (u8 *) entry->bytearray_value,
+ entry->array_len);
+ if (ret <= 0) {
+ os_free(value);
+ return -1;
+ }
+ } else if (entry->type == DBUS_TYPE_STRING) {
+ if (should_quote_opt(entry->key)) {
+ size = os_strlen(entry->str_value);
+
+ size += 3;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = os_snprintf(value, size, "\"%s\"",
+ entry->str_value);
+ if (os_snprintf_error(size, ret)) {
+ os_free(value);
+ return -1;
+ }
+ } else {
+ value = os_strdup(entry->str_value);
+ if (!value)
+ return -1;
+ }
+ } else if (entry->type == DBUS_TYPE_UINT32) {
+ size = 50;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = os_snprintf(value, size, "%u", entry->uint32_value);
+ if (os_snprintf_error(size, ret)) {
+ os_free(value);
+ return -1;
+ }
+ } else if (entry->type == DBUS_TYPE_INT32) {
+ size = 50;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = os_snprintf(value, size, "%d", entry->int32_value);
+ if (os_snprintf_error(size, ret)) {
+ os_free(value);
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+
+ ret = wpa_config_set_cred(cred, entry->key, value, 0);
+ os_free(value);
+ return ret;
+}
+
+
/**
* set_cred_properties - Set the properties of a configured credential
* @wpa_s: wpa_supplicant structure for a network interface
@@ -348,91 +480,28 @@
{
struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
DBusMessageIter iter_dict;
- char *value = NULL;
if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
return FALSE;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
- size_t size = 50;
- int ret;
+ int res;
- if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
- goto error;
-
- value = NULL;
- if (entry.type == DBUS_TYPE_ARRAY &&
- entry.array_type == DBUS_TYPE_BYTE) {
- if (entry.array_len <= 0)
- goto error;
-
- size = entry.array_len * 2 + 1;
- value = os_zalloc(size);
- if (!value)
- goto error;
-
- ret = wpa_snprintf_hex(value, size,
- (u8 *) entry.bytearray_value,
- entry.array_len);
- if (ret <= 0)
- goto error;
- } else if (entry.type == DBUS_TYPE_STRING) {
- if (should_quote_opt(entry.key)) {
- size = os_strlen(entry.str_value);
-
- size += 3;
- value = os_zalloc(size);
- if (!value)
- goto error;
-
- ret = os_snprintf(value, size, "\"%s\"",
- entry.str_value);
- if (os_snprintf_error(size, ret))
- goto error;
- } else {
- value = os_strdup(entry.str_value);
- if (!value)
- goto error;
- }
- } else if (entry.type == DBUS_TYPE_UINT32) {
- value = os_zalloc(size);
- if (!value)
- goto error;
-
- ret = os_snprintf(value, size, "%u",
- entry.uint32_value);
- if (os_snprintf_error(size, ret))
- goto error;
- } else if (entry.type == DBUS_TYPE_INT32) {
- value = os_zalloc(size);
- if (!value)
- goto error;
-
- ret = os_snprintf(value, size, "%d",
- entry.int32_value);
- if (os_snprintf_error(size, ret))
- goto error;
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+ res = -1;
} else {
- goto error;
+ res = set_cred_property(cred, &entry);
+ wpa_dbus_dict_entry_clear(&entry);
}
- ret = wpa_config_set_cred(cred, entry.key, value, 0);
- if (ret < 0)
- goto error;
-
- os_free(value);
- value = NULL;
- wpa_dbus_dict_entry_clear(&entry);
+ if (res < 0) {
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
+ }
}
return TRUE;
-
-error:
- os_free(value);
- wpa_dbus_dict_entry_clear(&entry);
- dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
- "invalid message format");
- return FALSE;
}
@@ -704,6 +773,9 @@
char *ifname = NULL;
char *confname = NULL;
char *bridge_ifname = NULL;
+ bool create_iface = false;
+ u8 *if_addr = NULL;
+ enum wpa_driver_if_type if_type = WPA_IF_STATION;
dbus_message_iter_init(message, &iter);
@@ -740,6 +812,33 @@
wpa_dbus_dict_entry_clear(&entry);
if (bridge_ifname == NULL)
goto oom;
+ } else if (os_strcmp(entry.key, "Create") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ create_iface = entry.bool_value;
+ wpa_dbus_dict_entry_clear(&entry);
+ } else if (os_strcmp(entry.key, "Type") == 0 &&
+ entry.type == DBUS_TYPE_STRING) {
+ if (os_strcmp(entry.str_value, "sta") == 0) {
+ if_type = WPA_IF_STATION;
+ } else if (os_strcmp(entry.str_value, "ap") == 0) {
+ if_type = WPA_IF_AP_BSS;
+ } else {
+ wpa_dbus_dict_entry_clear(&entry);
+ goto error;
+ }
+ wpa_dbus_dict_entry_clear(&entry);
+ } else if (os_strcmp(entry.key, "Address") == 0 &&
+ entry.type == DBUS_TYPE_STRING) {
+ if_addr = os_malloc(ETH_ALEN);
+ if (if_addr == NULL) {
+ wpa_dbus_dict_entry_clear(&entry);
+ goto oom;
+ }
+ if (hwaddr_aton(entry.str_value, if_addr)) {
+ wpa_dbus_dict_entry_clear(&entry);
+ goto error;
+ }
+ wpa_dbus_dict_entry_clear(&entry);
} else {
wpa_dbus_dict_entry_clear(&entry);
goto error;
@@ -761,6 +860,23 @@
struct wpa_supplicant *wpa_s;
struct wpa_interface iface;
+ if (create_iface) {
+ u8 mac_addr[ETH_ALEN];
+
+ wpa_printf(MSG_DEBUG,
+ "%s[dbus]: creating an interface '%s'",
+ __func__, ifname);
+ if (!global->ifaces ||
+ wpa_drv_if_add(global->ifaces, if_type, ifname,
+ if_addr, NULL, NULL, mac_addr,
+ NULL) < 0) {
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "interface creation failed.");
+ goto out;
+ }
+ }
+
os_memset(&iface, 0, sizeof(iface));
iface.driver = driver;
iface.ifname = ifname;
@@ -771,6 +887,7 @@
if (wpa_s && wpa_s->dbus_new_path) {
const char *path = wpa_s->dbus_new_path;
+ wpa_s->added_vif = create_iface;
reply = dbus_message_new_method_return(message);
dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
&path, DBUS_TYPE_INVALID);
@@ -778,6 +895,13 @@
reply = wpas_dbus_error_unknown_error(
message,
"wpa_supplicant couldn't grab this interface.");
+ if (create_iface) {
+ /* wpa_supplicant does not create multi-BSS AP,
+ * so collapse to WPA_IF_STATION to avoid
+ * unwanted clean up in the driver. */
+ wpa_drv_if_remove(global->ifaces,
+ WPA_IF_STATION, ifname);
+ }
}
}
@@ -786,6 +910,7 @@
os_free(ifname);
os_free(confname);
os_free(bridge_ifname);
+ os_free(if_addr);
return reply;
error:
@@ -814,19 +939,38 @@
struct wpa_supplicant *wpa_s;
char *path;
DBusMessage *reply = NULL;
+ bool delete_iface;
dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID);
wpa_s = get_iface_by_dbus_path(global, path);
- if (wpa_s == NULL)
+ if (!wpa_s) {
reply = wpas_dbus_error_iface_unknown(message);
- else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
+ goto out;
+ }
+ delete_iface = wpa_s->added_vif;
+ if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
reply = wpas_dbus_error_unknown_error(
message,
"wpa_supplicant couldn't remove this interface.");
+ goto out;
}
+ if (delete_iface) {
+ wpa_printf(MSG_DEBUG, "%s[dbus]: deleting the interface '%s'",
+ __func__, wpa_s->ifname);
+ /* wpa_supplicant does not create multi-BSS AP, so collapse to
+ * WPA_IF_STATION to avoid unwanted clean up in the driver. */
+ if (wpa_drv_if_remove(global->ifaces, WPA_IF_STATION,
+ wpa_s->ifname)) {
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "wpa_supplicant couldn't delete this interface.");
+ }
+ }
+
+out:
return reply;
}
@@ -1826,7 +1970,7 @@
{
struct wpa_signal_info si;
DBusMessage *reply = NULL;
- DBusMessageIter iter, iter_dict, variant_iter;
+ DBusMessageIter iter;
int ret;
ret = wpa_drv_signal_poll(wpa_s, &si);
@@ -1841,31 +1985,7 @@
dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- "a{sv}", &variant_iter) ||
- !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
- !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
- si.current_signal) ||
- !wpa_dbus_dict_append_int32(&iter_dict, "linkspeed",
- si.current_txrate / 1000) ||
- !wpa_dbus_dict_append_int32(&iter_dict, "noise",
- si.current_noise) ||
- !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
- si.frequency) ||
- (si.chanwidth != CHAN_WIDTH_UNKNOWN &&
- !wpa_dbus_dict_append_string(
- &iter_dict, "width",
- channel_width_to_string(si.chanwidth))) ||
- (si.center_frq1 > 0 && si.center_frq2 > 0 &&
- (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
- si.center_frq1) ||
- !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
- si.center_frq2))) ||
- (si.avg_signal &&
- !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
- si.avg_signal)) ||
- !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
- !dbus_message_iter_close_container(&iter, &variant_iter))
+ if (wpas_dbus_new_from_signal_information(&iter, &si) != 0)
goto nomem;
return reply;
@@ -4324,6 +4444,7 @@
const char *new_value = NULL;
char buf[250];
size_t combined_len;
+ int wpa_sm_param;
int ret;
if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
@@ -4342,6 +4463,35 @@
if (!new_value[0])
new_value = "NULL";
+ wpa_sm_param = -1;
+ if (os_strcmp(property_desc->data, "dot11RSNAConfigPMKLifetime") == 0)
+ wpa_sm_param = RSNA_PMK_LIFETIME;
+ else if (os_strcmp(property_desc->data,
+ "dot11RSNAConfigPMKReauthThreshold") == 0)
+ wpa_sm_param = RSNA_PMK_REAUTH_THRESHOLD;
+ else if (os_strcmp(property_desc->data, "dot11RSNAConfigSATimeout") == 0)
+ wpa_sm_param = RSNA_SA_TIMEOUT;
+
+ if (wpa_sm_param != -1) {
+ char *end;
+ int val;
+
+ val = strtol(new_value, &end, 0);
+ if (*end) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+ "Invalid value for property %s",
+ property_desc->dbus_property);
+ return FALSE;
+ }
+
+ if (wpa_sm_set_param(wpa_s->wpa, wpa_sm_param, val)) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+ "Failed to apply interface property %s",
+ property_desc->dbus_property);
+ return FALSE;
+ }
+ }
+
ret = os_snprintf(buf, combined_len, "%s=%s", property_desc->data,
new_value);
if (os_snprintf_error(combined_len, ret)) {
@@ -4600,6 +4750,27 @@
/**
+ * wpas_dbus_getter_mac_address - Get MAC address of an interface
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: a list of stations
+ *
+ * Getter for "MACAddress" property.
+ */
+dbus_bool_t wpas_dbus_getter_mac_address(
+ 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_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ wpa_s->own_addr, ETH_ALEN,
+ error);
+}
+
+
+/**
* wpas_dbus_getter_sta_address - Return the address of a connected station
* @iter: Pointer to incoming dbus message iter
* @error: Location to store error on failure
@@ -5945,3 +6116,28 @@
}
#endif /* CONFIG_MESH */
+
+
+/**
+ * wpas_dbus_getter_signal_change - Get signal change
+ * @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 "SignalChange" property.
+ */
+dbus_bool_t wpas_dbus_getter_signal_change(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct wpa_signal_info si = wpa_s->last_signal_info;
+
+ if (wpas_dbus_new_from_signal_information(iter, &si) != 0) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: error constructing reply", __func__);
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index a421083..97fa337 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -196,6 +196,7 @@
DECLARE_ACCESSOR(wpas_dbus_getter_stas);
DECLARE_ACCESSOR(wpas_dbus_getter_mac_address_randomization_mask);
DECLARE_ACCESSOR(wpas_dbus_setter_mac_address_randomization_mask);
+DECLARE_ACCESSOR(wpas_dbus_getter_mac_address);
DECLARE_ACCESSOR(wpas_dbus_getter_sta_address);
DECLARE_ACCESSOR(wpas_dbus_getter_sta_aid);
DECLARE_ACCESSOR(wpas_dbus_getter_sta_caps);
@@ -246,6 +247,8 @@
DECLARE_ACCESSOR(wpas_dbus_getter_mesh_peers);
DECLARE_ACCESSOR(wpas_dbus_getter_mesh_group);
+DECLARE_ACCESSOR(wpas_dbus_getter_signal_change);
+
DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
struct wpa_supplicant *wpa_s);
DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index d9009ba..e21f912 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
+#include "drivers/driver.h"
#include "dbus_common.h"
#include "dbus_common_i.h"
#include "dbus_new.h"
@@ -1023,3 +1024,131 @@
}
return NULL;
}
+
+
+/**
+ * wpas_dbus_new_from_signal_information - Adds a wpa_signal_info
+ * to a DBusMessage.
+ * @msg: Pointer to message to append fields to
+ * @si: Pointer to wpa_signal_info to add to the message
+ * Returns: 0 on success, otherwise, an errorcode
+ *
+ * Adds all the pertinent fields from a wpa_signal_info to a DBusMessage.
+ * The same logic is useful in both responding to signal_poll calls, and
+ * sending signal_change signals.
+ */
+int wpas_dbus_new_from_signal_information(DBusMessageIter *iter,
+ struct wpa_signal_info *si)
+{
+ DBusMessageIter iter_dict, variant_iter;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ "a{sv}", &variant_iter) ||
+ !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
+ !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
+ si->data.signal) ||
+ !wpa_dbus_dict_append_uint32(&iter_dict, "linkspeed",
+ si->data.current_tx_rate / 1000) ||
+ !wpa_dbus_dict_append_int32(&iter_dict, "noise",
+ si->current_noise) ||
+ !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
+ si->frequency) ||
+ (si->chanwidth != CHAN_WIDTH_UNKNOWN &&
+ !wpa_dbus_dict_append_string(
+ &iter_dict, "width",
+ channel_width_to_string(si->chanwidth))) ||
+ (si->center_frq1 > 0 && si->center_frq2 > 0 &&
+ (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
+ si->center_frq1) ||
+ !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
+ si->center_frq2))) ||
+ (si->data.avg_signal &&
+ !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
+ si->data.avg_signal)) ||
+ (si->data.rx_bytes &&
+ !wpa_dbus_dict_append_uint64(&iter_dict, "rx-bytes",
+ si->data.rx_bytes)) ||
+ (si->data.tx_bytes &&
+ !wpa_dbus_dict_append_uint64(&iter_dict, "tx-bytes",
+ si->data.tx_bytes)) ||
+ (si->data.rx_packets &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-packets",
+ si->data.rx_packets)) ||
+ (si->data.tx_packets &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-packets",
+ si->data.tx_packets)) ||
+ (si->data.beacons_count &&
+ !wpa_dbus_dict_append_uint64(&iter_dict, "beacons",
+ si->data.beacons_count)) ||
+ (si->data.current_rx_rate &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "linkrxspeed",
+ si->data.current_rx_rate)) ||
+ (si->data.current_rx_rate &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "linktxspeed",
+ si->data.current_tx_rate)) ||
+ (si->data.tx_retry_failed &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "retries-failed",
+ si->data.tx_retry_failed)) ||
+ (si->data.tx_retry_count &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "retries",
+ si->data.tx_retry_count)) ||
+ (si->data.last_ack_rssi &&
+ !wpa_dbus_dict_append_int32(&iter_dict, "last-ack-rssi",
+ si->data.last_ack_rssi)) ||
+ (si->data.fcs_error_count &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "fcs-errors",
+ si->data.fcs_error_count)) ||
+ (si->data.beacon_loss_count &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "beacon-losses",
+ si->data.beacon_loss_count)) ||
+ (si->data.expected_throughput &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "expected-throughput",
+ si->data.expected_throughput)) ||
+ (si->data.rx_drop_misc &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-drop-misc",
+ si->data.rx_drop_misc)) ||
+ (si->data.rx_mpdus &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-mpdus",
+ si->data.rx_mpdus)) ||
+ (si->data.rx_hemcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-he-mcs",
+ si->data.rx_hemcs)) ||
+ (si->data.tx_hemcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-he-mcs",
+ si->data.tx_hemcs)) ||
+ (si->data.rx_vhtmcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-vht-mcs",
+ si->data.rx_vhtmcs)) ||
+ (si->data.tx_vhtmcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-vht-mcs",
+ si->data.tx_vhtmcs)) ||
+ (si->data.rx_mcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-mcs",
+ si->data.rx_mcs)) ||
+ (si->data.tx_mcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-mcs",
+ si->data.tx_mcs)) ||
+ (si->data.rx_he_nss &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-he-nss",
+ si->data.rx_he_nss)) ||
+ (si->data.tx_he_nss &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-he-nss",
+ si->data.tx_he_nss)) ||
+ (si->data.rx_vht_nss &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-vht-nss",
+ si->data.rx_vht_nss)) ||
+ (si->data.tx_vht_nss &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-vht-nss",
+ si->data.tx_vht_nss)) ||
+ (si->data.avg_beacon_signal &&
+ !wpa_dbus_dict_append_int32(&iter_dict, "avg-beacon-rssi",
+ si->data.avg_beacon_signal)) ||
+ (si->data.avg_ack_signal &&
+ !wpa_dbus_dict_append_int32(&iter_dict, "avg-ack-rssi",
+ si->data.avg_ack_signal)) ||
+ !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+ !dbus_message_iter_close_container(iter, &variant_iter))
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
index 7b63b28..c8d44a0 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -12,6 +12,8 @@
#include <dbus/dbus.h>
+struct wpa_signal_info;
+
typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message,
void *user_data);
typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
@@ -151,4 +153,7 @@
const char *fallback_name,
const char *fallback_string);
+int wpas_dbus_new_from_signal_information(DBusMessageIter *iter,
+ struct wpa_signal_info *si);
+
#endif /* WPA_DBUS_CTRL_H */
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index d13a8d0..895d5fa 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -1353,7 +1353,10 @@
if (dpp_akm_sae(conf->akm))
ssid->key_mgmt |= WPA_KEY_MGMT_SAE |
WPA_KEY_MGMT_FT_SAE;
- ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+ if (dpp_akm_psk(conf->akm))
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+ else
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
if (conf->passphrase[0]) {
if (wpa_config_set_quoted(ssid, "psk",
conf->passphrase) < 0)
@@ -1923,7 +1926,7 @@
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);
+ dpp_notify_auth_success(wpa_s->dpp_auth, initiator);
wpas_notify_dpp_auth_success(wpa_s);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
@@ -2301,6 +2304,7 @@
u16 r_bootstrap_len;
struct dpp_bootstrap_info *peer_bi;
struct dpp_authentication *auth;
+ unsigned int wait_time, max_wait_time;
if (!wpa_s->dpp)
return;
@@ -2332,6 +2336,9 @@
return;
}
+ wpa_printf(MSG_DEBUG, "DPP: Start Authentication exchange with " MACSTR
+ " based on the received Presence Announcement",
+ MAC2STR(src));
auth = dpp_auth_init(wpa_s->dpp, wpa_s, peer_bi, NULL,
DPP_CAPAB_CONFIGURATOR, freq, NULL, 0);
if (!auth)
@@ -2348,6 +2355,13 @@
* MAC address information from the bootstrapping information. */
os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+ 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;
+ wpas_dpp_stop_listen_for_tx(wpa_s, freq, wait_time);
+
wpa_s->dpp_auth = auth;
if (wpas_dpp_auth_init_next(wpa_s) < 0) {
dpp_auth_deinit(wpa_s->dpp_auth);
@@ -2642,6 +2656,8 @@
return;
}
+ os_memset(&intro, 0, sizeof(intro));
+
trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
&trans_id_len);
if (!trans_id || trans_id_len != 1) {
@@ -2693,7 +2709,7 @@
ssid->dpp_netaccesskey_len,
ssid->dpp_csign,
ssid->dpp_csign_len,
- connector, connector_len, &expiry);
+ connector, connector_len, &expiry, NULL);
if (res != DPP_STATUS_OK) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in failure");
@@ -2709,6 +2725,7 @@
if (!entry)
goto fail;
os_memcpy(entry->aa, src, ETH_ALEN);
+ os_memcpy(entry->spa, wpa_s->own_addr, 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;
@@ -2752,7 +2769,7 @@
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
fail:
- os_memset(&intro, 0, sizeof(intro));
+ dpp_peer_intro_deinit(&intro);
}
@@ -3845,7 +3862,7 @@
ssid->dpp_netaccesskey_len,
ssid->dpp_csign,
ssid->dpp_csign_len,
- connector, connector_len, &expiry);
+ connector, connector_len, &expiry, NULL);
if (res != DPP_STATUS_OK) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in failure");
@@ -3871,6 +3888,7 @@
goto fail;
entry->dpp_pfs = peer_version >= 2;
os_memcpy(entry->aa, src, ETH_ALEN);
+ os_memcpy(entry->spa, wpa_s->own_addr, 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;
@@ -4087,7 +4105,7 @@
* TX status handler, but since there was no such handler call
* yet, simply send out the event message and proceed with
* exchange. */
- wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=1");
+ dpp_notify_auth_success(auth, 1);
wpa_s->dpp_auth_ok_on_ack = 0;
}
@@ -4380,7 +4398,7 @@
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
!(ied.key_mgmt & WPA_KEY_MGMT_DPP))
return 0; /* AP does not support DPP AKM - continue */
- if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid))
+ if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, wpa_s->own_addr, ssid))
return 0; /* PMKSA exists for DPP AKM - continue */
if (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 6be117c..5dd2a51 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -143,7 +143,7 @@
return -1;
}
-static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
+static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, int link_id,
enum wpa_alg alg, const u8 *addr,
int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
@@ -163,6 +163,7 @@
params.key = key;
params.key_len = key_len;
params.key_flag = key_flag;
+ params.link_id = link_id;
if (alg != WPA_ALG_NONE) {
/* keyidx = 1 can be either a broadcast or--with
@@ -406,20 +407,10 @@
return 0;
}
-static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
- unsigned int freq,
- unsigned int wait,
- const u8 *dst, const u8 *src,
- const u8 *bssid,
- const u8 *data, size_t data_len,
- int no_cck)
-{
- if (wpa_s->driver->send_action)
- return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
- wait, dst, src, bssid,
- data, data_len, no_cck);
- return -1;
-}
+int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *src,
+ const u8 *bssid, const u8 *data, size_t data_len,
+ int no_cck);
static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s)
{
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index efec31c..9641062 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -1472,7 +1472,7 @@
dl_list_init(&wpa_s.bss);
dl_list_init(&wpa_s.bss_id);
if (conf)
- wpa_s.conf = wpa_config_read(conf, NULL);
+ wpa_s.conf = wpa_config_read(conf, NULL, false);
else
wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL);
if (wpa_s.conf == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 6c82b66..3275b64 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -140,7 +140,7 @@
struct wpa_bss *bss = NULL;
struct wpa_ssid *ssid = wpa_s->current_ssid;
- if (ssid->ssid_len > 0)
+ if (ssid && ssid->ssid_len > 0)
bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
if (!bss)
bss = wpa_bss_get_bssid(wpa_s, bssid);
@@ -183,7 +183,8 @@
}
-static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
+static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
{
struct wpa_ssid *ssid, *old_ssid;
u8 drv_ssid[SSID_MAX_LEN];
@@ -256,8 +257,15 @@
if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
u8 wpa_ie[80];
size_t wpa_ie_len = sizeof(wpa_ie);
+ bool skip_default_rsne;
+
+ /* Do not override RSNE/RSNXE with the default values if the
+ * driver indicated the actual values used in the
+ * (Re)Association Request frame. */
+ skip_default_rsne = data && data->assoc_info.req_ies;
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
- wpa_ie, &wpa_ie_len) < 0)
+ wpa_ie, &wpa_ie_len,
+ skip_default_rsne) < 0)
wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites");
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
@@ -301,16 +309,15 @@
}
-static void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s)
+void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s)
{
- int i;
-
if (!wpa_s->valid_links)
return;
wpa_s->valid_links = 0;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
- wpa_s->links[i].bss = NULL;
+ wpa_s->mlo_assoc_link_id = 0;
+ os_memset(wpa_s->ap_mld_addr, 0, ETH_ALEN);
+ os_memset(wpa_s->links, 0, sizeof(wpa_s->links));
}
@@ -382,6 +389,7 @@
if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0)
wpa_s->enabled_4addr_mode = 0;
+ wpa_s->wps_scan_done = false;
wpas_reset_mlo_info(wpa_s);
}
@@ -601,7 +609,8 @@
#ifdef CONFIG_WEP
int wep_ok;
#endif /* CONFIG_WEP */
- bool is_6ghz_bss = is_6ghz_freq(bss->freq);
+ bool is_6ghz_bss_or_mld = is_6ghz_freq(bss->freq) ||
+ !is_zero_ether_addr(bss->mld_addr);
ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
if (ret >= 0)
@@ -616,10 +625,10 @@
#endif /* CONFIG_WEP */
rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
- if (is_6ghz_bss && !rsn_ie) {
+ if (is_6ghz_bss_or_mld && !rsn_ie) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - 6 GHz BSS without RSNE");
+ " skip - 6 GHz/MLD BSS without RSNE");
return 0;
}
@@ -637,7 +646,7 @@
if (!ie.has_group)
ie.group_cipher = wpa_default_rsn_cipher(bss->freq);
- if (is_6ghz_bss) {
+ if (is_6ghz_bss_or_mld) {
/* WEP and TKIP are not allowed on 6 GHz */
ie.pairwise_cipher &= ~(WPA_CIPHER_WEP40 |
WPA_CIPHER_WEP104 |
@@ -688,12 +697,12 @@
break;
}
- if (is_6ghz_bss) {
+ if (is_6ghz_bss_or_mld) {
/* MFPC must be supported on 6 GHz */
if (!(ie.capabilities & WPA_CAPABILITY_MFPC)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
- " skip RSNE - 6 GHz without MFPC");
+ " skip RSNE - 6 GHz/MLD without MFPC");
break;
}
@@ -733,10 +742,10 @@
return 1;
}
- if (is_6ghz_bss) {
+ if (is_6ghz_bss_or_mld) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - 6 GHz BSS without matching RSNE");
+ " skip - 6 GHz/MLD BSS without matching RSNE");
return 0;
}
@@ -956,10 +965,22 @@
continue;
}
+ if (flagged && ((rate_ie[j] & 0x7f) ==
+ BSS_MEMBERSHIP_SELECTOR_HE_PHY)) {
+ if (!he_supported(mode, IEEE80211_MODE_INFRA)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support HE PHY");
+ return 0;
+ }
+ continue;
+ }
+
#ifdef CONFIG_SAE
if (flagged && ((rate_ie[j] & 0x7f) ==
BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) {
- if (wpa_s->conf->sae_pwe == 0 &&
+ if (wpa_s->conf->sae_pwe ==
+ SAE_PWE_HUNT_AND_PECK &&
!ssid->sae_password_id &&
wpa_key_mgmt_sae(ssid->key_mgmt)) {
if (debug_print)
@@ -1393,9 +1414,10 @@
#ifdef CONFIG_SAE
/* When using SAE Password Identifier and when operationg on the 6 GHz
* band, only H2E is allowed. */
- if ((wpa_s->conf->sae_pwe == 1 || is_6ghz_freq(bss->freq) ||
- ssid->sae_password_id) &&
- wpa_s->conf->sae_pwe != 3 && wpa_key_mgmt_sae(ssid->key_mgmt) &&
+ if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ is_6ghz_freq(bss->freq) || ssid->sae_password_id) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+ wpa_key_mgmt_sae(ssid->key_mgmt) &&
#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
!(wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) &&
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
@@ -1511,7 +1533,8 @@
#ifdef CONFIG_DPP
if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
- !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
+ !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, wpa_s->own_addr,
+ ssid) &&
(!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
!ssid->dpp_csign)) {
if (debug_print)
@@ -1802,10 +1825,12 @@
struct wpa_bss *selected,
struct wpa_ssid *ssid)
{
- if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
+ if (wpas_wps_partner_link_overlap_detect(wpa_s) ||
+ wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
wpas_notify_wps_event_pbc_overlap(wpa_s);
+ wpa_s->wps_overlap = true;
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
wpa_s->p2p_in_provisioning) {
@@ -1994,9 +2019,9 @@
* information about our currently associated AP.
*/
if (wpa_drv_signal_poll(wpa_s, &si) == 0 &&
- (si.avg_beacon_signal || si.avg_signal)) {
- cur_level = si.avg_beacon_signal ? si.avg_beacon_signal :
- si.avg_signal;
+ (si.data.avg_beacon_signal || si.data.avg_signal)) {
+ cur_level = si.data.avg_beacon_signal ?
+ si.data.avg_beacon_signal : si.data.avg_signal;
cur_snr = wpas_get_snr_signal_info(si.frequency, cur_level,
si.current_noise);
@@ -2309,6 +2334,9 @@
}
}
+ if (wpa_s->supp_pbc_active && !wpas_wps_partner_link_scan_done(wpa_s))
+ return ret;
+
return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
scan_work_done:
@@ -2375,6 +2403,8 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
return -1;
}
+ wpa_s->supp_pbc_active = false;
+
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
/*
@@ -2409,9 +2439,9 @@
if (res == 1)
return 0;
- if (wpas_p2p_retry_limit_exceeded(wpa_s)) {
+ if (wpas_p2p_retry_limit_exceeded(wpa_s))
return 0;
- }
+
if (wpa_s->p2p_in_provisioning ||
wpa_s->show_group_started ||
wpa_s->p2p_in_invitation) {
@@ -2545,6 +2575,17 @@
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
+
+int wpa_wps_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+ return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
+ return wpas_select_network_from_last_scan(wpa_s, 1, 1);
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
+
#ifdef CONFIG_WNM
static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
@@ -2844,15 +2885,15 @@
if (!ssid->psk_set) {
wpa_dbg(wpa_s, MSG_INFO,
"No PSK available for association");
- wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE");
+ wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL);
return -1;
}
wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, NULL);
if (wpa_s->conf->key_mgmt_offload &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD) &&
- wpa_drv_set_key(wpa_s, 0, NULL, 0, 0, NULL, 0, ssid->psk,
- PMK_LEN, KEY_FLAG_PMK))
+ wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0, NULL, 0,
+ ssid->psk, PMK_LEN, KEY_FLAG_PMK))
wpa_dbg(wpa_s, MSG_ERROR,
"WPA: Cannot set PMK for key management offload");
}
@@ -3453,7 +3494,7 @@
struct driver_sta_mlo_info mlo;
int i;
- mlo.valid_links = 0;
+ os_memset(&mlo, 0, sizeof(mlo));
if (wpas_drv_get_sta_mlo_info(wpa_s, &mlo)) {
wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO link info");
wpa_supplicant_deauthenticate(wpa_s,
@@ -3480,13 +3521,14 @@
}
}
- if (match &&
+ if (match && wpa_s->mlo_assoc_link_id == mlo.assoc_link_id &&
os_memcmp(wpa_s->ap_mld_addr, mlo.ap_mld_addr,
ETH_ALEN) == 0)
return 0;
}
wpa_s->valid_links = mlo.valid_links;
+ wpa_s->mlo_assoc_link_id = mlo.assoc_link_id;
os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN);
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
if (!(wpa_s->valid_links & BIT(i)))
@@ -3502,6 +3544,66 @@
}
+static int wpa_sm_set_ml_info(struct wpa_supplicant *wpa_s)
+{
+ struct driver_sta_mlo_info drv_mlo;
+ struct wpa_sm_mlo wpa_mlo;
+ const u8 *bss_rsn = NULL, *bss_rsnx = NULL;
+ int i;
+
+ os_memset(&drv_mlo, 0, sizeof(drv_mlo));
+ if (wpas_drv_get_sta_mlo_info(wpa_s, &drv_mlo)) {
+ wpa_dbg(wpa_s, MSG_INFO, "Failed to get MLO link info");
+ return -1;
+ }
+
+ os_memset(&wpa_mlo, 0, sizeof(wpa_mlo));
+ if (!drv_mlo.valid_links)
+ goto out;
+
+ os_memcpy(wpa_mlo.ap_mld_addr, drv_mlo.ap_mld_addr, ETH_ALEN);
+ wpa_mlo.assoc_link_id = drv_mlo.assoc_link_id;
+ wpa_mlo.valid_links = drv_mlo.valid_links;
+ wpa_mlo.req_links = drv_mlo.req_links;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ struct wpa_bss *bss;
+
+ if (!(drv_mlo.req_links & BIT(i)))
+ continue;
+
+ bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid);
+ if (!bss) {
+ wpa_supplicant_update_scan_results(wpa_s);
+ bss = wpa_supplicant_get_new_bss(
+ wpa_s, drv_mlo.links[i].bssid);
+ }
+
+ if (!bss) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Failed to get MLO link %d BSS", i);
+ return -1;
+ }
+
+ bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+
+ wpa_mlo.links[i].ap_rsne = bss_rsn ? (u8 *) bss_rsn : NULL;
+ wpa_mlo.links[i].ap_rsne_len = bss_rsn ? 2 + bss_rsn[1] : 0;
+ wpa_mlo.links[i].ap_rsnxe = bss_rsnx ? (u8 *) bss_rsnx : NULL;
+ wpa_mlo.links[i].ap_rsnxe_len = bss_rsnx ? 2 + bss_rsnx[1] : 0;
+
+ os_memcpy(wpa_mlo.links[i].bssid, drv_mlo.links[i].bssid,
+ ETH_ALEN);
+ os_memcpy(wpa_mlo.links[i].addr, drv_mlo.links[i].addr,
+ ETH_ALEN);
+ }
+
+out:
+ return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
+}
+
+
static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
@@ -3631,7 +3733,7 @@
if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
wpa_clear_keys(wpa_s, bssid);
}
- if (wpa_supplicant_select_config(wpa_s) < 0) {
+ if (wpa_supplicant_select_config(wpa_s, data) < 0) {
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
@@ -3668,6 +3770,15 @@
wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
}
wpa_sm_notify_assoc(wpa_s->wpa, bssid);
+
+ if (wpa_sm_set_ml_info(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Failed to set MLO connection info to wpa_sm");
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
if (wpa_s->l2)
l2_packet_notify_auth_start(wpa_s->l2);
@@ -3737,12 +3848,12 @@
eapol_sm_notify_portValid(wpa_s->eapol, true);
eapol_sm_notify_eap_success(wpa_s->eapol, true);
} else {
- /* Update port, WPA_COMPLETED state from
- * EVENT_PORT_AUTHORIZED context when driver is done
- * with 4way handshake.
+ /* Update port, WPA_COMPLETED state from the
+ * EVENT_PORT_AUTHORIZED handler when the driver is done
+ * with the 4-way handshake.
*/
- wpa_msg(wpa_s, MSG_INFO, "ASSOC INFO: wait for driver port "
- "authorized indication");
+ wpa_msg(wpa_s, MSG_INFO,
+ "ASSOC INFO: wait for driver port authorized indication");
}
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
@@ -3792,8 +3903,9 @@
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
if (age.sec == 0 && age.usec < 200000 &&
- os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
- 0) {
+ os_memcmp(wpa_s->pending_eapol_rx_src,
+ wpa_s->valid_links ? wpa_s->ap_mld_addr : bssid,
+ ETH_ALEN) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
"frame that was received just before "
"association notification");
@@ -3973,7 +4085,7 @@
"pre-shared key may be incorrect");
if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
return; /* P2P group removed */
- wpas_auth_failed(wpa_s, "WRONG_KEY");
+ wpas_auth_failed(wpa_s, "WRONG_KEY", prev_pending_bssid);
#ifdef CONFIG_DPP2
wpas_dpp_send_conn_status_result(wpa_s,
DPP_STATUS_AUTH_FAILURE);
@@ -4477,7 +4589,7 @@
(wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
eapol_sm_failed(wpa_s->eapol))) &&
!wpa_s->eap_expected_failure))
- wpas_auth_failed(wpa_s, "AUTH_FAILED");
+ wpas_auth_failed(wpa_s, "AUTH_FAILED", addr);
#ifdef CONFIG_P2P
if (deauth && reason_code > 0) {
@@ -5372,6 +5484,7 @@
os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
wpa_s->scan_start_time.sec = 0;
wpa_s->scan_start_time.usec = 0;
+ wpa_s->wps_scan_done = true;
wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
diff.sec, diff.usec);
}
@@ -5821,18 +5934,21 @@
break;
case EVENT_SIGNAL_CHANGE:
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
- "above=%d signal=%d noise=%d txrate=%d",
+ "above=%d signal=%d noise=%d txrate=%lu",
data->signal_change.above_threshold,
- data->signal_change.current_signal,
+ data->signal_change.data.signal,
data->signal_change.current_noise,
- data->signal_change.current_txrate);
+ data->signal_change.data.current_tx_rate);
wpa_bss_update_level(wpa_s->current_bss,
- data->signal_change.current_signal);
+ data->signal_change.data.signal);
bgscan_notify_signal_change(
wpa_s, data->signal_change.above_threshold,
- data->signal_change.current_signal,
+ data->signal_change.data.signal,
data->signal_change.current_noise,
- data->signal_change.current_txrate);
+ data->signal_change.data.current_tx_rate);
+ os_memcpy(&wpa_s->last_signal_info, data,
+ sizeof(struct wpa_signal_info));
+ wpas_notify_signal_change(wpa_s);
break;
case EVENT_INTERFACE_MAC_CHANGED:
wpa_supplicant_update_mac_addr(wpa_s);
@@ -6099,6 +6215,15 @@
break;
#endif /* CONFIG_PASN */
case EVENT_PORT_AUTHORIZED:
+#ifndef CONFIG_NO_WPA
+ if (data->port_authorized.td_bitmap_len) {
+ wpa_printf(MSG_DEBUG,
+ "WPA3: Transition Disable bitmap from the driver event: 0x%x",
+ data->port_authorized.td_bitmap[0]);
+ wpas_transition_disable(
+ wpa_s, data->port_authorized.td_bitmap[0]);
+ }
+#endif /* CONFIG_NO_WPA */
wpa_supplicant_event_port_authorized(wpa_s);
break;
case EVENT_STATION_OPMODE_CHANGED:
diff --git a/wpa_supplicant/examples/dpp-nfc.py b/wpa_supplicant/examples/dpp-nfc.py
index 8e865f3..6cffe71 100755
--- a/wpa_supplicant/examples/dpp-nfc.py
+++ b/wpa_supplicant/examples/dpp-nfc.py
@@ -359,7 +359,7 @@
summary("NFC Handover Request message for DPP: " + str(message))
if handover.peer_crn is not None and not alt:
- summary("NFC handover request from peer was already received - do not send own")
+ summary("NFC handover request from peer was already received - do not send own[1]")
return
if handover.client:
summary("Use already started handover client")
@@ -382,7 +382,8 @@
handover.client = client
if handover.peer_crn is not None and not alt:
- summary("NFC handover request from peer was already received - do not send own")
+ summary("NFC handover request from peer was already received - do not send own[2] (except alt)")
+ run_client_alt(handover, alt)
return
summary("Sending handover request")
@@ -876,6 +877,11 @@
if init_on_touch:
summary("Starting handover client (init_on_touch)")
dpp_handover_client(handover)
+ summary("llcp_worker init_on_touch processing completed: try_own={} hs_sent={} no_alt_proposal={} start_client_alt={}".format(handover.try_own, handover.hs_sent, handover.no_alt_proposal, handover.start_client_alt))
+ if handover.start_client_alt and not handover.hs_sent:
+ summary("Try alternative handover request before exiting llcp_worker")
+ handover.start_client_alt = False
+ dpp_handover_client(handover, alt=True)
summary("Exiting llcp_worker thread (init_on_touch)")
return
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 802f120..c301f74 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -404,14 +404,14 @@
static void gas_query_rx_initial(struct gas_query *gas,
struct gas_query_pending *query,
- const u8 *adv_proto, const u8 *resp,
- size_t len, u16 comeback_delay)
+ const u8 *adv_proto, size_t adv_proto_len,
+ 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]);
+ query->adv_proto = wpabuf_alloc_copy(adv_proto, adv_proto_len);
if (query->adv_proto == NULL) {
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
return;
@@ -436,9 +436,9 @@
static void gas_query_rx_comeback(struct gas_query *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)
+ const u8 *adv_proto, size_t adv_proto_len,
+ 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 "
@@ -447,7 +447,7 @@
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) ||
+ if (adv_proto_len != 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 "
@@ -516,6 +516,7 @@
u8 action, dialog_token, frag_id = 0, more_frags = 0;
u16 comeback_delay, resp_len;
const u8 *pos, *adv_proto;
+ size_t adv_proto_len;
int prot, pmf;
unsigned int left;
@@ -596,22 +597,26 @@
pos += 2;
/* Advertisement Protocol element */
- if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+ adv_proto = pos;
+ left = data + len - adv_proto;
+ if (left < 2 || adv_proto[1] > left - 2) {
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) {
+ if (*adv_proto != WLAN_EID_ADV_PROTO) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
"Protocol element ID %u in response from " MACSTR,
- *pos, MAC2STR(sa));
+ *adv_proto, MAC2STR(sa));
return 0;
}
+ adv_proto_len = 2 + adv_proto[1];
+ if (adv_proto_len > (size_t) (data + len - pos))
+ return 0; /* unreachable due to an earlier check */
- adv_proto = pos;
- pos += 2 + pos[1];
+ pos += adv_proto_len;
/* Query Response Length */
if (pos + 2 > data + len) {
@@ -635,11 +640,12 @@
}
if (action == WLAN_PA_GAS_COMEBACK_RESP)
- gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
- frag_id, more_frags, comeback_delay);
+ gas_query_rx_comeback(gas, query, adv_proto, adv_proto_len,
+ pos, resp_len, frag_id, more_frags,
+ comeback_delay);
else
- gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
- comeback_delay);
+ gas_query_rx_initial(gas, query, adv_proto, adv_proto_len,
+ pos, resp_len, comeback_delay);
return 0;
}
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 874c2bf..5b31f7b 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -143,7 +143,7 @@
}
-static int supp_set_key(void *ctx, enum wpa_alg alg,
+static int supp_set_key(void *ctx, int link_id, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len, enum key_flag key_flag)
@@ -172,8 +172,9 @@
if (is_broadcast_ether_addr(addr))
addr = peer->addr;
- return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
- set_tx, seq, seq_len, key, key_len, key_flag);
+ return wpa_drv_set_key(peer->ibss_rsn->wpa_s, link_id, alg, addr,
+ key_idx, set_tx, seq, seq_len, key, key_len,
+ key_flag);
}
@@ -352,7 +353,7 @@
}
}
- return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx,
+ return wpa_drv_set_key(ibss_rsn->wpa_s, -1, alg, addr, idx,
1, seq, 6, key, key_len, key_flag);
}
@@ -868,7 +869,7 @@
* 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,
+ wpa_drv_set_key(ibss_rsn->wpa_s, -1, WPA_ALG_NONE, addr, 0, 0,
NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
}
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index acd9044..4d0fc63 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -144,9 +144,9 @@
struct wpa_cred *cred;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- if (cred->roaming_consortium_len)
+ if (cred->num_home_ois)
return 1;
- if (cred->required_roaming_consortium_len)
+ if (cred->num_required_home_ois)
return 1;
if (cred->num_roaming_consortiums)
return 1;
@@ -421,6 +421,11 @@
case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Unsupported EAP-TTLS inner method %u",
+ *pos);
+ break;
}
break;
case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
@@ -1098,8 +1103,7 @@
}
-static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
- size_t rc_len)
+static int oi_element_match(const u8 *ie, const u8 *oi, size_t oi_len)
{
const u8 *pos, *end;
u8 lens;
@@ -1124,24 +1128,24 @@
if ((lens & 0x0f) + (lens >> 4) > end - pos)
return 0;
- if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ if ((lens & 0x0f) == oi_len && os_memcmp(pos, oi, oi_len) == 0)
return 1;
pos += lens & 0x0f;
- if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ if ((lens >> 4) == oi_len && os_memcmp(pos, oi, oi_len) == 0)
return 1;
pos += lens >> 4;
- if (pos < end && (size_t) (end - pos) == rc_len &&
- os_memcmp(pos, rc_id, rc_len) == 0)
+ if (pos < end && (size_t) (end - pos) == oi_len &&
+ os_memcmp(pos, oi, oi_len) == 0)
return 1;
return 0;
}
-static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
- const u8 *rc_id, size_t rc_len)
+static int oi_anqp_match(const struct wpabuf *anqp, const u8 *oi,
+ size_t oi_len)
{
const u8 *pos, *end;
u8 len;
@@ -1157,7 +1161,7 @@
len = *pos++;
if (len > end - pos)
break;
- if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ if (len == oi_len && os_memcmp(pos, oi, oi_len) == 0)
return 1;
pos += len;
}
@@ -1166,11 +1170,26 @@
}
-static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
- const u8 *rc_id, size_t rc_len)
+static int oi_match(const u8 *ie, const struct wpabuf *anqp,
+ const u8 *oi, size_t oi_len)
{
- return roaming_consortium_element_match(ie, rc_id, rc_len) ||
- roaming_consortium_anqp_match(anqp, rc_id, rc_len);
+ return oi_element_match(ie, oi, oi_len) ||
+ oi_anqp_match(anqp, oi, oi_len);
+}
+
+
+static int cred_home_ois_match(const u8 *ie, const struct wpabuf *anqp,
+ const struct wpa_cred *cred) {
+ unsigned int i;
+
+ /* There's a match if at least one of the home OI matches. */
+ for (i = 0; i < cred->num_home_ois; i++) {
+ if (oi_match(ie, anqp, cred->home_ois[i],
+ cred->home_ois_len[i]))
+ return 1;
+ }
+
+ return 0;
}
@@ -1181,9 +1200,8 @@
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]))
+ if (oi_match(ie, anqp, cred->roaming_consortiums[i],
+ cred->roaming_consortiums_len[i]))
return 1;
}
@@ -1194,8 +1212,9 @@
static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
{
const u8 *ie;
+ unsigned int i;
- if (cred->required_roaming_consortium_len == 0)
+ if (cred->num_required_home_ois == 0)
return 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
@@ -1204,11 +1223,16 @@
(bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
return 1;
- return !roaming_consortium_match(ie,
- bss->anqp ?
- bss->anqp->roaming_consortium : NULL,
- cred->required_roaming_consortium,
- cred->required_roaming_consortium_len);
+ /* According to Passpoint specification, there must be a match for
+ * each required home OI provided. */
+ for (i = 0; i < cred->num_required_home_ois; i++) {
+ if (!oi_match(ie, bss->anqp ?
+ bss->anqp->roaming_consortium : NULL,
+ cred->required_home_ois[i],
+ cred->required_home_ois_len[i]))
+ return 1;
+ }
+ return 0;
}
@@ -1408,26 +1432,24 @@
return NULL;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- if (cred->roaming_consortium_len == 0 &&
+ if (cred->num_home_ois == 0 &&
+ cred->num_required_home_ois == 0 &&
cred->num_roaming_consortiums == 0)
continue;
if (!cred->eap_method)
continue;
- 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) &&
- (cred->required_roaming_consortium_len == 0 ||
- !roaming_consortium_match(
- ie, anqp, cred->required_roaming_consortium,
- cred->required_roaming_consortium_len)))
+ /* If there's required home OIs, there must be a match for each
+ * required OI (see Passpoint v3.2 - 9.1.2 - RequiredHomeOI). */
+ if (cred->num_required_home_ois > 0 &&
+ cred_no_required_oi_match(cred, bss))
continue;
- if (cred_no_required_oi_match(cred, bss))
+ if (!cred_home_ois_match(ie, anqp, cred) &&
+ !cred_roaming_consortiums_match(ie, anqp, cred))
continue;
+
if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
continue;
if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
@@ -1642,9 +1664,8 @@
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]))
+ if (!oi_match(ie, anqp, cred->roaming_consortiums[i],
+ cred->roaming_consortiums_len[i]))
continue;
ssid->roaming_consortium_selection =
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 2eb9a7e..c1ed8c4 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -879,7 +879,8 @@
if (conf->security & MESH_CONF_SEC_AMPE) {
wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len);
- wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher),
+ wpa_drv_set_key(wpa_s, -1,
+ wpa_cipher_to_alg(conf->pairwise_cipher),
sta->addr, 0, 0, seq, sizeof(seq),
sta->mtk, sta->mtk_len,
KEY_FLAG_PAIRWISE_RX_TX);
@@ -888,7 +889,8 @@
sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK",
sta->mgtk, sta->mgtk_len);
- wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher),
+ wpa_drv_set_key(wpa_s, -1,
+ wpa_cipher_to_alg(conf->group_cipher),
sta->addr, sta->mgtk_key_id, 0,
sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
sta->mgtk, sta->mgtk_len,
@@ -900,7 +902,7 @@
wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK",
sta->igtk, sta->igtk_len);
wpa_drv_set_key(
- wpa_s,
+ wpa_s, -1,
wpa_cipher_to_alg(conf->mgmt_group_cipher),
sta->addr, sta->igtk_key_id, 0,
sta->igtk_rsc, sizeof(sta->igtk_rsc),
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 65daa77..12dcc30 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -118,7 +118,7 @@
}
wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
- return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx,
+ return wpa_drv_set_key(mesh_rsn->wpa_s, -1, alg, addr, idx,
1, seq, 6, key, key_len, key_flag);
}
@@ -194,7 +194,7 @@
/* group mgmt */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
rsn->igtk, rsn->igtk_len);
- wpa_drv_set_key(rsn->wpa_s,
+ wpa_drv_set_key(rsn->wpa_s, -1,
wpa_cipher_to_alg(rsn->mgmt_group_cipher),
broadcast_ether_addr,
rsn->igtk_key_id, 1,
@@ -205,7 +205,7 @@
/* group privacy / data frames */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
rsn->mgtk, rsn->mgtk_len);
- wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher),
+ wpa_drv_set_key(rsn->wpa_s, -1, wpa_cipher_to_alg(rsn->group_cipher),
broadcast_ether_addr,
rsn->mgtk_key_id, 1, seq, sizeof(seq),
rsn->mgtk, rsn->mgtk_len, KEY_FLAG_GROUP_TX_DEFAULT);
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 76f2232..32ddf1f 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -16,6 +16,7 @@
#include "dbus/dbus_common.h"
#include "dbus/dbus_new.h"
#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
#include "fst/fst.h"
#include "crypto/tls.h"
#include "bss.h"
@@ -237,6 +238,15 @@
}
+void wpas_notify_mac_address_changed(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_MAC_ADDRESS);
+}
+
+
void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
@@ -447,11 +457,6 @@
wpas_notify_persistent_group_removed(wpa_s, ssid);
wpas_p2p_network_removed(wpa_s, ssid);
-
-#ifdef CONFIG_PASN
- if (wpa_s->pasn.ssid == ssid)
- wpa_s->pasn.ssid = NULL;
-#endif /* CONFIG_PASN */
}
@@ -1357,3 +1362,9 @@
{
return wpas_aidl_get_certificate(alias, value);
}
+
+
+void wpas_notify_signal_change(struct wpa_supplicant *wpa_s)
+{
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SIGNAL_CHANGE);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 9a818ef..b1824ec 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -19,6 +19,7 @@
struct wps_event_fail;
struct tls_cert_data;
struct wpa_cred;
+struct rsn_pmksa_cache_entry;
int wpas_notify_supplicant_initialized(struct wpa_global *global);
void wpas_notify_supplicant_deinitialized(struct wpa_global *global);
@@ -39,6 +40,7 @@
void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_mac_address_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
@@ -199,8 +201,6 @@
const char *channel_list, unsigned short band_list[], int size);
void wpas_notify_dpp_config_accepted(struct wpa_supplicant *wpa_s);
void wpas_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s);
-void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
- struct rsn_pmksa_cache_entry *entry);
void wpas_notify_transition_disable(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
u8 bitmap);
@@ -222,5 +222,8 @@
struct dscp_policy_data *policies, int num_policies);
void wpas_notify_frequency_changed(struct wpa_supplicant *wpa_s, int frequency);
ssize_t wpas_get_certificate(const char *alias, uint8_t** value);
+void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
+ struct rsn_pmksa_cache_entry *entry);
+void wpas_notify_signal_change(struct wpa_supplicant *wpa_s);
#endif /* NOTIFY_H */
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index a505e4f..926ba7a 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -192,7 +192,7 @@
return -1;
num = get_shared_radio_freqs(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ wpa_s->num_multichan_concurrent, false);
os_free(freqs);
unused = wpa_s->num_multichan_concurrent - num;
@@ -219,7 +219,8 @@
return 0;
num = get_shared_radio_freqs_data(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ wpa_s->num_multichan_concurrent,
+ false);
os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len);
@@ -339,6 +340,23 @@
}
+void wpas_p2p_scan_freqs(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ bool include_6ghz)
+{
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+ params, false, false, false);
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+ params, false, false, false);
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211AD,
+ params, false, false, false);
+ if (!wpa_s->conf->p2p_6ghz_disable &&
+ is_p2p_allow_6ghz(wpa_s->global->p2p) && include_6ghz)
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+ params, true, true, false);
+}
+
+
static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
@@ -361,14 +379,9 @@
params->only_new_results = 1;
}
- if (!params->p2p_include_6ghz && !params->freqs) {
- wpa_printf(MSG_DEBUG,
- "P2P: Exclude 6 GHz channels - update the scan frequency list");
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
- false, false);
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
- false, false);
- }
+ if (!params->freqs)
+ wpas_p2p_scan_freqs(wpa_s, params, params->p2p_include_6ghz);
+
ret = wpa_drv_scan(wpa_s, params);
if (ret == 0)
wpa_s->curr_scan_cookie = params->scan_cookie;
@@ -447,6 +460,13 @@
num_req_dev_types, req_dev_types);
if (wps_ie == NULL)
goto fail;
+
+ /*
+ * In case 6 GHz channels are requested as part of the P2P scan, only
+ * the PSCs would be included as P2P GOs are not expected to be
+ * collocated, i.e., they would not be announced in the RNR element of
+ * other APs.
+ */
if (!wpa_s->conf->p2p_6ghz_disable)
params->p2p_include_6ghz = include_6ghz;
switch (type) {
@@ -533,9 +553,9 @@
return WPA_IF_P2P_GO;
case P2P_GROUP_INTERFACE_CLIENT:
return WPA_IF_P2P_CLIENT;
+ default:
+ return WPA_IF_P2P_GROUP;
}
-
- return WPA_IF_P2P_GROUP;
}
@@ -2090,7 +2110,7 @@
ssid->auth_alg |= WPA_AUTH_ALG_SAE;
ssid->key_mgmt = WPA_KEY_MGMT_SAE;
ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
- ssid->sae_pwe = 1;
+ ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use SAE auth_alg and key_mgmt");
} else {
p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false);
@@ -2181,6 +2201,7 @@
d->passive_scan = s->passive_scan;
d->pmf = s->pmf;
d->p2p_6ghz_disable = s->p2p_6ghz_disable;
+ d->sae_pwe = s->sae_pwe;
if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey &&
!d->wps_nfc_pw_from_config) {
@@ -2412,9 +2433,9 @@
bool wpas_p2p_retry_limit_exceeded(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->p2p_in_invitation || !wpa_s->p2p_retry_limit ||
- wpa_s->p2p_in_invitation <= wpa_s->p2p_retry_limit) {
+ wpa_s->p2p_in_invitation <= wpa_s->p2p_retry_limit)
return false;
- }
+
wpa_printf(MSG_DEBUG, "P2P: Group join retry limit exceeded");
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
@@ -3997,8 +4018,7 @@
for (op = 0; global_op_class[op].op_class; op++) {
const struct oper_class_map *o = &global_op_class[op];
- u16 ch;
- int chan = channel;
+ u16 ch = 0;
/* Allow DFS channels marked as NO_P2P_SUPP to be used with
* driver offloaded DFS. */
@@ -4009,15 +4029,22 @@
wpa_s->conf->p2p_6ghz_disable))
continue;
+ /* IEEE Std 802.11ax-2021 26.17.2.3.2: "A 6 GHz-only AP should
+ * set up the BSS with a primary 20 MHz channel that coincides
+ * with a preferred scanning channel (PSC)."
+ * 6 GHz BW40 operation class 132 in wpa_supplicant uses the
+ * lowest 20 MHz channel for simplicity, so increase ch by 4 to
+ * match the PSC.
+ */
if (is_6ghz_op_class(o->op_class) && o->bw == BW40 &&
get_6ghz_sec_channel(channel) < 0)
- chan = channel - 4;
+ ch = 4;
- for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ for (ch += o->min_chan; ch <= o->max_chan; ch += o->inc) {
if (o->mode != HOSTAPD_MODE_IEEE80211A ||
(o->bw != BW40PLUS && o->bw != BW40MINUS &&
o->bw != BW40) ||
- ch != chan)
+ ch != channel)
continue;
ret = wpas_p2p_verify_channel(wpa_s, mode, o->op_class,
ch, o->bw);
@@ -5557,14 +5584,8 @@
if (freq > 0) {
freqs[0] = freq;
params.freqs = freqs;
- } else if (wpa_s->conf->p2p_6ghz_disable ||
- !is_p2p_allow_6ghz(wpa_s->global->p2p)) {
- wpa_printf(MSG_DEBUG,
- "P2P: 6 GHz disabled - update the scan frequency list");
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms,
- false, false);
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms,
- false, false);
+ } else {
+ wpas_p2p_scan_freqs(wpa_s, ¶ms, true);
}
ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
@@ -6541,7 +6562,8 @@
return -1;
num = get_shared_radio_freqs_data(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ wpa_s->num_multichan_concurrent,
+ false);
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
@@ -8426,7 +8448,7 @@
if (!freqs)
return;
- num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+ num = get_shared_radio_freqs_data(wpa_s, freqs, num, false);
os_memset(&chan, 0, sizeof(chan));
os_memset(&cli_chan, 0, sizeof(cli_chan));
@@ -8612,6 +8634,10 @@
"in group formation",
wpa_s->global->p2p_group_formation->ifname);
ret = 1;
+ } else if (wpa_s->global->p2p_group_formation == wpa_s) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Skip Extended Listen timeout and allow scans on current interface for group formation");
+ ret = 2;
}
}
@@ -9973,7 +9999,7 @@
if (!freqs)
return;
- num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+ num = get_shared_radio_freqs_data(wpa_s, freqs, num, false);
/* Previous attempt to move a GO was not possible -- try again. */
wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index c87e1bf..e113c62 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -147,6 +147,9 @@
void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
const u8 *addr);
int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
+void wpas_p2p_scan_freqs(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ bool include_6ghz);
int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 channel);
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 2e6d9a7..edecfde 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -30,7 +30,7 @@
struct wpa_pasn_auth_work {
u8 own_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
int akmp;
int cipher;
u16 group;
@@ -39,6 +39,15 @@
};
+static int wpas_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+ int noack, unsigned int freq, unsigned int wait)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+}
+
+
static void wpas_pasn_free_auth_work(struct wpa_pasn_auth_work *awork)
{
wpabuf_free(awork->comeback);
@@ -68,7 +77,8 @@
}
-static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
+static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
int akmp, int cipher, u8 status,
struct wpabuf *comeback,
u16 comeback_after)
@@ -84,7 +94,7 @@
wpa_msg(wpa_s, MSG_INFO, PASN_AUTH_STATUS MACSTR
" akmp=%s, status=%u comeback_after=%u comeback=%s",
- MAC2STR(bssid),
+ MAC2STR(peer_addr),
wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
status, comeback_after, comeback_txt);
@@ -95,176 +105,15 @@
wpa_msg(wpa_s, MSG_INFO,
PASN_AUTH_STATUS MACSTR " akmp=%s, status=%u",
- MAC2STR(bssid), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
+ MAC2STR(peer_addr), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
status);
}
#ifdef CONFIG_SAE
-static struct wpabuf * wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf = NULL;
- int ret;
-
- ret = sae_set_group(&pasn->sae, pasn->group);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
- return NULL;
- }
-
- ret = sae_prepare_commit_pt(&pasn->sae, pasn->ssid->pt,
- pasn->own_addr, pasn->bssid,
- NULL, NULL);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
- return NULL;
- }
-
- /* Need to add the entire Authentication frame body */
- buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
- if (!buf) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
- return NULL;
- }
-
- wpabuf_put_le16(buf, WLAN_AUTH_SAE);
- wpabuf_put_le16(buf, 1);
- wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
-
- sae_write_commit(&pasn->sae, buf, NULL, 0);
- pasn->sae.state = SAE_COMMITTED;
-
- return buf;
-}
-
-
-static int wpas_pasn_wd_sae_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- const u8 *data;
- size_t buf_len;
- u16 len, res, alg, seq, status;
- int groups[] = { pasn->group, 0 };
- int ret;
-
- if (!wd)
- return -1;
-
- data = wpabuf_head_u8(wd);
- buf_len = wpabuf_len(wd);
-
- /* first handle the commit message */
- if (buf_len < 2) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
- return -1;
- }
-
- len = WPA_GET_LE16(data);
- if (len < 6 || buf_len - 2 < len) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
- return -1;
- }
-
- buf_len -= 2;
- data += 2;
-
- alg = WPA_GET_LE16(data);
- seq = WPA_GET_LE16(data + 2);
- status = WPA_GET_LE16(data + 4);
-
- wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
- alg, seq, status);
-
- if (alg != WLAN_AUTH_SAE || seq != 1 ||
- status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
- wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
- return -1;
- }
-
- res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
- 1);
- if (res != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
- return -1;
- }
-
- /* Process the commit message and derive the PMK */
- ret = sae_process_commit(&pasn->sae);
- if (ret) {
- wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
- return -1;
- }
-
- buf_len -= len;
- data += len;
-
- /* Handle the confirm message */
- if (buf_len < 2) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
- return -1;
- }
-
- len = WPA_GET_LE16(data);
- if (len < 6 || buf_len - 2 < len) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
- return -1;
- }
-
- buf_len -= 2;
- data += 2;
-
- alg = WPA_GET_LE16(data);
- seq = WPA_GET_LE16(data + 2);
- status = WPA_GET_LE16(data + 4);
-
- wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
- alg, seq, status);
-
- if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
- return -1;
- }
-
- res = sae_check_confirm(&pasn->sae, data + 6, len - 6);
- if (res != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
- pasn->sae.state = SAE_ACCEPTED;
-
- return 0;
-}
-
-
-static struct wpabuf * wpas_pasn_wd_sae_confirm(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf = NULL;
-
- /* Need to add the entire authentication frame body */
- buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
- if (!buf) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
- return NULL;
- }
-
- wpabuf_put_le16(buf, WLAN_AUTH_SAE);
- wpabuf_put_le16(buf, 2);
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
- sae_write_confirm(&pasn->sae, buf);
- pasn->sae.state = SAE_CONFIRMED;
-
- return buf;
-}
-
-
-static int wpas_pasn_sae_setup_pt(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid, int group)
+static struct sae_pt *
+wpas_pasn_sae_derive_pt(struct wpa_ssid *ssid, int group)
{
const char *password = ssid->sae_password;
int groups[2] = { group, 0 };
@@ -274,15 +123,26 @@
if (!password) {
wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
+ return NULL;
+ }
+
+ return sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
+ (const u8 *) password, os_strlen(password),
+ ssid->sae_password_id);
+}
+
+
+static int wpas_pasn_sae_setup_pt(struct wpa_ssid *ssid, int group)
+{
+ if (!ssid->sae_password && !ssid->passphrase) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
return -1;
}
if (ssid->pt)
return 0; /* PT already derived */
- ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
- (const u8 *) password, os_strlen(password),
- ssid->sae_password_id);
+ ssid->pt = wpas_pasn_sae_derive_pt(ssid, group);
return ssid->pt ? 0 : -1;
}
@@ -302,12 +162,12 @@
struct wpa_ssid *ssid = NULL;
size_t ssid_str_len = 0;
const u8 *ssid_str = NULL;
- const u8 *bssid = peer->peer_addr;
+ const u8 *peer_addr = peer->peer_addr;
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_supplicant_update_scan_results(wpa_s);
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: BSS not found");
return -1;
@@ -394,13 +254,13 @@
} else if ((sel & WPA_KEY_MGMT_SAE_EXT_KEY) &&
(ieee802_11_rsnx_capab(rsnxe,
WLAN_RSNX_CAPAB_SAE_H2E)) &&
- (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) == 0)) {
+ (wpas_pasn_sae_setup_pt(ssid, group) == 0)) {
key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE (ext key)");
} else if ((sel & WPA_KEY_MGMT_SAE) &&
(ieee802_11_rsnx_capab(rsnxe,
WLAN_RSNX_CAPAB_SAE_H2E)) &&
- (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) == 0)) {
+ (wpas_pasn_sae_setup_pt(ssid, group) == 0)) {
key_mgmt = WPA_KEY_MGMT_SAE;
wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE");
#endif /* CONFIG_SAE */
@@ -447,15 +307,16 @@
static int wpas_pasn_set_keys_from_cache(struct wpa_supplicant *wpa_s,
- const u8 *own_addr, const u8 *bssid,
+ const u8 *own_addr,
+ const u8 *peer_addr,
int cipher, int akmp)
{
struct ptksa_cache_entry *entry;
- entry = ptksa_cache_get(wpa_s->ptksa, bssid, cipher);
+ entry = ptksa_cache_get(wpa_s->ptksa, peer_addr, cipher);
if (!entry) {
wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
- " not present in PTKSA cache", MAC2STR(bssid));
+ " not present in PTKSA cache", MAC2STR(peer_addr));
return -1;
}
@@ -468,8 +329,8 @@
}
wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
- MAC2STR(bssid));
- wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, bssid, cipher,
+ MAC2STR(peer_addr));
+ wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, peer_addr, cipher,
entry->ptk.tk_len,
entry->ptk.tk,
entry->ptk.ltf_keyseed_len,
@@ -559,871 +420,59 @@
for (i = 0; i < pasn_params->num_peers; i++) {
peer = &pasn_params->peer[i];
- wpas_pasn_deauthenticate(wpa_s, peer->own_addr,
- peer->peer_addr);
+ ptksa_cache_flush(wpa_s->ptksa, peer->peer_addr,
+ WPA_CIPHER_NONE);
}
}
-#ifdef CONFIG_FILS
-
-static struct wpabuf * wpas_pasn_fils_build_auth(struct wpa_supplicant *wpa_s)
+static void wpas_pasn_initiate_eapol(struct pasn_data *pasn,
+ struct wpa_ssid *ssid)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf = NULL;
- struct wpabuf *erp_msg;
- int ret;
-
- erp_msg = eapol_sm_build_erp_reauth_start(wpa_s->eapol);
- if (!erp_msg) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS: ERP EAP-Initiate/Re-auth unavailable");
- return NULL;
- }
-
- if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 ||
- random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0)
- goto fail;
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce,
- FILS_NONCE_LEN);
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session,
- FILS_SESSION_LEN);
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- goto fail;
-
- /* Add the authentication algorithm */
- wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
-
- /* Authentication Transaction seq# */
- wpabuf_put_le16(buf, 1);
-
- /* Status Code */
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
- /* Own RSNE */
- wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
-
- /* FILS Nonce */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
- wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
- wpabuf_put_data(buf, pasn->fils.nonce, FILS_NONCE_LEN);
-
- /* FILS Session */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
- wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
- wpabuf_put_data(buf, pasn->fils.session, FILS_SESSION_LEN);
-
- /* Wrapped Data (ERP) */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg));
- wpabuf_put_u8(buf, WLAN_EID_EXT_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.
- */
- ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg),
- wpabuf_len(erp_msg),
- pasn->fils.erp_pmkid);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID");
- goto fail;
- }
-
- wpabuf_free(erp_msg);
- erp_msg = NULL;
-
- wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf);
- return buf;
-fail:
- wpabuf_free(erp_msg);
- wpabuf_free(buf);
- return NULL;
-}
-
-
-static void wpas_pasn_initiate_eapol(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
struct eapol_config eapol_conf;
- struct wpa_ssid *ssid = pasn->ssid;
wpa_printf(MSG_DEBUG, "PASN: FILS: Initiating EAPOL");
- eapol_sm_notify_eap_success(wpa_s->eapol, false);
- eapol_sm_notify_eap_fail(wpa_s->eapol, false);
- eapol_sm_notify_portControl(wpa_s->eapol, Auto);
+ eapol_sm_notify_eap_success(pasn->eapol, false);
+ eapol_sm_notify_eap_fail(pasn->eapol, false);
+ eapol_sm_notify_portControl(pasn->eapol, Auto);
os_memset(&eapol_conf, 0, sizeof(eapol_conf));
- eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ eapol_conf.fast_reauth = pasn->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
- eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
-}
-
-
-static struct wpabuf * wpas_pasn_wd_fils_auth(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpa_bss *bss;
- const u8 *indic;
- u16 fils_info;
-
- wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u",
- pasn->fils.completed);
-
- /* Nothing to add as we are done */
- if (pasn->fils.completed)
- return NULL;
-
- if (!pasn->ssid) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: No network block");
- return NULL;
- }
-
- bss = wpa_bss_get_bssid(wpa_s, pasn->bssid);
- if (!bss) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: BSS not found");
- return NULL;
- }
-
- indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
- if (!indic || indic[1] < 2) {
- wpa_printf(MSG_DEBUG, "PASN: Missing FILS Indication IE");
- return NULL;
- }
-
- fils_info = WPA_GET_LE16(indic + 2);
- if (!(fils_info & BIT(9))) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS auth without PFS not supported");
- return NULL;
- }
-
- wpas_pasn_initiate_eapol(wpa_s);
-
- return wpas_pasn_fils_build_auth(wpa_s);
-}
-
-
-static int wpas_pasn_wd_fils_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct ieee802_11_elems elems;
- struct wpa_ie_data rsne_data;
- u8 rmsk[ERP_MAX_KEY_LEN];
- size_t rmsk_len;
- u8 anonce[FILS_NONCE_LEN];
- const u8 *data;
- size_t buf_len;
- struct wpabuf *fils_wd = NULL;
- u16 alg, seq, status;
- int ret;
-
- if (!wd)
- return -1;
-
- data = wpabuf_head(wd);
- buf_len = wpabuf_len(wd);
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu",
- data, buf_len);
-
- /* first handle the header */
- if (buf_len < 6) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short");
- return -1;
- }
-
- alg = WPA_GET_LE16(data);
- seq = WPA_GET_LE16(data + 2);
- status = WPA_GET_LE16(data + 4);
-
- wpa_printf(MSG_DEBUG, "PASN: FILS: commit: alg=%u, seq=%u, status=%u",
- alg, seq, status);
-
- if (alg != WLAN_AUTH_FILS_SK || seq != 2 ||
- status != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS: Dropping peer authentication");
- return -1;
- }
-
- data += 6;
- buf_len -= 6;
-
- if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
- return -1;
- }
-
- if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
- !elems.wrapped_data) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
- return -1;
- }
-
- ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
- &rsne_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE");
- return -1;
- }
-
- ret = wpa_pasn_validate_rsne(&rsne_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
- return -1;
- }
-
- if (rsne_data.num_pmkid) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS: Not expecting PMKID in RSNE");
- return -1;
- }
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.fils_nonce,
- FILS_NONCE_LEN);
- os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN);
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session,
- FILS_SESSION_LEN);
-
- if (os_memcmp(pasn->fils.session, elems.fils_session,
- FILS_SESSION_LEN)) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch");
- return -1;
- }
-
- fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
-
- if (!fils_wd) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS: Failed getting wrapped data");
- return -1;
- }
-
- eapol_sm_process_erp_finish(wpa_s->eapol, wpabuf_head(fils_wd),
- wpabuf_len(fils_wd));
-
- wpabuf_free(fils_wd);
- fils_wd = NULL;
-
- if (eapol_sm_failed(wpa_s->eapol)) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed");
- return -1;
- }
-
- rmsk_len = ERP_MAX_KEY_LEN;
- ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
-
- if (ret == PMK_LEN) {
- rmsk_len = PMK_LEN;
- ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
- }
-
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK");
- return -1;
- }
-
- ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len,
- pasn->fils.nonce, anonce, NULL, 0,
- pasn->pmk, &pasn->pmk_len);
-
- forced_memzero(rmsk, sizeof(rmsk));
-
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK");
- return -1;
- }
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid,
- PMKID_LEN);
-
- wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded");
-
- wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
- pasn->pmk_len, pasn->fils.erp_pmkid,
- pasn->bssid, pasn->akmp);
-
- pasn->fils.completed = true;
- return 0;
-}
-
-#endif /* CONFIG_FILS */
-
-
-static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
-
- if (pasn->using_pmksa)
- return NULL;
-
- switch (pasn->akmp) {
- case WPA_KEY_MGMT_PASN:
- /* no wrapped data */
- return NULL;
- case WPA_KEY_MGMT_SAE:
-#ifdef CONFIG_SAE
- if (pasn->trans_seq == 0)
- return wpas_pasn_wd_sae_commit(wpa_s);
- if (pasn->trans_seq == 2)
- return wpas_pasn_wd_sae_confirm(wpa_s);
-#endif /* CONFIG_SAE */
- wpa_printf(MSG_ERROR,
- "PASN: SAE: Cannot derive wrapped data");
- return NULL;
- case WPA_KEY_MGMT_FILS_SHA256:
- case WPA_KEY_MGMT_FILS_SHA384:
-#ifdef CONFIG_FILS
- return wpas_pasn_wd_fils_auth(wpa_s);
-#endif /* CONFIG_FILS */
- case WPA_KEY_MGMT_FT_PSK:
- case WPA_KEY_MGMT_FT_IEEE8021X:
- case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
- /*
- * Wrapped data with these AKMs is optional and is only needed
- * for further validation of FT security parameters. For now do
- * not use them.
- */
- return NULL;
- default:
- wpa_printf(MSG_ERROR,
- "PASN: TODO: Wrapped data for akmp=0x%x",
- pasn->akmp);
- return NULL;
- }
-}
-
-
-static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
-{
- if (pasn->using_pmksa)
- return WPA_PASN_WRAPPED_DATA_NO;
-
- /* Note: Valid AKMP is expected to already be validated */
- switch (pasn->akmp) {
- case WPA_KEY_MGMT_SAE:
- return WPA_PASN_WRAPPED_DATA_SAE;
- case WPA_KEY_MGMT_FILS_SHA256:
- case WPA_KEY_MGMT_FILS_SHA384:
- return WPA_PASN_WRAPPED_DATA_FILS_SK;
- case WPA_KEY_MGMT_FT_PSK:
- case WPA_KEY_MGMT_FT_IEEE8021X:
- case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
- /*
- * Wrapped data with these AKMs is optional and is only needed
- * for further validation of FT security parameters. For now do
- * not use them.
- */
- return WPA_PASN_WRAPPED_DATA_NO;
- case WPA_KEY_MGMT_PASN:
- default:
- return WPA_PASN_WRAPPED_DATA_NO;
- }
-}
-
-
-static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s,
- const struct wpabuf *comeback)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
- const u8 *pmkid;
- u8 wrapped_data;
- int ret;
- u16 capab;
-
- wpa_printf(MSG_DEBUG, "PASN: Building frame 1");
-
- if (pasn->trans_seq)
- return NULL;
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- goto fail;
-
- /* Get public key */
- pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
- pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh));
- if (!pubkey) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
- goto fail;
- }
-
- wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
-
- wpa_pasn_build_auth_header(buf, pasn->bssid,
- pasn->own_addr, pasn->bssid,
- pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
-
- pmkid = NULL;
- if (wpa_key_mgmt_ft(pasn->akmp)) {
- ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, pasn->akmp,
- pasn->bssid,
- pasn->pmk_r1,
- &pasn->pmk_r1_len,
- pasn->pmk_r1_name);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: FT: Failed to derive keys");
- goto fail;
- }
-
- pmkid = pasn->pmk_r1_name;
- } else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
- struct rsn_pmksa_cache_entry *pmksa;
-
- pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
- NULL, NULL, pasn->akmp);
- if (pmksa)
- pmkid = pmksa->pmkid;
-
- /*
- * Note: Even when PMKSA is available, also add wrapped data as
- * it is possible that the PMKID is no longer valid at the AP.
- */
- wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
- }
-
- if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
- goto fail;
-
- if (!wrapped_data_buf)
- wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
-
- wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
- pubkey, true, comeback, -1);
-
- if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
- goto fail;
-
- /* Add own RNSXE */
- capab = 0;
- capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
- if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
- capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
- if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA)
- capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
- if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
- capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
- wpa_pasn_add_rsnxe(buf, capab);
-
- ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
- wpabuf_head_u8(buf) + IEEE80211_HDRLEN,
- wpabuf_len(buf) - IEEE80211_HDRLEN,
- pasn->hash);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
- goto fail;
- }
-
- pasn->trans_seq++;
-
- wpabuf_free(wrapped_data_buf);
- wpabuf_free(pubkey);
-
- wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success");
- return buf;
-fail:
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- wpabuf_free(wrapped_data_buf);
- wpabuf_free(pubkey);
- wpabuf_free(buf);
- return NULL;
-}
-
-
-static struct wpabuf * wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf, *wrapped_data_buf = NULL;
- u8 mic[WPA_PASN_MAX_MIC_LEN];
- u8 mic_len, data_len;
- const u8 *data;
- u8 *ptr;
- u8 wrapped_data;
- int ret;
-
- wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
-
- if (pasn->trans_seq != 2)
- return NULL;
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- goto fail;
-
- wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
-
- wpa_pasn_build_auth_header(buf, pasn->bssid,
- pasn->own_addr, pasn->bssid,
- pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
-
- wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
-
- if (!wrapped_data_buf)
- wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
-
- wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
- NULL, false, NULL, -1);
-
- if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
- goto fail;
- wpabuf_free(wrapped_data_buf);
- wrapped_data_buf = NULL;
-
- /* Add the MIC */
- mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
- wpabuf_put_u8(buf, WLAN_EID_MIC);
- wpabuf_put_u8(buf, mic_len);
- ptr = wpabuf_put(buf, mic_len);
-
- os_memset(ptr, 0, mic_len);
-
- data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
- data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
-
- ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
- pasn->own_addr, pasn->bssid,
- pasn->hash, mic_len * 2, data, data_len, mic);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
- goto fail;
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (wpa_s->conf->pasn_corrupt_mic) {
- wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC");
- mic[0] = ~mic[0];
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- os_memcpy(ptr, mic, mic_len);
-
- pasn->trans_seq++;
-
- wpa_printf(MSG_DEBUG, "PASN: frame 3: Success");
- return buf;
-fail:
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- wpabuf_free(wrapped_data_buf);
- wpabuf_free(buf);
- return NULL;
+ eapol_sm_notify_config(pasn->eapol, &ssid->eap, &eapol_conf);
}
static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
-
- wpa_printf(MSG_DEBUG, "PASN: Reset");
-
- crypto_ecdh_deinit(pasn->ecdh);
- pasn->ecdh = NULL;
+ struct pasn_data *pasn = &wpa_s->pasn;
wpas_pasn_cancel_auth_work(wpa_s);
wpa_s->pasn_auth_work = NULL;
-
eloop_cancel_timeout(wpas_pasn_auth_work_timeout, wpa_s, NULL);
- pasn->akmp = 0;
- pasn->cipher = 0;
- pasn->group = 0;
- pasn->trans_seq = 0;
- pasn->pmk_len = 0;
- pasn->using_pmksa = false;
-
- forced_memzero(pasn->pmk, sizeof(pasn->pmk));
- forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
- forced_memzero(&pasn->hash, sizeof(pasn->hash));
-
- wpabuf_free(pasn->beacon_rsne_rsnxe);
- pasn->beacon_rsne_rsnxe = NULL;
-
- wpabuf_free(pasn->comeback);
- pasn->comeback = NULL;
- pasn->comeback_after = 0;
-
-#ifdef CONFIG_SAE
- sae_clear_data(&pasn->sae);
-#endif /* CONFIG_SAE */
-
-#ifdef CONFIG_FILS
- os_memset(&pasn->fils, 0, sizeof(pasn->fils));
-#endif /* CONFIG_FILS*/
-
-#ifdef CONFIG_IEEE80211R
- forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
- pasn->pmk_r1_len = 0;
- os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
-#endif /* CONFIG_IEEE80211R */
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-}
-
-
-static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
- struct wpa_ie_data *rsn_data,
- struct wpa_pasn_params_data *pasn_data,
- struct wpabuf *wrapped_data)
-{
- static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
- struct wpas_pasn *pasn = &wpa_s->pasn;
-
- os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
- pasn->pmk_len = 0;
-
- if (pasn->akmp == WPA_KEY_MGMT_PASN) {
- wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
-
- pasn->pmk_len = WPA_PASN_PMK_LEN;
- os_memcpy(pasn->pmk, pasn_default_pmk,
- sizeof(pasn_default_pmk));
- return 0;
- }
-
- if (wpa_key_mgmt_ft(pasn->akmp)) {
-#ifdef CONFIG_IEEE80211R
- wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
- pasn->pmk_len = pasn->pmk_r1_len;
- os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
- pasn->using_pmksa = true;
- return 0;
-#else /* CONFIG_IEEE80211R */
- wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
- return -1;
-#endif /* CONFIG_IEEE80211R */
- }
-
- if (rsn_data->num_pmkid) {
- struct rsn_pmksa_cache_entry *pmksa;
-
- pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
- rsn_data->pmkid, NULL,
- pasn->akmp);
- if (pmksa) {
- wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");
-
- pasn->pmk_len = pmksa->pmk_len;
- os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
- pasn->using_pmksa = true;
-
- return 0;
- }
- }
-
-#ifdef CONFIG_SAE
- if (pasn->akmp == WPA_KEY_MGMT_SAE) {
- int ret;
-
- ret = wpas_pasn_wd_sae_rx(wpa_s, wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed processing SAE wrapped data");
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
- pasn->pmk_len = PMK_LEN;
- os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
-
- wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
- pasn->pmk_len, pasn->sae.pmkid,
- pasn->bssid, pasn->akmp);
- return 0;
- }
-#endif /* CONFIG_SAE */
-
-#ifdef CONFIG_FILS
- if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
- pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
- int ret;
-
- ret = wpas_pasn_wd_fils_rx(wpa_s, wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed processing FILS wrapped data");
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- return -1;
- }
-
- return 0;
- }
-#endif /* CONFIG_FILS */
-
- /* TODO: Derive PMK based on wrapped data */
- wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- return -1;
-}
-
-
-static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *own_addr,
- const u8 *bssid, int akmp, int cipher, u16 group,
- int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
- const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
- int network_id, struct wpabuf *comeback)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpa_ssid *ssid = NULL;
- struct wpabuf *frame;
- int ret;
- bool derive_kdk;
-
- /* TODO: Currently support only ECC groups */
- if (!dragonfly_suitable_group(group, 1)) {
- wpa_printf(MSG_DEBUG,
- "PASN: Reject unsuitable group %u", group);
- return -1;
- }
-
- ssid = wpa_config_get_network(wpa_s->conf, network_id);
-
- switch (akmp) {
- case WPA_KEY_MGMT_PASN:
- break;
-#ifdef CONFIG_SAE
- case WPA_KEY_MGMT_SAE:
- if (!ssid) {
- wpa_printf(MSG_DEBUG,
- "PASN: No network profile found for SAE");
- return -1;
- }
-
- if (!ieee802_11_rsnx_capab(beacon_rsnxe,
- WLAN_RSNX_CAPAB_SAE_H2E)) {
- wpa_printf(MSG_DEBUG,
- "PASN: AP does not support SAE H2E");
- return -1;
- }
-
- if (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) < 0) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed to derive PT");
- return -1;
- }
-
- pasn->sae.state = SAE_NOTHING;
- pasn->sae.send_confirm = 0;
- pasn->ssid = ssid;
- break;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
- case WPA_KEY_MGMT_FILS_SHA256:
- case WPA_KEY_MGMT_FILS_SHA384:
- pasn->ssid = ssid;
- break;
-#endif /* CONFIG_FILS */
-#ifdef CONFIG_IEEE80211R
- case WPA_KEY_MGMT_FT_PSK:
- case WPA_KEY_MGMT_FT_IEEE8021X:
- case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
- break;
-#endif /* CONFIG_IEEE80211R */
- default:
- wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp);
- return -1;
- }
-
- pasn->ecdh = crypto_ecdh_init(group);
- if (!pasn->ecdh) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
- goto fail;
- }
-
- pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len +
- beacon_rsnxe_len);
- if (!pasn->beacon_rsne_rsnxe) {
- wpa_printf(MSG_DEBUG, "PASN: Failed storing beacon RSNE/RSNXE");
- goto fail;
- }
-
- wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne, beacon_rsne_len);
- if (beacon_rsnxe && beacon_rsnxe_len)
- wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsnxe,
- beacon_rsnxe_len);
-
- pasn->akmp = akmp;
- pasn->cipher = cipher;
- pasn->group = group;
- pasn->freq = freq;
-
- derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
- ieee802_11_rsnx_capab(beacon_rsnxe,
- WLAN_RSNX_CAPAB_SECURE_LTF);
-#ifdef CONFIG_TESTING_OPTIONS
- if (!derive_kdk)
- derive_kdk = wpa_s->conf->force_kdk_derivation;
-#endif /* CONFIG_TESTING_OPTIONS */
- if (derive_kdk)
- pasn->kdk_len = WPA_KDK_MAX_LEN;
- else
- pasn->kdk_len = 0;
- wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
-
- if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
- ieee802_11_rsnx_capab(beacon_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
- pasn->secure_ltf = true;
- else
- pasn->secure_ltf = false;
-
- os_memcpy(pasn->own_addr, own_addr, ETH_ALEN);
- os_memcpy(pasn->bssid, bssid, ETH_ALEN);
-
- wpa_printf(MSG_DEBUG,
- "PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
- MAC2STR(pasn->bssid), pasn->akmp, pasn->cipher,
- pasn->group);
-
- frame = wpas_pasn_build_auth_1(wpa_s, comeback);
- if (!frame) {
- wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
- goto fail;
- }
-
- ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
- pasn->freq, 1000);
-
- wpabuf_free(frame);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
- goto fail;
- }
-
- eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL);
- return 0;
-
-fail:
- return -1;
+ wpa_pasn_reset(pasn);
}
static struct wpa_bss * wpas_pasn_allowed(struct wpa_supplicant *wpa_s,
- const u8 *bssid, int akmp, int cipher)
+ const u8 *peer_addr, int akmp,
+ int cipher)
{
struct wpa_bss *bss;
const u8 *rsne;
struct wpa_ie_data rsne_data;
int ret;
- if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+ if (os_memcmp(wpa_s->bssid, peer_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"PASN: Not doing authentication with current BSS");
return NULL;
}
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: BSS not found");
return NULL;
@@ -1456,8 +505,14 @@
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct wpa_pasn_auth_work *awork = work->ctx;
+ struct pasn_data *pasn = &wpa_s->pasn;
+ struct wpa_ssid *ssid;
struct wpa_bss *bss;
const u8 *rsne, *rsnxe;
+ const u8 *indic;
+ u16 fils_info;
+ u16 capab = 0;
+ bool derive_kdk;
int ret;
wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: deinit=%d", deinit);
@@ -1478,7 +533,7 @@
* authentication is not allowed, e.g., a connection with the AP was
* established.
*/
- bss = wpas_pasn_allowed(wpa_s, awork->bssid, awork->akmp,
+ bss = wpas_pasn_allowed(wpa_s, awork->peer_addr, awork->akmp,
awork->cipher);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: Not allowed");
@@ -1493,16 +548,115 @@
rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
- ret = wpas_pasn_start(wpa_s, awork->own_addr, awork->bssid, awork->akmp,
- awork->cipher, awork->group, bss->freq,
- rsne, *(rsne + 1) + 2,
+ derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
+ ieee802_11_rsnx_capab(rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!derive_kdk)
+ derive_kdk = wpa_s->conf->force_kdk_derivation;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (derive_kdk)
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+ else
+ pasn->kdk_len = 0;
+ wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+ if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
+ ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
+ pasn->secure_ltf = true;
+ else
+ pasn->secure_ltf = false;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ pasn->corrupt_mic = wpa_s->conf->pasn_corrupt_mic;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ pasn->rsnxe_capab = capab;
+ pasn->send_mgmt = wpas_pasn_send_mlme;
+
+ ssid = wpa_config_get_network(wpa_s->conf, awork->network_id);
+
+#ifdef CONFIG_SAE
+ if (awork->akmp == WPA_KEY_MGMT_SAE) {
+ if (!ssid) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: No network profile found for SAE");
+ goto fail;
+ }
+ pasn->pt = wpas_pasn_sae_derive_pt(ssid, awork->group);
+ if (!pasn->pt) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT");
+ goto fail;
+ }
+ pasn->network_id = ssid->id;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ /* Prepare needed information for wpas_pasn_wd_fils_auth(). */
+ if (awork->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ awork->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+ if (!ssid) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: No network block");
+ } else if (!indic || indic[1] < 2) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Missing FILS Indication IE");
+ } else {
+ fils_info = WPA_GET_LE16(indic + 2);
+ if ((fils_info & BIT(9)) && ssid) {
+ pasn->eapol = wpa_s->eapol;
+ pasn->network_id = ssid->id;
+ wpas_pasn_initiate_eapol(pasn, ssid);
+ pasn->fils_eapol = true;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS auth without PFS not supported");
+ }
+ }
+ pasn->fast_reauth = wpa_s->conf->fast_reauth;
+ }
+#endif /* CONFIG_FILS */
+
+ pasn->cb_ctx = wpa_s;
+ pasn->pmksa = wpa_sm_get_pmksa_cache(wpa_s->wpa);
+
+ if (wpa_key_mgmt_ft(awork->akmp)) {
+#ifdef CONFIG_IEEE80211R
+ ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, awork->akmp,
+ awork->peer_addr,
+ pasn->pmk_r1,
+ &pasn->pmk_r1_len,
+ pasn->pmk_r1_name);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FT: Failed to derive keys");
+ goto fail;
+ }
+#else /* CONFIG_IEEE80211R */
+ goto fail;
+#endif /* CONFIG_IEEE80211R */
+ }
+
+
+ ret = wpas_pasn_start(pasn, awork->own_addr, awork->peer_addr,
+ awork->peer_addr, awork->akmp, awork->cipher,
+ awork->group, bss->freq, rsne, *(rsne + 1) + 2,
rsnxe, rsnxe ? *(rsnxe + 1) + 2 : 0,
- awork->network_id, awork->comeback);
+ awork->comeback);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed to start PASN authentication");
goto fail;
}
+ eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL);
/* comeback token is no longer needed at this stage */
wpabuf_free(awork->comeback);
@@ -1518,7 +672,7 @@
int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
- const u8 *own_addr, const u8 *bssid,
+ const u8 *own_addr, const u8 *peer_addr,
int akmp, int cipher, u16 group, int network_id,
const u8 *comeback, size_t comeback_len)
{
@@ -1526,7 +680,7 @@
struct wpa_bss *bss;
wpa_printf(MSG_DEBUG, "PASN: Start: " MACSTR " akmp=0x%x, cipher=0x%x",
- MAC2STR(bssid), akmp, cipher);
+ MAC2STR(peer_addr), akmp, cipher);
/*
* TODO: Consider modifying the offchannel logic to handle additional
@@ -1550,7 +704,7 @@
return -1;
}
- bss = wpas_pasn_allowed(wpa_s, bssid, akmp, cipher);
+ bss = wpas_pasn_allowed(wpa_s, peer_addr, akmp, cipher);
if (!bss)
return -1;
@@ -1561,7 +715,7 @@
return -1;
os_memcpy(awork->own_addr, own_addr, ETH_ALEN);
- os_memcpy(awork->bssid, bssid, ETH_ALEN);
+ os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN);
awork->akmp = akmp;
awork->cipher = cipher;
awork->group = group;
@@ -1588,14 +742,14 @@
void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct pasn_data *pasn = &wpa_s->pasn;
if (!wpa_s->pasn.ecdh)
return;
wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
- wpas_pasn_auth_status(wpa_s, pasn->bssid, pasn->akmp, pasn->cipher,
+ wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn->akmp, pasn->cipher,
pasn->status, pasn->comeback,
pasn->comeback_after);
@@ -1604,23 +758,22 @@
static int wpas_pasn_immediate_retry(struct wpa_supplicant *wpa_s,
- struct wpas_pasn *pasn,
+ struct pasn_data *pasn,
struct wpa_pasn_params_data *params)
{
int akmp = pasn->akmp;
int cipher = pasn->cipher;
u16 group = pasn->group;
u8 own_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
- int network_id = pasn->ssid ? pasn->ssid->id : 0;
+ u8 peer_addr[ETH_ALEN];
wpa_printf(MSG_DEBUG, "PASN: Immediate retry");
os_memcpy(own_addr, pasn->own_addr, ETH_ALEN);
- os_memcpy(bssid, pasn->bssid, ETH_ALEN);
+ os_memcpy(peer_addr, pasn->peer_addr, ETH_ALEN);
wpas_pasn_reset(wpa_s);
- return wpas_pasn_auth_start(wpa_s, own_addr, bssid, akmp, cipher, group,
- network_id,
+ return wpas_pasn_auth_start(wpa_s, own_addr, peer_addr, akmp, cipher,
+ group, pasn->network_id,
params->comeback, params->comeback_len);
}
@@ -1628,279 +781,51 @@
static void wpas_pasn_deauth_cb(struct ptksa_cache_entry *entry)
{
struct wpa_supplicant *wpa_s = entry->ctx;
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
- wpas_pasn_deauthenticate(wpa_s, entry->own_addr, entry->addr);
+ /* Use a copy of the addresses from the entry to avoid issues with the
+ * entry getting freed during deauthentication processing. */
+ os_memcpy(own_addr, entry->own_addr, ETH_ALEN);
+ os_memcpy(peer_addr, entry->addr, ETH_ALEN);
+ wpas_pasn_deauthenticate(wpa_s, own_addr, peer_addr);
}
int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct ieee802_11_elems elems;
- struct wpa_ie_data rsn_data;
- struct wpa_pasn_params_data pasn_params;
- struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
- u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
- u8 mic_len;
- u16 status;
- int ret, inc_y;
- u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
- (WLAN_FC_STYPE_AUTH << 4));
+ struct pasn_data *pasn = &wpa_s->pasn;
+ struct wpa_pasn_params_data pasn_data;
+ int ret;
- if (!wpa_s->pasn_auth_work || !mgmt ||
- len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ if (!wpa_s->pasn_auth_work)
return -2;
- /* Not an Authentication frame; do nothing */
- if ((mgmt->frame_control & fc) != fc)
- return -2;
+ pasn->cb_ctx = wpa_s;
+ ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
+ if (ret == 0) {
+ ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr,
+ pasn->cipher, dot11RSNAConfigPMKLifetime,
+ &pasn->ptk,
+ wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
+ wpa_s->pasn_params ? wpa_s : NULL, pasn->akmp);
- /* Not our frame; do nothing */
- if (os_memcmp(mgmt->da, pasn->own_addr, ETH_ALEN) != 0 ||
- os_memcmp(mgmt->sa, pasn->bssid, ETH_ALEN) != 0 ||
- os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
- return -2;
-
- /* Not PASN; do nothing */
- if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
- return -2;
-
- if (mgmt->u.auth.auth_transaction !=
- host_to_le16(pasn->trans_seq + 1)) {
- wpa_printf(MSG_DEBUG,
- "PASN: RX: Invalid transaction sequence: (%u != %u)",
- le_to_host16(mgmt->u.auth.auth_transaction),
- pasn->trans_seq + 1);
- return -1;
+ if (pasn->pmksa_entry)
+ wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry);
}
- status = le_to_host16(mgmt->u.auth.status_code);
-
- if (status != WLAN_STATUS_SUCCESS &&
- status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
- wpa_printf(MSG_DEBUG,
- "PASN: Authentication rejected - status=%u", status);
- goto fail;
- }
-
- if (ieee802_11_parse_elems(mgmt->u.auth.variable,
- len - offsetof(struct ieee80211_mgmt,
- u.auth.variable),
- &elems, 0) == ParseFailed) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed parsing Authentication frame");
- goto fail;
- }
-
- /* Check that the MIC IE exists. Save it and zero out the memory */
- mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
- if (status == WLAN_STATUS_SUCCESS) {
- if (!elems.mic || elems.mic_len != mic_len) {
- wpa_printf(MSG_DEBUG,
- "PASN: Invalid MIC. Expecting len=%u",
- mic_len);
- goto fail;
- } else {
- os_memcpy(mic, elems.mic, mic_len);
- /* TODO: Clean this up.. Should not be modifying the
- * received message buffer. */
- os_memset((u8 *) elems.mic, 0, mic_len);
- }
- }
-
- if (!elems.pasn_params || !elems.pasn_params_len) {
- wpa_printf(MSG_DEBUG,
- "PASN: Missing PASN Parameters IE");
- goto fail;
- }
-
- ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
- elems.pasn_params_len + 3,
- true, &pasn_params);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed validation PASN of Parameters IE");
- goto fail;
- }
-
- if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
- wpa_printf(MSG_DEBUG,
- "PASN: Authentication temporarily rejected");
-
- if (pasn_params.comeback && pasn_params.comeback_len) {
- wpa_printf(MSG_DEBUG,
- "PASN: Comeback token available. After=%u",
- pasn_params.after);
-
- if (!pasn_params.after)
- return wpas_pasn_immediate_retry(wpa_s, pasn,
- &pasn_params);
-
- pasn->comeback = wpabuf_alloc_copy(
- pasn_params.comeback, pasn_params.comeback_len);
- if (pasn->comeback)
- pasn->comeback_after = pasn_params.after;
- }
-
- pasn->status = status;
- goto fail;
- }
-
- ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
- &rsn_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
- goto fail;
- }
-
- ret = wpa_pasn_validate_rsne(&rsn_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
- goto fail;
- }
-
- if (pasn->akmp != rsn_data.key_mgmt ||
- pasn->cipher != rsn_data.pairwise_cipher) {
- wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
- goto fail;
- }
-
- if (pasn->group != pasn_params.group) {
- wpa_printf(MSG_DEBUG, "PASN: Mismatch in group");
- goto fail;
- }
-
- if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
- wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
- goto fail;
- }
-
- if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
- inc_y = 1;
- } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
- pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
- inc_y = 0;
- } else {
- wpa_printf(MSG_DEBUG,
- "PASN: Invalid first octet in pubkey=0x%x",
- pasn_params.pubkey[0]);
- goto fail;
- }
-
- secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
- pasn_params.pubkey + 1,
- pasn_params.pubkey_len - 1);
-
- if (!secret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
- goto fail;
- }
-
- if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems,
- WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
-
- if (!wrapped_data) {
- wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
- goto fail;
- }
- }
-
- ret = wpas_pasn_set_pmk(wpa_s, &rsn_data, &pasn_params, wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK");
- goto fail;
- }
-
- ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
- pasn->own_addr, pasn->bssid,
- wpabuf_head(secret), wpabuf_len(secret),
- &pasn->ptk, pasn->akmp, pasn->cipher,
- pasn->kdk_len);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
- goto fail;
- }
-
- if (pasn->secure_ltf) {
- ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed to derive LTF keyseed");
- goto fail;
- }
- }
-
- wpabuf_free(wrapped_data);
- wrapped_data = NULL;
- wpabuf_free(secret);
- secret = NULL;
-
- /* Verify the MIC */
- ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
- pasn->bssid, pasn->own_addr,
- wpabuf_head(pasn->beacon_rsne_rsnxe),
- wpabuf_len(pasn->beacon_rsne_rsnxe),
- (u8 *) &mgmt->u.auth,
- len - offsetof(struct ieee80211_mgmt, u.auth),
- out_mic);
-
- wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
- if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
- wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
- goto fail;
- }
-
- pasn->trans_seq++;
-
- wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
-
- frame = wpas_pasn_build_auth_3(wpa_s);
- if (!frame) {
- wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
- goto fail;
- }
-
- ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
- pasn->freq, 100);
- wpabuf_free(frame);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
- goto fail;
- }
-
- wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
-
- ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->bssid,
- pasn->cipher, dot11RSNAConfigPMKLifetime, &pasn->ptk,
- wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
- wpa_s->pasn_params ? wpa_s : NULL);
-
forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
- pasn->status = WLAN_STATUS_SUCCESS;
- return 0;
-fail:
- wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating");
- wpabuf_free(wrapped_data);
- wpabuf_free(secret);
+ if (ret == -1) {
+ wpas_pasn_auth_stop(wpa_s);
+ wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE);
+ }
- /*
- * TODO: In case of an error the standard allows to silently drop
- * the frame and terminate the authentication exchange. However, better
- * reply to the AP with an error status.
- */
- if (status == WLAN_STATUS_SUCCESS)
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- else
- pasn->status = status;
+ if (ret == 1)
+ ret = wpas_pasn_immediate_retry(wpa_s, pasn, &pasn_data);
- wpas_pasn_auth_stop(wpa_s);
-
- wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE);
- return -1;
+ return ret;
}
@@ -1961,12 +886,8 @@
const u8 *data, size_t data_len, u8 acked)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- const struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) data;
- u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
- (WLAN_FC_STYPE_AUTH << 4));
-
- wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
+ struct pasn_data *pasn = &wpa_s->pasn;
+ int ret;
if (!wpa_s->pasn_auth_work) {
wpa_printf(MSG_DEBUG,
@@ -1974,87 +895,46 @@
return -1;
}
- if (!mgmt ||
- data_len < offsetof(struct ieee80211_mgmt, u.auth.variable))
- return -1;
+ ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked);
+ if (ret != 1)
+ return ret;
- /* Not an authentication frame; do nothing */
- if ((mgmt->frame_control & fc) != fc)
- return -1;
-
- /* Not our frame; do nothing */
- if (os_memcmp(mgmt->da, pasn->bssid, ETH_ALEN) ||
- os_memcmp(mgmt->sa, pasn->own_addr, ETH_ALEN) ||
- os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN))
- return -1;
-
- /* Not PASN; do nothing */
- if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
- return -1;
-
- if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
- wpa_printf(MSG_ERROR,
- "PASN: Invalid transaction sequence: (%u != %u)",
- pasn->trans_seq,
- le_to_host16(mgmt->u.auth.auth_transaction));
+ if (!wpa_s->pasn_params) {
+ wpas_pasn_auth_stop(wpa_s);
return 0;
}
- wpa_printf(MSG_ERROR,
- "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
- acked);
-
- /*
- * Even if the frame was not acked, do not treat this is an error, and
- * try to complete the flow, relying on the PASN timeout callback to
- * clean up.
- */
- if (pasn->trans_seq == 3) {
- wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
- MAC2STR(pasn->bssid));
- /*
- * Either frame was not ACKed or it was ACKed but the trans_seq
- * != 1, i.e., not expecting an RX frame, so we are done.
- */
- if (!wpa_s->pasn_params) {
- wpas_pasn_auth_stop(wpa_s);
- return 0;
- }
-
- wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr,
- pasn->bssid, pasn->cipher,
- pasn->akmp);
- wpas_pasn_auth_stop(wpa_s);
-
- wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
- }
+ wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr, pasn->peer_addr,
+ pasn->cipher, pasn->akmp);
+ wpas_pasn_auth_stop(wpa_s);
+ wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
return 0;
}
int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *own_addr,
- const u8 *bssid)
+ const u8 *peer_addr)
{
struct wpa_bss *bss;
struct wpabuf *buf;
struct ieee80211_mgmt *deauth;
int ret;
- if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+ if (os_memcmp(wpa_s->bssid, peer_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"PASN: Cannot deauthenticate from current BSS");
return -1;
}
- wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, bssid, 0, 0, NULL, 0,
- NULL, 1);
+ wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, peer_addr, 0, 0, NULL,
+ 0, NULL, 1);
wpa_printf(MSG_DEBUG, "PASN: deauth: Flushing all PTKSA entries for "
- MACSTR, MAC2STR(bssid));
- ptksa_cache_flush(wpa_s->ptksa, bssid, WPA_CIPHER_NONE);
+ MACSTR, MAC2STR(peer_addr));
+ ptksa_cache_flush(wpa_s->ptksa, peer_addr, WPA_CIPHER_NONE);
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: deauth: BSS not found");
return -1;
@@ -2072,9 +952,9 @@
deauth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_DEAUTH << 4));
- os_memcpy(deauth->da, bssid, ETH_ALEN);
+ os_memcpy(deauth->da, peer_addr, ETH_ALEN);
os_memcpy(deauth->sa, own_addr, ETH_ALEN);
- os_memcpy(deauth->bssid, bssid, ETH_ALEN);
+ os_memcpy(deauth->bssid, peer_addr, ETH_ALEN);
deauth->u.deauth.reason_code =
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 3ae99da..3aa6675 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -131,7 +131,7 @@
}
-static int wpa_supplicant_set_key(void *wpa_s, enum wpa_alg alg,
+static int wpa_supplicant_set_key(void *wpa_s, int link_id, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
@@ -318,7 +318,7 @@
}
os_memset(&wpa_s, 0, sizeof(wpa_s));
- wpa_s.conf = wpa_config_read(argv[1], NULL);
+ wpa_s.conf = wpa_config_read(argv[1], NULL, false);
if (wpa_s.conf == NULL) {
printf("Failed to parse configuration file '%s'.\n", argv[1]);
return -1;
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index faa97e0..88d7e6e 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -299,6 +299,8 @@
return -1;
}
+ wpa_s->wps_scan_done = false;
+
return 0;
}
@@ -476,25 +478,28 @@
wpa_s->p2p_invite_go_freq > 0) {
if (wpa_s->p2p_invite_go_freq == 2 ||
wpa_s->p2p_invite_go_freq == 5) {
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred band %d GHz during invitation",
- wpa_s->p2p_invite_go_freq);
enum hostapd_hw_mode mode;
- if (wpa_s->hw.modes == NULL)
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Scan only GO preferred band %d GHz during invitation",
+ wpa_s->p2p_invite_go_freq);
+
+ if (!wpa_s->hw.modes)
return;
mode = wpa_s->p2p_invite_go_freq == 5 ?
- HOSTAPD_MODE_IEEE80211A :
- HOSTAPD_MODE_IEEE80211G;
+ HOSTAPD_MODE_IEEE80211A :
+ HOSTAPD_MODE_IEEE80211G;
if (wpa_s->p2p_in_invitation <= 2)
wpa_add_scan_freqs_list(wpa_s, mode,
params, false,
- true);
- if (params->freqs == NULL ||
- (params->freqs && params->freqs[0] == 0))
+ false, true);
+ if (!params->freqs || params->freqs[0] == 0)
wpa_add_scan_freqs_list(wpa_s, mode,
params, false,
- false);
+ false, false);
} else {
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Scan only GO preferred frequency %d MHz during invitation",
wpa_s->p2p_invite_go_freq);
params->freqs = os_calloc(2, sizeof(int));
if (params->freqs)
@@ -757,7 +762,8 @@
int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
enum hostapd_hw_mode band,
- struct wpa_driver_scan_params *params, bool is_6ghz,
+ struct wpa_driver_scan_params *params,
+ bool is_6ghz, bool only_6ghz_psc,
bool exclude_radar)
{
/* Include only supported channels for the specified band */
@@ -766,7 +772,7 @@
int *freqs, i;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, is_6ghz);
- if (!mode)
+ if (!mode || !mode->num_channels)
return -1;
if (params->freqs) {
@@ -783,8 +789,14 @@
for (i = 0; i < mode->num_channels; i++) {
if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
continue;
- if (exclude_radar && (mode->channels[i].flag & HOSTAPD_CHAN_RADAR))
+ if (exclude_radar &&
+ (mode->channels[i].flag & HOSTAPD_CHAN_RADAR))
continue;
+
+ if (is_6ghz && only_6ghz_psc &&
+ !is_6ghz_psc_frequency(mode->channels[i].freq))
+ continue;
+
params->freqs[num_chans++] = mode->channels[i].freq;
}
params->freqs[num_chans] = 0;
@@ -803,13 +815,13 @@
if (wpa_s->setband_mask & WPA_SETBAND_5G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
- false, false);
+ false, false, false);
if (wpa_s->setband_mask & WPA_SETBAND_2G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
- false, false);
+ false, false, false);
if (wpa_s->setband_mask & WPA_SETBAND_6G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
- true, false);
+ true, false, false);
}
@@ -1318,7 +1330,8 @@
params.freqs = os_calloc(num + 1, sizeof(int));
if (params.freqs) {
- num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+ num = get_shared_radio_freqs(wpa_s, params.freqs, num,
+ false);
if (num > 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
"current operating channels since "
@@ -1408,7 +1421,13 @@
params.freqs = os_calloc(num + 1, sizeof(int));
if (params.freqs) {
- num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+ /*
+ * Exclude the operating frequency of the current
+ * interface since we're looking to transition off of
+ * it.
+ */
+ num = get_shared_radio_freqs(wpa_s, params.freqs, num,
+ true);
if (num > 0 && num == wpa_s->num_multichan_concurrent) {
wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
} else {
@@ -1418,18 +1437,9 @@
}
}
- if (!params.freqs &&
- (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning) &&
- !is_p2p_allow_6ghz(wpa_s->global->p2p) &&
- is_6ghz_supported(wpa_s)) {
- /* Exclude 6 GHz channels from the full scan for P2P connection
- * since the 6 GHz band is disabled for P2P uses. */
- wpa_printf(MSG_DEBUG,
- "P2P: 6 GHz disabled - update the scan frequency list");
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms, false, false);
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms, false, false);
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211AD, ¶ms, false, false);
- }
+ if (!params.freqs && is_6ghz_supported(wpa_s) &&
+ (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning))
+ wpas_p2p_scan_freqs(wpa_s, ¶ms, true);
#endif /* CONFIG_P2P */
ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
@@ -2418,6 +2428,8 @@
{ -1, 780000 } /* SNR > 37 */
};
+/* EHT needs to be enabled in order to achieve MCS12 and MCS13 rates. */
+#define EHT_MCS 12
static const struct minsnr_bitrate_entry he20_table[] = {
{ 0, 0 },
@@ -2433,7 +2445,9 @@
{ 31, 114700 }, /* HE20 MCS9 */
{ 34, 129000 }, /* HE20 MCS10 */
{ 36, 143400 }, /* HE20 MCS11 */
- { -1, 143400 } /* SNR > 29 */
+ { 39, 154900 }, /* EHT20 MCS12 */
+ { 42, 172100 }, /* EHT20 MCS13 */
+ { -1, 172100 } /* SNR > 42 */
};
static const struct minsnr_bitrate_entry he40_table[] = {
@@ -2450,7 +2464,9 @@
{ 34, 229400 }, /* HE40 MCS9 */
{ 37, 258100 }, /* HE40 MCS10 */
{ 39, 286800 }, /* HE40 MCS11 */
- { -1, 286800 } /* SNR > 34 */
+ { 42, 309500 }, /* EHT40 MCS12 */
+ { 45, 344100 }, /* EHT40 MCS13 */
+ { -1, 344100 } /* SNR > 45 */
};
static const struct minsnr_bitrate_entry he80_table[] = {
@@ -2467,7 +2483,9 @@
{ 37, 480400 }, /* HE80 MCS9 */
{ 40, 540400 }, /* HE80 MCS10 */
{ 42, 600500 }, /* HE80 MCS11 */
- { -1, 600500 } /* SNR > 37 */
+ { 45, 648500 }, /* EHT80 MCS12 */
+ { 48, 720600 }, /* EHT80 MCS13 */
+ { -1, 720600 } /* SNR > 48 */
};
@@ -2485,9 +2503,31 @@
{ 40, 960800 }, /* HE160 MCS9 */
{ 43, 1080900 }, /* HE160 MCS10 */
{ 45, 1201000 }, /* HE160 MCS11 */
- { -1, 1201000 } /* SNR > 37 */
+ { 48, 1297100 }, /* EHT160 MCS12 */
+ { 51, 1441200 }, /* EHT160 MCS13 */
+ { -1, 1441200 } /* SNR > 51 */
};
+/* See IEEE P802.11be/D2.0, Table 36-86: EHT-MCSs for 4x996-tone RU, NSS,u = 1
+ */
+static const struct minsnr_bitrate_entry eht320_table[] = {
+ { 0, 0 },
+ { 14, 144100 }, /* EHT320 MCS0 */
+ { 17, 288200 }, /* EHT320 MCS1 */
+ { 21, 432400 }, /* EHT320 MCS2 */
+ { 23, 576500 }, /* EHT320 MCS3 */
+ { 27, 864700 }, /* EHT320 MCS4 */
+ { 30, 1152900 }, /* EHT320 MCS5 */
+ { 32, 1297100 }, /* EHT320 MCS6 */
+ { 37, 1441200 }, /* EHT320 MCS7 */
+ { 41, 1729400 }, /* EHT320 MCS8 */
+ { 43, 1921500 }, /* EHT320 MCS9 */
+ { 46, 2161800 }, /* EHT320 MCS10 */
+ { 48, 2401900 }, /* EHT320 MCS11 */
+ { 51, 2594100 }, /* EHT320 MCS12 */
+ { 54, 2882400 }, /* EHT320 MCS13 */
+ { -1, 2882400 } /* SNR > 54 */
+};
static unsigned int interpolate_rate(int snr, int snr0, int snr1,
int rate0, int rate1)
@@ -2539,17 +2579,18 @@
}
-static unsigned int max_he_rate(const struct minsnr_bitrate_entry table[],
- int snr)
+static unsigned int max_he_eht_rate(const struct minsnr_bitrate_entry table[],
+ int snr, bool eht)
{
const struct minsnr_bitrate_entry *prev, *entry = table;
- while (entry->minsnr != -1 && snr >= entry->minsnr)
+ while (entry->minsnr != -1 && snr >= entry->minsnr &&
+ (eht || entry - table <= EHT_MCS))
entry++;
if (entry == table)
return 0;
prev = entry - 1;
- if (entry->minsnr == -1)
+ if (entry->minsnr == -1 || (!eht && entry - table > EHT_MCS))
return prev->bitrate;
return interpolate_rate(snr, prev->minsnr, entry->minsnr,
prev->bitrate, entry->bitrate);
@@ -2687,8 +2728,11 @@
if (hw_mode && hw_mode->he_capab[IEEE80211_MODE_INFRA].he_supported) {
/* Use +2 to assume HE is always faster than HT/VHT */
struct ieee80211_he_capabilities *he;
+ struct ieee80211_eht_capabilities *eht;
struct he_capabilities *own_he;
- u8 cw;
+ u8 cw, boost = 2;
+ const u8 *eht_ie;
+ bool is_eht = false;
ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_CAPABILITIES);
if (!ie || (ie[1] < 1 + IEEE80211_HE_CAPAB_MIN_LEN))
@@ -2696,7 +2740,18 @@
he = (struct ieee80211_he_capabilities *) &ie[3];
own_he = &hw_mode->he_capab[IEEE80211_MODE_INFRA];
- tmp = max_he_rate(he20_table, snr) + 2;
+ /* Use +3 to assume EHT is always faster than HE */
+ if (hw_mode->eht_capab[IEEE80211_MODE_INFRA].eht_supported) {
+ eht_ie = get_ie_ext(ies, ies_len,
+ WLAN_EID_EXT_EHT_CAPABILITIES);
+ if (eht_ie &&
+ (eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN)) {
+ is_eht = true;
+ boost = 3;
+ }
+ }
+
+ tmp = max_he_eht_rate(he20_table, snr, is_eht) + boost;
if (tmp > est)
est = tmp;
@@ -2705,14 +2760,14 @@
if (cw &
(IS_2P4GHZ(freq) ? HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
- tmp = max_he_rate(he40_table, snr) + 2;
+ tmp = max_he_eht_rate(he40_table, snr, is_eht) + boost;
if (tmp > est)
est = tmp;
}
if (!IS_2P4GHZ(freq) &&
(cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
- tmp = max_he_rate(he80_table, snr) + 2;
+ tmp = max_he_eht_rate(he80_table, snr, is_eht) + boost;
if (tmp > est)
est = tmp;
}
@@ -2720,7 +2775,20 @@
if (!IS_2P4GHZ(freq) &&
(cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))) {
- tmp = max_he_rate(he160_table, snr) + 2;
+ tmp = max_he_eht_rate(he160_table, snr, is_eht) + boost;
+ if (tmp > est)
+ est = tmp;
+ }
+
+ if (!is_eht)
+ return est;
+
+ eht = (struct ieee80211_eht_capabilities *) &eht_ie[3];
+
+ if (is_6ghz_freq(freq) &&
+ (eht->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+ EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
+ tmp = max_he_eht_rate(eht320_table, snr, true);
if (tmp > est)
est = tmp;
}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 52d2696..30f4395 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -92,7 +92,7 @@
int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
enum hostapd_hw_mode band,
struct wpa_driver_scan_params *params,
- bool is_6ghz,
+ bool is_6ghz, bool only_6ghz_psc,
bool exclude_radar);
#endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index a847e2f..406e357 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "utils/eloop.h"
+#include "utils/ext_password.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/ocv.h"
@@ -53,7 +54,7 @@
}
-static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
+static int sme_set_sae_group(struct wpa_supplicant *wpa_s, bool external)
{
int *groups = wpa_s->conf->sae_groups;
int default_groups[] = { 19, 20, 21, 0 };
@@ -72,7 +73,8 @@
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);
- wpa_s->sme.sae.akmp = wpa_s->key_mgmt;
+ wpa_s->sme.sae.akmp = external ?
+ wpa_s->sme.ext_auth_key_mgmt : wpa_s->key_mgmt;
return 0;
}
wpa_s->sme.sae_group_index++;
@@ -84,17 +86,22 @@
static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- const u8 *bssid, int external,
+ const u8 *bssid,
+ const u8 *mld_addr,
+ int external,
int reuse, int *ret_use_pt,
bool *ret_use_pk)
{
struct wpabuf *buf;
size_t len;
- const char *password;
+ char *password = NULL;
struct wpa_bss *bss;
int use_pt = 0;
bool use_pk = false;
u8 rsnxe_capa = 0;
+ int key_mgmt = external ? wpa_s->sme.ext_auth_key_mgmt :
+ wpa_s->key_mgmt;
+ const u8 *addr = mld_addr ? mld_addr : bssid;
if (ret_use_pt)
*ret_use_pt = 0;
@@ -106,7 +113,7 @@
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
if (!buf)
- return NULL;
+ goto fail;
if (!external) {
wpabuf_put_le16(buf, 1); /* Transaction seq# */
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
@@ -116,25 +123,58 @@
}
#endif /* CONFIG_TESTING_OPTIONS */
- password = ssid->sae_password;
- if (!password)
- password = ssid->passphrase;
+ if (ssid->sae_password) {
+ password = os_strdup(ssid->sae_password);
+ if (!password) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "SAE: Failed to allocate password");
+ goto fail;
+ }
+ }
+ if (!password && ssid->passphrase) {
+ password = os_strdup(ssid->passphrase);
+ if (!password) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "SAE: Failed to allocate password");
+ goto fail;
+ }
+ }
+ if (!password && ssid->ext_psk) {
+ struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+ ssid->ext_psk);
+
+ if (!pw) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "SAE: No password found from external storage");
+ goto fail;
+ }
+
+ password = os_malloc(wpabuf_len(pw) + 1);
+ if (!password) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "SAE: Failed to allocate password");
+ goto fail;
+ }
+ os_memcpy(password, wpabuf_head(pw), wpabuf_len(pw));
+ password[wpabuf_len(pw)] = '\0';
+ ext_password_free(pw);
+ }
if (!password) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
- return NULL;
+ goto fail;
}
if (reuse && wpa_s->sme.sae.tmp &&
- os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
+ os_memcmp(addr, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"SAE: Reuse previously generated PWE on a retry with the same AP");
use_pt = wpa_s->sme.sae.h2e;
use_pk = wpa_s->sme.sae.pk;
goto reuse_data;
}
- if (sme_set_sae_group(wpa_s) < 0) {
+ if (sme_set_sae_group(wpa_s, external) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
- return NULL;
+ goto fail;
}
bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
@@ -152,10 +192,11 @@
rsnxe_capa = rsnxe[2];
}
- if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3)
+ if (ssid->sae_password_id &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
use_pt = 1;
- if (wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt) &&
- wpa_s->conf->sae_pwe != 3)
+ if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
use_pt = 1;
#ifdef CONFIG_SAE_PK
if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
@@ -171,43 +212,45 @@
if (ssid->sae_pk == SAE_PK_MODE_ONLY && !use_pk) {
wpa_printf(MSG_DEBUG,
"SAE: Cannot use PK with the selected AP");
- return NULL;
+ goto fail;
}
#endif /* CONFIG_SAE_PK */
- if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
+ if (use_pt || wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ wpa_s->conf->sae_pwe == SAE_PWE_BOTH) {
use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
- if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id ||
- wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
- wpa_s->conf->sae_pwe != 3 &&
+ if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ ssid->sae_password_id ||
+ wpa_key_mgmt_sae_ext_key(key_mgmt)) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
!use_pt) {
wpa_printf(MSG_DEBUG,
"SAE: Cannot use H2E with the selected AP");
- return NULL;
+ goto fail;
}
}
if (use_pt &&
sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
- wpa_s->own_addr, bssid,
+ wpa_s->own_addr, addr,
wpa_s->sme.sae_rejected_groups, NULL) < 0)
- return NULL;
+ goto fail;
if (!use_pt &&
sae_prepare_commit(wpa_s->own_addr, bssid,
(u8 *) password, os_strlen(password),
&wpa_s->sme.sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
- return NULL;
+ goto fail;
}
if (wpa_s->sme.sae.tmp) {
- os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN);
+ os_memcpy(wpa_s->sme.sae.tmp->bssid, addr, ETH_ALEN);
if (use_pt && use_pk)
wpa_s->sme.sae.pk = 1;
#ifdef CONFIG_SAE_PK
os_memcpy(wpa_s->sme.sae.tmp->own_addr, wpa_s->own_addr,
ETH_ALEN);
- os_memcpy(wpa_s->sme.sae.tmp->peer_addr, bssid, ETH_ALEN);
+ os_memcpy(wpa_s->sme.sae.tmp->peer_addr, addr, ETH_ALEN);
sae_pk_set_password(&wpa_s->sme.sae, password);
#endif /* CONFIG_SAE_PK */
}
@@ -218,7 +261,7 @@
len += 4 + os_strlen(ssid->sae_password_id);
buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
if (buf == NULL)
- return NULL;
+ goto fail;
if (!external) {
wpabuf_put_le16(buf, 1); /* Transaction seq# */
if (use_pk)
@@ -231,14 +274,19 @@
if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
ssid->sae_password_id) < 0) {
wpabuf_free(buf);
- return NULL;
+ goto fail;
}
if (ret_use_pt)
*ret_use_pt = use_pt;
if (ret_use_pk)
*ret_use_pk = use_pk;
+ str_clear_free(password);
return buf;
+
+fail:
+ str_clear_free(password);
+ return NULL;
}
@@ -325,6 +373,188 @@
}
+static bool wpas_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpabuf *mlbuf;
+ const u8 *rnr_ie, *pos;
+ u8 ml_ie_len, rnr_ie_len;
+ const struct ieee80211_eht_ml *eht_ml;
+ const struct eht_ml_basic_common_info *ml_basic_common_info;
+ u8 i;
+ const u16 control =
+ host_to_le16(MULTI_LINK_CONTROL_TYPE_BASIC |
+ BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+ BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+ BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA);
+ bool ret = false;
+
+ if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO))
+ return false;
+
+ mlbuf = wpa_bss_defrag_mle(bss, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mlbuf) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No ML element");
+ return false;
+ }
+
+ ml_ie_len = wpabuf_len(mlbuf);
+
+ /* control + common info len + MLD address + MLD link information */
+ if (ml_ie_len < 2 + 1 + ETH_ALEN + 1)
+ goto out;
+
+ eht_ml = wpabuf_head(mlbuf);
+ if ((eht_ml->ml_control & control) != control) {
+ wpa_printf(MSG_DEBUG, "MLD: Unexpected ML element control=0x%x",
+ eht_ml->ml_control);
+ goto out;
+ }
+
+ ml_basic_common_info =
+ (const struct eht_ml_basic_common_info *) eht_ml->variable;
+
+ /* common info length should be valid (self, mld_addr, link_id) */
+ if (ml_basic_common_info->len < 1 + ETH_ALEN + 1)
+ goto out;
+
+ /* get the MLD address and MLD link ID */
+ os_memcpy(wpa_s->ap_mld_addr, ml_basic_common_info->mld_addr,
+ ETH_ALEN);
+ wpa_s->mlo_assoc_link_id = ml_basic_common_info->variable[0] &
+ EHT_ML_LINK_ID_MSK;
+
+ os_memcpy(wpa_s->links[wpa_s->mlo_assoc_link_id].bssid, bss->bssid,
+ ETH_ALEN);
+ wpa_s->links[wpa_s->mlo_assoc_link_id].freq = bss->freq;
+
+ wpa_printf(MSG_DEBUG, "MLD: address=" MACSTR ", link ID=%u",
+ MAC2STR(wpa_s->ap_mld_addr), wpa_s->mlo_assoc_link_id);
+
+ wpa_s->valid_links = BIT(wpa_s->mlo_assoc_link_id);
+
+ rnr_ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
+ if (!rnr_ie) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RNR element");
+ ret = true;
+ goto out;
+ }
+
+ rnr_ie_len = rnr_ie[1];
+ pos = rnr_ie + 2;
+
+ while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) {
+ const struct ieee80211_neighbor_ap_info *ap_info =
+ (const struct ieee80211_neighbor_ap_info *) pos;
+ const u8 *data = ap_info->data;
+ size_t len = sizeof(struct ieee80211_neighbor_ap_info) +
+ ap_info->tbtt_info_len;
+
+ wpa_printf(MSG_DEBUG, "MLD: op_class=%u, channel=%u",
+ ap_info->op_class, ap_info->channel);
+
+ if (len > rnr_ie_len)
+ break;
+
+ if (ap_info->tbtt_info_len < 16) {
+ rnr_ie_len -= len;
+ pos += len;
+ continue;
+ }
+
+ data += 13;
+
+ wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
+ *data, *(data + 1) & 0xF);
+
+ if (*data) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Reported link not part of MLD");
+ } else {
+ struct wpa_bss *neigh_bss =
+ wpa_bss_get_bssid(wpa_s, ap_info->data + 1);
+ u8 link_id = *(data + 1) & 0xF;
+
+ if (neigh_bss) {
+ if (wpa_scan_res_match(wpa_s, 0, neigh_bss,
+ wpa_s->current_ssid,
+ 1, 0)) {
+ wpa_s->valid_links |= BIT(link_id);
+ os_memcpy(wpa_s->links[link_id].bssid,
+ ap_info->data + 1, ETH_ALEN);
+ wpa_s->links[link_id].freq =
+ neigh_bss->freq;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Neighbor doesn't match current SSID - skip link");
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Neighbor not found in scan");
+ }
+ }
+
+ rnr_ie_len -= len;
+ pos += len;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%x", wpa_s->valid_links);
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(wpa_s->valid_links & BIT(i)))
+ continue;
+
+ wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
+ i, MAC2STR(wpa_s->links[i].bssid));
+ }
+
+ ret = true;
+out:
+ wpabuf_free(mlbuf);
+ return ret;
+}
+
+
+static void wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data,
+ int ie_offset)
+{
+ struct ieee802_11_elems elems;
+ const u8 *mld_addr;
+
+ if (!wpa_s->valid_links)
+ return;
+
+ if (ieee802_11_parse_elems(data->auth.ies + ie_offset,
+ data->auth.ies_len - ie_offset,
+ &elems, 0) != ParseOK) {
+ wpa_printf(MSG_DEBUG, "MLD: Failed parsing elements");
+ goto out;
+ }
+
+ if (!elems.basic_mle || !elems.basic_mle_len) {
+ wpa_printf(MSG_DEBUG, "MLD: No ML element in authentication");
+ goto out;
+ }
+
+ mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+ if (!mld_addr)
+ goto out;
+
+ wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr));
+
+ if (os_memcmp(wpa_s->ap_mld_addr, mld_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "MLD: Unexpected MLD address (expected "
+ MACSTR ")", MAC2STR(wpa_s->ap_mld_addr));
+ goto out;
+ }
+
+ return;
+out:
+ wpa_printf(MSG_DEBUG, "MLD: Authentication - clearing MLD state");
+ wpas_reset_mlo_info(wpa_s);
+}
+
+
static void sme_send_authentication(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
int start)
@@ -369,6 +599,13 @@
params.ssid_len = bss->ssid_len;
params.p2p = ssid->p2p_group;
+ if (wpas_ml_element(wpa_s, bss)) {
+ wpa_printf(MSG_DEBUG, "MLD: In authentication");
+ params.mld = true;
+ params.mld_link_id = wpa_s->mlo_assoc_link_id;
+ params.ap_mld_addr = wpa_s->ap_mld_addr;
+ }
+
if (wpa_s->sme.ssid_len != params.ssid_len ||
os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
wpa_s->sme.prev_bssid_set = 0;
@@ -413,8 +650,13 @@
#endif /* CONFIG_DPP */
} else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
wpa_key_mgmt_sae(ied.key_mgmt)) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
- params.auth_alg = WPA_AUTH_ALG_SAE;
+ if (wpas_is_sae_avoided(wpa_s, ssid, &ied)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SAE enabled, but disallowing SAE auth_alg without PMF");
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
+ params.auth_alg = WPA_AUTH_ALG_SAE;
+ }
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"SAE enabled, but target BSS does not advertise SAE AKM for RSN");
@@ -449,7 +691,9 @@
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,
+ if (pmksa_cache_set_current(wpa_s->wpa, NULL,
+ params.mld ? params.ap_mld_addr :
+ bss->bssid,
wpa_s->current_ssid,
try_opportunistic, cache_id,
0) == 0)
@@ -457,7 +701,8 @@
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_s->sme.assoc_req_ie_len,
+ false)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites");
wpas_connect_work_done(wpa_s);
@@ -470,7 +715,8 @@
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_s->sme.assoc_req_ie_len,
+ false)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites");
wpas_connect_work_done(wpa_s);
@@ -490,7 +736,8 @@
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_s->sme.assoc_req_ie,
- &wpa_s->sme.assoc_req_ie_len)) {
+ &wpa_s->sme.assoc_req_ie_len,
+ false)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites (no "
"scan results)");
@@ -572,7 +819,7 @@
if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used &&
os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
- wpa_sm_has_ptk(wpa_s->wpa)) {
+ wpa_sm_has_ft_keys(wpa_s->wpa, md)) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
"over-the-air");
params.auth_alg = WPA_AUTH_ALG_FT;
@@ -752,7 +999,10 @@
#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,
+ pmksa_cache_set_current(wpa_s->wpa, NULL,
+ params.mld ? params.ap_mld_addr :
+ bss->bssid,
+ ssid, 0,
NULL,
wpa_key_mgmt_sae(wpa_s->key_mgmt) ?
wpa_s->key_mgmt :
@@ -767,7 +1017,10 @@
if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
if (start)
resp = sme_auth_build_sae_commit(wpa_s, ssid,
- bss->bssid, 0,
+ bss->bssid,
+ params.mld ?
+ params.ap_mld_addr :
+ NULL, 0,
start == 2, NULL,
NULL);
else
@@ -846,7 +1099,9 @@
goto no_fils;
}
- if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+ if (pmksa_cache_set_current(wpa_s->wpa, NULL,
+ params.mld ? params.ap_mld_addr :
+ bss->bssid,
ssid, 0,
wpa_bss_get_fils_cache_id(bss),
0) == 0)
@@ -899,7 +1154,7 @@
*/
if (wpa_s->num_multichan_concurrent < 2) {
int freq, num;
- num = get_shared_radio_freqs(wpa_s, &freq, 1);
+ num = get_shared_radio_freqs(wpa_s, &freq, 1, false);
if (num > 0 && freq > 0 && freq != params.freq) {
wpa_printf(MSG_DEBUG,
"Conflicting frequency found (%d != %d)",
@@ -984,6 +1239,7 @@
wpa_s->rsnxe_len = 0;
sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
+ wpas_notify_auth_changed(wpa_s);
}
@@ -1081,8 +1337,8 @@
bool use_pk;
u16 status;
- resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt,
- &use_pk);
+ resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, NULL,
+ 1, 0, &use_pt, &use_pk);
if (!resp) {
wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
return -1;
@@ -1186,11 +1442,41 @@
}
+static bool is_sae_key_mgmt_suite(struct wpa_supplicant *wpa_s, u32 suite)
+{
+ /* suite is supposed to be the selector value in host byte order with
+ * the OUI in three most significant octets. However, the initial
+ * implementation swapped that byte order and did not work with drivers
+ * that followed the expected byte order. Keep a workaround here to
+ * match that initial implementation so that already deployed use cases
+ * remain functional. */
+ if (RSN_SELECTOR_GET(&suite) == RSN_AUTH_KEY_MGMT_SAE) {
+ /* Old drivers which follow initial implementation send SAE AKM
+ * for both SAE and FT-SAE connections. In that case, determine
+ * the actual AKM from wpa_s->key_mgmt. */
+ wpa_s->sme.ext_auth_key_mgmt = wpa_s->key_mgmt;
+ return true;
+ }
+
+ if (suite == RSN_AUTH_KEY_MGMT_SAE)
+ wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_SAE;
+ else if (suite == RSN_AUTH_KEY_MGMT_FT_SAE)
+ wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+ else if (suite == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
+ wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+ else if (suite == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
+ wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+ else
+ return false;
+
+ return true;
+}
+
+
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)
+ if (!is_sae_key_mgmt_suite(wpa_s, data->external_auth.key_mgmt_suite))
return;
if (data->external_auth.action == EXT_AUTH_START) {
@@ -1264,7 +1550,7 @@
static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
u16 status_code, const u8 *data, size_t len,
- int external, const u8 *sa)
+ int external, const u8 *sa, int *ie_offset)
{
int *groups;
@@ -1328,7 +1614,17 @@
}
token_len = elen - 1;
}
+
+ if (ie_offset)
+ *ie_offset = token_pos + token_len - data;
+
wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len);
+ if (!wpa_s->sme.sae_token) {
+ wpa_dbg(wpa_s, MSG_ERROR,
+ "SME: Failed to allocate SAE token");
+ return -1;
+ }
+
wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token",
wpa_s->sme.sae_token);
if (!external)
@@ -1350,7 +1646,7 @@
int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
wpa_s->sme.sae.group);
wpa_s->sme.sae_group_index++;
- if (sme_set_sae_group(wpa_s) < 0)
+ if (sme_set_sae_group(wpa_s, external) < 0)
return -1; /* no other groups enabled */
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
if (!external)
@@ -1423,7 +1719,8 @@
res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
groups, status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
- status_code == WLAN_STATUS_SAE_PK);
+ status_code == WLAN_STATUS_SAE_PK,
+ ie_offset);
if (res == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message due to reflection attack");
@@ -1458,7 +1755,8 @@
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
if (wpa_s->sme.sae.state != SAE_CONFIRMED)
return -1;
- if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
+ if (sae_check_confirm(&wpa_s->sme.sae, data, len,
+ ie_offset) < 0)
return -1;
wpa_s->sme.sae.state = SAE_ACCEPTED;
sae_clear_temp_data(&wpa_s->sme.sae);
@@ -1530,7 +1828,7 @@
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);
+ len - auth_length, 1, header->sa, NULL);
if (res < 0) {
/* Notify failure to the driver */
sme_send_external_auth_status(
@@ -1554,6 +1852,7 @@
void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
+ int ie_offset = 0;
if (ssid == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
@@ -1567,7 +1866,9 @@
return;
}
- if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
+ if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0 &&
+ !(wpa_s->valid_links &&
+ os_memcmp(wpa_s->ap_mld_addr, data->auth.peer, ETH_ALEN) == 0)) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
"unexpected peer " MACSTR,
MAC2STR(data->auth.peer));
@@ -1585,10 +1886,13 @@
#ifdef CONFIG_SAE
if (data->auth.auth_type == WLAN_AUTH_SAE) {
+ const u8 *addr = wpa_s->pending_bssid;
int res;
+
res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
data->auth.status_code, data->auth.ies,
- data->auth.ies_len, 0, data->auth.peer);
+ data->auth.ies_len, 0, data->auth.peer,
+ &ie_offset);
if (res < 0) {
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
@@ -1597,7 +1901,10 @@
if (res != 1)
return;
- if (sme_sae_set_pmk(wpa_s, wpa_s->pending_bssid) < 0)
+ if (wpa_s->valid_links)
+ addr = wpa_s->ap_mld_addr;
+
+ if (sme_sae_set_pmk(wpa_s, addr) < 0)
return;
}
#endif /* CONFIG_SAE */
@@ -1725,6 +2032,11 @@
}
#endif /* CONFIG_FILS */
+ /* TODO: Support additional auth_type values as well */
+ if (data->auth.auth_type == WLAN_AUTH_OPEN ||
+ data->auth.auth_type == WLAN_AUTH_SAE)
+ wpas_sme_ml_auth(wpa_s, data, ie_offset);
+
sme_associate(wpa_s, ssid->mode, data->auth.peer,
data->auth.auth_type);
}
@@ -2014,6 +2326,7 @@
#ifdef CONFIG_HE_OVERRIDES
wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HE_OVERRIDES */
+ wpa_supplicant_apply_eht_overrides(wpa_s, ssid, ¶ms);
#ifdef CONFIG_IEEE80211R
if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len,
@@ -2142,6 +2455,31 @@
else
params.uapsd = -1;
+ if (wpa_s->valid_links) {
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: In association. assoc_link_id=%u, valid_links=0x%x",
+ wpa_s->mlo_assoc_link_id, wpa_s->valid_links);
+
+ params.mld_params.mld_addr = wpa_s->ap_mld_addr;
+ params.mld_params.valid_links = wpa_s->valid_links;
+ params.mld_params.assoc_link_id = wpa_s->mlo_assoc_link_id;
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(wpa_s->valid_links & BIT(i)))
+ continue;
+
+ params.mld_params.mld_links[i].bssid =
+ wpa_s->links[i].bssid;
+ params.mld_params.mld_links[i].freq =
+ wpa_s->links[i].freq;
+
+ wpa_printf(MSG_DEBUG, "MLD: id=%u, freq=%d, " MACSTR,
+ i, wpa_s->links[i].freq,
+ MAC2STR(wpa_s->links[i].bssid));
+ }
+ }
+
if (wpa_drv_associate(wpa_s, ¶ms) < 0) {
wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
"driver failed");
@@ -2239,6 +2577,34 @@
}
#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 &&
+ data->assoc_reject.status_code == WLAN_STATUS_INVALID_PMKID) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = pmksa_cache_get_current(wpa_s->wpa);
+ if (pmksa) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "DPP: Drop PMKSA cache entry for the BSS due to invalid PMKID report");
+ wpa_sm_pmksa_cache_remove(wpa_s->wpa, pmksa);
+ }
+ wpa_sm_aborted_cached(wpa_s->wpa);
+ if (wpa_s->current_bss) {
+ struct wpa_bss *bss = wpa_s->current_bss;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "DPP: Try network introduction again");
+ wpas_connect_work_done(wpa_s);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_connect(wpa_s, bss, ssid);
+ return;
+ }
+ }
+#endif /* CONFIG_DPP */
+
/*
* For now, unconditionally terminate the previous authentication. In
* theory, this should not be needed, but mac80211 gets quite confused
diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
index da69a87..4eab335 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
@@ -1,5 +1,5 @@
[Unit]
-Description=WPA supplicant daemon (interface- and nl80211 driver-specific version)
+Description=WPA supplicant daemon (for interface %I using nl80211)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
Before=network.target
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
index 55d2b9c..b0d610f 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
@@ -1,5 +1,5 @@
[Unit]
-Description=WPA supplicant daemon (interface-specific version)
+Description=WPA supplicant daemon (for interface %I)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
Before=network.target
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 5393f1c..72a119d 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -524,6 +524,11 @@
rep->mul_bssid->subelem_len = elen - 1;
os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "WNM: Unsupported neighbor report subelement id %u",
+ id);
+ break;
}
}
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 197efe0..bfdc42d 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -503,7 +503,7 @@
"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",
- "sae_groups", "dtim_period", "beacon_int",
+ "sae_check_mfp", "sae_groups", "dtim_period", "beacon_int",
"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
"scan_cur_freq", "scan_res_valid_for_connect",
"sched_scan_interval",
@@ -602,6 +602,7 @@
"go_venue_group", "go_venue_type",
"wps_nfc_dev_pw_id", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+ "sae_check_mfp",
"dtim_period", "beacon_int", "ignore_old_scan_res",
"scan_cur_freq", "scan_res_valid_for_connect",
"sched_scan_interval",
@@ -1479,6 +1480,7 @@
#ifdef CONFIG_HE_OVERRIDES
"disable_he",
#endif /* CONFIG_HE_OVERRIDES */
+ "disable_eht",
"ap_max_inactivity", "dtim_period", "beacon_int",
#ifdef CONFIG_MACSEC
"macsec_policy",
@@ -3260,19 +3262,18 @@
#ifdef CONFIG_PASN
-static int wpa_cli_cmd_pasn_auth_start(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+static int wpa_cli_cmd_pasn_start(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- return wpa_cli_cmd(ctrl, "PASN_AUTH_START", 4, argc, argv);
+ return wpa_cli_cmd(ctrl, "PASN_START", 4, argc, argv);
}
-static int wpa_cli_cmd_pasn_auth_stop(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+static int wpa_cli_cmd_pasn_stop(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- return wpa_cli_cmd(ctrl, "PASN_AUTH_STOP", 0, argc, argv);
+ return wpa_cli_cmd(ctrl, "PASN_STOP", 0, argc, argv);
}
+
static int wpa_cli_cmd_ptksa_cache_list(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -4024,10 +4025,10 @@
{ "all_bss", wpa_cli_cmd_all_bss, NULL, cli_cmd_flag_none,
"= list all BSS entries (scan results)" },
#ifdef CONFIG_PASN
- { "pasn_auth_start", wpa_cli_cmd_pasn_auth_start, NULL,
+ { "pasn_start", wpa_cli_cmd_pasn_start, NULL,
cli_cmd_flag_none,
"bssid=<BSSID> akmp=<WPA key mgmt> cipher=<WPA cipher> group=<group> nid=<network id> = Start PASN authentication" },
- { "pasn_auth_stop", wpa_cli_cmd_pasn_auth_stop, NULL,
+ { "pasn_stop", wpa_cli_cmd_pasn_stop, NULL,
cli_cmd_flag_none,
"= Stop PASN authentication" },
{ "ptksa_cache_list", wpa_cli_cmd_ptksa_cache_list, NULL,
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
index d9c07e6..cfab4f1 100644
--- a/wpa_supplicant/wpa_passphrase.c
+++ b/wpa_supplicant/wpa_passphrase.c
@@ -7,6 +7,7 @@
*/
#include "includes.h"
+#include <termios.h>
#include "common.h"
#include "crypto/sha1.h"
@@ -14,6 +15,7 @@
int main(int argc, char *argv[])
{
+ struct termios term;
unsigned char psk[32];
int i;
char *ssid, *passphrase, buf[64], *pos;
@@ -31,11 +33,28 @@
if (argc > 2) {
passphrase = argv[2];
} else {
+ bool ctrl_echo;
+
fprintf(stderr, "# reading passphrase from stdin\n");
+ if (tcgetattr(STDIN_FILENO, &term) < 0) {
+ perror("tcgetattr");
+ return 1;
+ }
+ ctrl_echo = term.c_lflag & ECHO;
+ term.c_lflag &= ~ECHO;
+ if (ctrl_echo && tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) {
+ perror("tcsetattr:error disabling echo");
+ return 1;
+ }
if (fgets(buf, sizeof(buf), stdin) == NULL) {
fprintf(stderr, "Failed to read passphrase\n");
return 1;
}
+ term.c_lflag |= ECHO;
+ if (ctrl_echo && tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) {
+ perror("tcsetattr:error enabling echo");
+ return 1;
+ }
buf[sizeof(buf) - 1] = '\0';
pos = buf;
while (*pos != '\0') {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index ff1fb67..31a9af6 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -414,6 +414,7 @@
p.key = params->key_len ? params->key : NULL;
p.key_len = params->key_len;
p.key_flag = params->key_flag;
+ p.link_id = -1;
res = iface->driver->set_key(iface->drv_priv, &p);
wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 7d896f4..521ff90 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -143,7 +143,7 @@
continue;
set = 1;
- wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_WEP, NULL,
i, i == ssid->wep_tx_keyidx, NULL, 0,
ssid->wep_key[i], ssid->wep_key_len[i],
i == ssid->wep_tx_keyidx ?
@@ -207,7 +207,7 @@
/* TODO: should actually remember the previously used seq#, both for TX
* and RX from each STA.. */
- ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen,
+ ret = wpa_drv_set_key(wpa_s, -1, alg, NULL, 0, 1, seq, 6, key, keylen,
KEY_FLAG_GROUP_RX_TX_DEFAULT);
os_memset(key, 0, sizeof(key));
return ret;
@@ -404,6 +404,7 @@
#ifdef CONFIG_WEP
int i;
#endif /* CONFIG_WEP */
+ struct wpa_sm_mlo mlo;
if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
@@ -444,6 +445,8 @@
wpa_s->mgmt_group_cipher);
pmksa_cache_clear_current(wpa_s->wpa);
+ os_memset(&mlo, 0, sizeof(mlo));
+ wpa_sm_set_mlo_params(wpa_s->wpa, &mlo);
}
@@ -766,18 +769,18 @@
for (i = 0; i < max; i++) {
if (wpa_s->keys_cleared & BIT(i))
continue;
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
NULL, 0, KEY_FLAG_GROUP);
}
/* Pairwise Key ID 1 for Extended Key ID is tracked in bit 15 */
if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr &&
!is_zero_ether_addr(addr)) {
if (!(wpa_s->keys_cleared & BIT(0)))
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL,
- 0, NULL, 0, KEY_FLAG_PAIRWISE);
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, addr, 0, 0,
+ NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
if (!(wpa_s->keys_cleared & BIT(15)))
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL,
- 0, NULL, 0, KEY_FLAG_PAIRWISE);
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, addr, 1, 0,
+ NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
/* MLME-SETPROTECTION.request(None) */
wpa_drv_mlme_setprotection(
wpa_s, addr,
@@ -1158,14 +1161,14 @@
if (wpa_s->confname == NULL)
return -1;
- conf = wpa_config_read(wpa_s->confname, NULL);
+ conf = wpa_config_read(wpa_s->confname, NULL, false);
if (conf == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
"file '%s' - exiting", wpa_s->confname);
return -1;
}
if (wpa_s->confanother &&
- !wpa_config_read(wpa_s->confanother, conf)) {
+ !wpa_config_read(wpa_s->confanother, conf, true)) {
wpa_msg(wpa_s, MSG_ERROR,
"Failed to parse the configuration file '%s' - exiting",
wpa_s->confanother);
@@ -1350,6 +1353,108 @@
wpas_get_ssid_pmf(wpa_s, ssid));
}
+/**
+ * wpa_supplicant_get_psk - Get PSK from config or external database
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ * @psk: Buffer for the PSK
+ * Returns: 0 on success or -1 if configuration parsing failed
+ *
+ * This function obtains the PSK for a network, either included inline in the
+ * config or retrieved from an external database.
+ */
+static int wpa_supplicant_get_psk(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid,
+ u8 *psk)
+{
+ if (ssid->psk_set) {
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)",
+ ssid->psk, PMK_LEN);
+ os_memcpy(psk, ssid->psk, PMK_LEN);
+ return 0;
+ }
+
+#ifndef CONFIG_NO_PBKDF2
+ if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) {
+ if (pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
+ 4096, psk, PMK_LEN) != 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "Error in pbkdf2_sha1()");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+ psk, PMK_LEN);
+ return 0;
+ }
+#endif /* CONFIG_NO_PBKDF2 */
+
+#ifdef CONFIG_EXT_PASSWORD
+ if (ssid->ext_psk) {
+ struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+ ssid->ext_psk);
+ char pw_str[64 + 1];
+
+ if (!pw) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "EXT PW: No PSK found from external storage");
+ return -1;
+ }
+
+ if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "EXT PW: Unexpected PSK length %d in external storage",
+ (int) wpabuf_len(pw));
+ ext_password_free(pw);
+ return -1;
+ }
+
+ os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
+ pw_str[wpabuf_len(pw)] = '\0';
+
+#ifndef CONFIG_NO_PBKDF2
+ if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
+ {
+ if (pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
+ 4096, psk, PMK_LEN) != 0) {
+ wpa_msg(wpa_s, MSG_WARNING,
+ "Error in pbkdf2_sha1()");
+ forced_memzero(pw_str, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "PSK (from external passphrase)",
+ psk, PMK_LEN);
+ } else
+#endif /* CONFIG_NO_PBKDF2 */
+ if (wpabuf_len(pw) == 2 * PMK_LEN) {
+ if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "EXT PW: Invalid PSK hex string");
+ forced_memzero(pw_str, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from external PSK)",
+ psk, PMK_LEN);
+ } else {
+ wpa_msg(wpa_s, MSG_INFO,
+ "EXT PW: No suitable PSK available");
+ forced_memzero(pw_str, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+
+ forced_memzero(pw_str, sizeof(pw_str));
+ ext_password_free(pw);
+
+ return 0;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
+ return -1;
+}
+
static void wpas_update_allowed_key_mgmt(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
@@ -1434,7 +1539,8 @@
return;
}
- if (wpa_s->conf->sae_pwe)
+ if (wpa_s->conf->sae_pwe != SAE_PWE_HUNT_AND_PECK &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (ssid->sae_pk)
@@ -1464,6 +1570,7 @@
* @wpa_ie: Buffer for the WPA/RSN IE
* @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the
* used buffer length in case the functions returns success.
+ * @skip_default_rsne: Whether to skip setting of the default RSNE/RSNXE
* Returns: 0 on success or -1 on failure
*
* This function is used to configure authentication and encryption parameters
@@ -1472,10 +1579,12 @@
*/
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
- u8 *wpa_ie, size_t *wpa_ie_len)
+ u8 *wpa_ie, size_t *wpa_ie_len,
+ bool skip_default_rsne)
{
struct wpa_ie_data ie;
- int sel, proto, sae_pwe;
+ int sel, proto;
+ enum sae_pwe sae_pwe;
const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
if (bss) {
@@ -1655,7 +1764,8 @@
sel = ie.key_mgmt & ssid->key_mgmt;
#ifdef CONFIG_SAE
- if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) ||
+ wpas_is_sae_avoided(wpa_s, ssid, &ie))
sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY |
WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_FT_SAE_EXT_KEY);
#endif /* CONFIG_SAE */
@@ -1793,7 +1903,8 @@
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
- wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
+ (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED ||
+ (bss && is_6ghz_freq(bss->freq)))) {
wpa_msg(wpa_s, MSG_INFO,
"RSN: Management frame protection required but the selected AP does not enable it");
return -1;
@@ -1808,18 +1919,14 @@
sae_pwe = wpa_s->conf->sae_pwe;
if ((ssid->sae_password_id ||
wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
- sae_pwe != 3)
- sae_pwe = 1;
- if (bss && is_6ghz_freq(bss->freq)) {
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: force hash-to-element mode for 6GHz BSS.");
- sae_pwe = 1;
+ sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ if (bss && is_6ghz_freq(bss->freq) &&
+ sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "RSN: Enable SAE hash-to-element mode for 6 GHz BSS");
+ sae_pwe = SAE_PWE_BOTH;
}
-#ifdef CONFIG_TESTING_OPTIONS
- if (wpa_s->force_hunting_and_pecking_pwe) {
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: force hunting and pecking mode.");
- sae_pwe = 0;
- }
-#endif
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe);
#ifdef CONFIG_SAE_PK
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PK,
@@ -1830,6 +1937,12 @@
(!ssid->sae_password && ssid->passphrase &&
sae_pk_valid_password(ssid->passphrase))));
#endif /* CONFIG_SAE_PK */
+ if (bss && is_6ghz_freq(bss->freq) &&
+ wpas_get_ssid_pmf(wpa_s, ssid) != MGMT_FRAME_PROTECTION_REQUIRED) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Force MFPR=1 on 6 GHz");
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
+ MGMT_FRAME_PROTECTION_REQUIRED);
+ }
#ifdef CONFIG_TESTING_OPTIONS
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_RSNXE_USED,
wpa_s->ft_rsnxe_used);
@@ -1869,16 +1982,21 @@
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, 0);
}
- if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
- wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
- return -1;
- }
+ if (!skip_default_rsne) {
+ if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie,
+ wpa_ie_len)) {
+ wpa_msg(wpa_s, MSG_WARNING,
+ "RSN: Failed to generate RSNE/WPA IE");
+ return -1;
+ }
- wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
- if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
- &wpa_s->rsnxe_len)) {
- wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE");
- return -1;
+ wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
+ if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
+ &wpa_s->rsnxe_len)) {
+ wpa_msg(wpa_s, MSG_WARNING,
+ "RSN: Failed to generate RSNXE");
+ return -1;
+ }
}
if (0) {
@@ -1892,120 +2010,27 @@
#endif /* CONFIG_DPP */
} else 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 (wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) {
+ u8 psk[PMK_LEN];
- 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_supplicant_get_psk(wpa_s, bss, ssid,
+ psk) == 0) {
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+ NULL);
+ psk_set = 1;
+ }
+ forced_memzero(psk, sizeof(psk));
}
if (wpa_key_mgmt_sae(ssid->key_mgmt) &&
- (ssid->sae_password || ssid->passphrase))
+ (ssid->sae_password || ssid->passphrase || ssid->ext_psk))
psk_set = 1;
-#ifndef CONFIG_NO_PBKDF2
- if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
- ssid->passphrase && !sae_only) {
- u8 psk[PMK_LEN];
-
- if (pbkdf2_sha1(ssid->passphrase, bss->ssid,
- bss->ssid_len,
- 4096, psk, PMK_LEN) != 0) {
- wpa_msg(wpa_s, MSG_WARNING,
- "Error in pbkdf2_sha1()");
- return -1;
- }
- wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
- psk, PMK_LEN);
- wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
- psk_set = 1;
- os_memset(psk, 0, sizeof(psk));
- }
-#endif /* CONFIG_NO_PBKDF2 */
-#ifdef CONFIG_EXT_PASSWORD
- if (ssid->ext_psk && !sae_only) {
- struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
- ssid->ext_psk);
- char pw_str[64 + 1];
- u8 psk[PMK_LEN];
-
- if (pw == NULL) {
- wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
- "found from external storage");
- return -1;
- }
-
- if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
- wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
- "PSK length %d in external storage",
- (int) wpabuf_len(pw));
- ext_password_free(pw);
- return -1;
- }
-
- os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
- pw_str[wpabuf_len(pw)] = '\0';
-
-#ifndef CONFIG_NO_PBKDF2
- if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
- {
- if (pbkdf2_sha1(pw_str, bss->ssid,
- bss->ssid_len,
- 4096, psk, PMK_LEN) != 0) {
- wpa_msg(wpa_s, MSG_WARNING,
- "Error in pbkdf2_sha1()");
- ext_password_free(pw);
- return -1;
- }
- os_memset(pw_str, 0, sizeof(pw_str));
- wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
- "external passphrase)",
- psk, PMK_LEN);
- wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
- NULL);
- psk_set = 1;
- os_memset(psk, 0, sizeof(psk));
- } else
-#endif /* CONFIG_NO_PBKDF2 */
- if (wpabuf_len(pw) == 2 * PMK_LEN) {
- if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
- wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
- "Invalid PSK hex string");
- os_memset(pw_str, 0, sizeof(pw_str));
- 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;
- os_memset(psk, 0, sizeof(psk));
- } else {
- wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
- "PSK available");
- os_memset(pw_str, 0, sizeof(pw_str));
- ext_password_free(pw);
- return -1;
- }
-
- os_memset(pw_str, 0, sizeof(pw_str));
- ext_password_free(pw);
- }
-#endif /* CONFIG_EXT_PASSWORD */
-
if (!psk_set) {
wpa_msg(wpa_s, MSG_INFO,
"No PSK available for association");
- wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE");
+ wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL);
return -1;
}
#ifdef CONFIG_OWE
@@ -2228,31 +2253,53 @@
}
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s,
+ enum wpas_mac_addr_style style,
+ struct wpa_ssid *ssid)
{
struct os_reltime now;
u8 addr[ETH_ALEN];
os_get_reltime(&now);
- if (wpa_s->last_mac_addr_style == style &&
- wpa_s->last_mac_addr_change.sec != 0 &&
- !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
- wpa_s->conf->rand_addr_lifetime)) {
- wpa_msg(wpa_s, MSG_DEBUG,
- "Previously selected random MAC address has not yet expired");
- return 0;
+ /* Random addresses are valid within a given ESS so check
+ * expiration/value only when continuing to use the same ESS. */
+ if (wpa_s->last_mac_addr_style == style && wpa_s->reassoc_same_ess) {
+ if (style == WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS) {
+ /* Pregenerated addresses do not expire but their value
+ * might have changed, so let's check that. */
+ if (os_memcmp(wpa_s->own_addr, ssid->mac_value,
+ ETH_ALEN) == 0)
+ return 0;
+ } else if ((wpa_s->last_mac_addr_change.sec != 0 ||
+ wpa_s->last_mac_addr_change.usec != 0) &&
+ !os_reltime_expired(
+ &now,
+ &wpa_s->last_mac_addr_change,
+ wpa_s->conf->rand_addr_lifetime)) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Previously selected random MAC address has not yet expired");
+ return 0;
+ }
}
switch (style) {
- case 1:
+ case WPAS_MAC_ADDR_STYLE_RANDOM:
if (random_mac_addr(addr) < 0)
return -1;
break;
- case 2:
+ case WPAS_MAC_ADDR_STYLE_RANDOM_SAME_OUI:
os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN);
if (random_mac_addr_keep_oui(addr) < 0)
return -1;
break;
+ case WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS:
+ if (!ssid) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Invalid 'ssid' for address policy 3");
+ return -1;
+ }
+ os_memcpy(addr, ssid->mac_value, ETH_ALEN);
+ break;
default:
return -1;
}
@@ -2276,7 +2323,7 @@
wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
MAC2STR(addr));
- return 0;
+ return 1;
}
@@ -2286,7 +2333,8 @@
!wpa_s->conf->preassoc_mac_addr)
return 0;
- return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
+ return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr,
+ NULL);
}
@@ -2305,10 +2353,10 @@
password = ssid->passphrase;
if (!password ||
- (conf->sae_pwe == 0 && !ssid->sae_password_id &&
+ (conf->sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id &&
!wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) &&
!sae_pk_valid_password(password)) ||
- conf->sae_pwe == 3) {
+ conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) {
/* PT derivation not needed */
sae_deinit_pt(ssid->pt);
ssid->pt = NULL;
@@ -2378,7 +2426,7 @@
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
struct wpa_connect_work *cwork;
- int rand_style;
+ enum wpas_mac_addr_style rand_style;
wpa_s->own_disconnect_req = 0;
wpa_s->own_reconnect_req = 0;
@@ -2390,7 +2438,7 @@
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = NULL;
- if (ssid->mac_addr == -1)
+ if (ssid->mac_addr == WPAS_MAC_ADDR_STYLE_NOT_SET)
rand_style = wpa_s->conf->mac_addr;
else
rand_style = ssid->mac_addr;
@@ -2422,11 +2470,16 @@
wpa_s_setup_sae_pt(wpa_s->conf, ssid);
#endif /* CONFIG_SAE */
- if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
- if (wpas_update_random_addr(wpa_s, rand_style) < 0)
+ if (rand_style > WPAS_MAC_ADDR_STYLE_PERMANENT) {
+ int status = wpas_update_random_addr(wpa_s, rand_style, ssid);
+
+ if (status < 0)
return;
- wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
- } else if (rand_style == 0 && wpa_s->mac_addr_changed) {
+ if (rand_style != WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS &&
+ status > 0) /* MAC changed */
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+ } else if (rand_style == WPAS_MAC_ADDR_STYLE_PERMANENT &&
+ wpa_s->mac_addr_changed) {
if (wpas_restore_permanent_mac_addr(wpa_s) < 0)
return;
}
@@ -3123,6 +3176,10 @@
wpa_key_mgmt_wpa(ssid->key_mgmt)) {
int try_opportunistic;
const u8 *cache_id = NULL;
+ const u8 *addr = bss->bssid;
+
+ if (wpa_s->valid_links)
+ addr = wpa_s->ap_mld_addr;
try_opportunistic = (ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc :
@@ -3132,7 +3189,7 @@
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,
+ if (pmksa_cache_set_current(wpa_s->wpa, NULL, addr,
ssid, try_opportunistic,
cache_id, 0) == 0) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
@@ -3142,7 +3199,7 @@
}
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
- wpa_ie, &wpa_ie_len)) {
+ wpa_ie, &wpa_ie_len, false)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites");
os_free(wpa_ie);
@@ -3154,7 +3211,7 @@
/* 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_ie, &wpa_ie_len, false)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites");
os_free(wpa_ie);
@@ -3174,7 +3231,7 @@
} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
- wpa_ie, &wpa_ie_len)) {
+ wpa_ie, &wpa_ie_len, false)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites (no "
"scan results)");
@@ -3499,7 +3556,7 @@
}
#ifdef CONFIG_SME
if (len > 0 && wpa_s->sme.ft_used &&
- wpa_sm_has_ptk(wpa_s->wpa)) {
+ wpa_sm_has_ft_keys(wpa_s->wpa, md)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Trying to use FT over-the-air");
algs |= WPA_AUTH_ALG_FT;
@@ -3783,6 +3840,7 @@
int use_crypt, ret, bssid_changed;
unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
struct wpa_driver_associate_params params;
+ u8 psk[PMK_LEN];
#if defined(CONFIG_WEP) || defined(IEEE8021X_EAPOL)
int wep_keys_set = 0;
#endif /* CONFIG_WEP || IEEE8021X_EAPOL */
@@ -4080,8 +4138,8 @@
(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK)))) {
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
params.passphrase = ssid->passphrase;
- if (ssid->psk_set)
- params.psk = ssid->psk;
+ if (wpa_supplicant_get_psk(wpa_s, bss, ssid, psk) == 0)
+ params.psk = psk;
}
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
@@ -4110,8 +4168,8 @@
if ((wpa_key_mgmt_wpa_psk_no_sae(params.key_mgmt_suite) ||
wpa_key_mgmt_wpa_psk_no_sae(params.allowed_key_mgmts)) &&
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
- ssid->psk_set)
- params.psk = ssid->psk;
+ wpa_supplicant_get_psk(wpa_s, bss, ssid, psk) == 0)
+ params.psk = psk;
}
params.drop_unencrypted = use_crypt;
@@ -4159,6 +4217,7 @@
#ifdef CONFIG_HE_OVERRIDES
wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HE_OVERRIDES */
+ wpa_supplicant_apply_eht_overrides(wpa_s, ssid, ¶ms);
#ifdef CONFIG_P2P
/*
@@ -4168,7 +4227,7 @@
*/
if (wpa_s->num_multichan_concurrent < 2) {
int freq, num;
- num = get_shared_radio_freqs(wpa_s, &freq, 1);
+ num = get_shared_radio_freqs(wpa_s, &freq, 1, false);
if (num > 0 && freq > 0 && freq != params.freq.freq) {
wpa_printf(MSG_DEBUG,
"Assoc conflicting freq found (%d != %d)",
@@ -4192,6 +4251,7 @@
#endif /* CONFIG_SAE */
ret = wpa_drv_associate(wpa_s, ¶ms);
+ forced_memzero(psk, sizeof(psk));
os_free(wpa_ie);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -4273,6 +4333,8 @@
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+ wpas_notify_auth_changed(wpa_s);
}
@@ -5184,6 +5246,8 @@
enum frame_encryption encrypted)
{
struct wpa_supplicant *wpa_s = ctx;
+ const u8 *connected_addr = wpa_s->valid_links ?
+ wpa_s->ap_mld_addr : wpa_s->bssid;
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " (encrypted=%d)",
MAC2STR(src_addr), encrypted);
@@ -5209,7 +5273,7 @@
#ifdef CONFIG_AP
!wpa_s->ap_iface &&
#endif /* CONFIG_AP */
- os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) {
+ os_memcmp(src_addr, connected_addr, ETH_ALEN) != 0)) {
/*
* There is possible race condition between receiving the
* association event and the EAPOL frame since they are coming
@@ -5222,10 +5286,11 @@
* Authenticator uses BSSID as the src_addr (which is not the
* case with wired IEEE 802.1X).
*/
- wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
- "of received EAPOL frame (state=%s bssid=" MACSTR ")",
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Not associated - Delay processing of received EAPOL frame (state=%s connected_addr="
+ MACSTR ")",
wpa_supplicant_state_txt(wpa_s->wpa_state),
- MAC2STR(wpa_s->bssid));
+ MAC2STR(connected_addr));
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
if (wpa_s->pending_eapol_rx) {
@@ -5238,7 +5303,7 @@
}
wpa_s->last_eapol_matches_bssid =
- os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0;
+ os_memcmp(src_addr, connected_addr, ETH_ALEN) == 0;
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
@@ -5354,6 +5419,10 @@
int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
{
+ u8 prev_mac_addr[ETH_ALEN];
+
+ os_memcpy(prev_mac_addr, wpa_s->own_addr, ETH_ALEN);
+
if ((!wpa_s->p2p_mgmt ||
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
@@ -5391,6 +5460,9 @@
fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr);
#endif /* CONFIG_FST */
+ if (os_memcmp(prev_mac_addr, wpa_s->own_addr, ETH_ALEN) != 0)
+ wpas_notify_mac_address_changed(wpa_s);
+
return 0;
}
@@ -5957,6 +6029,17 @@
#endif /* CONFIG_HE_OVERRIDES */
+void wpa_supplicant_apply_eht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params)
+{
+ if (!ssid)
+ return;
+
+ params->disable_eht = ssid->disable_eht;
+}
+
+
static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
{
#ifdef PCSC_FUNCS
@@ -6774,7 +6857,7 @@
#else /* CONFIG_BACKEND_FILE */
wpa_s->confname = os_strdup(iface->confname);
#endif /* CONFIG_BACKEND_FILE */
- wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
+ wpa_s->conf = wpa_config_read(wpa_s->confname, NULL, false);
if (wpa_s->conf == NULL) {
wpa_printf(MSG_ERROR, "Failed to read or parse "
"configuration '%s'.", wpa_s->confname);
@@ -6782,7 +6865,7 @@
}
wpa_s->confanother = os_rel2abs_path(iface->confanother);
if (wpa_s->confanother &&
- !wpa_config_read(wpa_s->confanother, wpa_s->conf)) {
+ !wpa_config_read(wpa_s->confanother, wpa_s->conf, true)) {
wpa_printf(MSG_ERROR,
"Failed to read or parse configuration '%s'.",
wpa_s->confanother);
@@ -6797,12 +6880,24 @@
os_free(wpa_s->conf->ctrl_interface);
wpa_s->conf->ctrl_interface =
os_strdup(iface->ctrl_interface);
+ if (!wpa_s->conf->ctrl_interface) {
+ wpa_printf(MSG_ERROR,
+ "Failed to duplicate control interface '%s'.",
+ iface->ctrl_interface);
+ return -1;
+ }
}
if (iface->driver_param) {
os_free(wpa_s->conf->driver_param);
wpa_s->conf->driver_param =
os_strdup(iface->driver_param);
+ if (!wpa_s->conf->driver_param) {
+ wpa_printf(MSG_ERROR,
+ "Failed to duplicate driver param '%s'.",
+ iface->driver_param);
+ return -1;
+ }
}
if (iface->p2p_mgmt && !iface->ctrl_interface) {
@@ -7571,26 +7666,63 @@
global->params.daemonize = params->daemonize;
global->params.wait_for_monitor = params->wait_for_monitor;
global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
- if (params->pid_file)
+
+ if (params->pid_file) {
global->params.pid_file = os_strdup(params->pid_file);
- if (params->ctrl_interface)
+ if (!global->params.pid_file) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
+ if (params->ctrl_interface) {
global->params.ctrl_interface =
os_strdup(params->ctrl_interface);
- if (params->ctrl_interface_group)
+ if (!global->params.ctrl_interface) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
+ if (params->ctrl_interface_group) {
global->params.ctrl_interface_group =
os_strdup(params->ctrl_interface_group);
- if (params->override_driver)
+ if (!global->params.ctrl_interface_group) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
+ if (params->override_driver) {
global->params.override_driver =
os_strdup(params->override_driver);
- if (params->override_ctrl_interface)
+ if (!global->params.override_driver) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
+ if (params->override_ctrl_interface) {
global->params.override_ctrl_interface =
os_strdup(params->override_ctrl_interface);
+ if (!global->params.override_ctrl_interface) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
#ifdef CONFIG_MATCH_IFACE
global->params.match_iface_count = params->match_iface_count;
if (params->match_iface_count) {
global->params.match_ifaces =
os_calloc(params->match_iface_count,
sizeof(struct wpa_interface));
+ if (!global->params.match_ifaces) {
+ wpa_printf(MSG_ERROR,
+ "Failed to allocate match interfaces");
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
os_memcpy(global->params.match_ifaces,
params->match_ifaces,
params->match_iface_count *
@@ -7598,9 +7730,15 @@
}
#endif /* CONFIG_MATCH_IFACE */
#ifdef CONFIG_P2P
- if (params->conf_p2p_dev)
+ if (params->conf_p2p_dev) {
global->params.conf_p2p_dev =
os_strdup(params->conf_p2p_dev);
+ if (!global->params.conf_p2p_dev) {
+ wpa_printf(MSG_ERROR, "Failed to allocate conf p2p");
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
#endif /* CONFIG_P2P */
wpa_debug_level = global->params.wpa_debug_level =
params->wpa_debug_level;
@@ -7935,7 +8073,7 @@
if (wpa_s->consecutive_conn_failures > 3 && wpa_s->current_ssid) {
wpa_printf(MSG_DEBUG, "Continuous association failures - "
"consider temporary network disabling");
- wpas_auth_failed(wpa_s, "CONN_FAILED");
+ wpas_auth_failed(wpa_s, "CONN_FAILED", bssid);
}
/*
* Multiple consecutive connection failures mean that other APs are
@@ -8112,6 +8250,8 @@
case WPA_CTRL_REQ_EAP_PASSWORD:
bin_clear_free(eap->password, eap->password_len);
eap->password = (u8 *) os_strdup(value);
+ if (!eap->password)
+ return -1;
eap->password_len = value_len;
eap->pending_req_password = 0;
if (ssid == wpa_s->current_ssid)
@@ -8120,6 +8260,8 @@
case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
bin_clear_free(eap->new_password, eap->new_password_len);
eap->new_password = (u8 *) os_strdup(value);
+ if (!eap->new_password)
+ return -1;
eap->new_password_len = value_len;
eap->pending_req_new_password = 0;
if (ssid == wpa_s->current_ssid)
@@ -8128,6 +8270,8 @@
case WPA_CTRL_REQ_EAP_PIN:
str_clear_free(eap->cert.pin);
eap->cert.pin = os_strdup(value);
+ if (!eap->cert.pin)
+ return -1;
eap->pending_req_pin = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
@@ -8135,6 +8279,8 @@
case WPA_CTRL_REQ_EAP_OTP:
bin_clear_free(eap->otp, eap->otp_len);
eap->otp = (u8 *) os_strdup(value);
+ if (!eap->otp)
+ return -1;
eap->otp_len = value_len;
os_free(eap->pending_req_otp);
eap->pending_req_otp = NULL;
@@ -8143,6 +8289,8 @@
case WPA_CTRL_REQ_EAP_PASSPHRASE:
str_clear_free(eap->cert.private_key_passwd);
eap->cert.private_key_passwd = os_strdup(value);
+ if (!eap->cert.private_key_passwd)
+ return -1;
eap->pending_req_passphrase = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
@@ -8150,6 +8298,8 @@
case WPA_CTRL_REQ_SIM:
str_clear_free(eap->external_sim_resp);
eap->external_sim_resp = os_strdup(value);
+ if (!eap->external_sim_resp)
+ return -1;
eap->pending_req_sim = 0;
break;
case WPA_CTRL_REQ_PSK_PASSPHRASE:
@@ -8277,6 +8427,13 @@
return NO_MGMT_FRAME_PROTECTION;
}
+#ifdef CONFIG_OCV
+ /* Enable PMF if OCV is being enabled */
+ if (wpa_s->conf->pmf == NO_MGMT_FRAME_PROTECTION &&
+ ssid && ssid->ocv)
+ return MGMT_FRAME_PROTECTION_OPTIONAL;
+#endif /* CONFIG_OCV */
+
return wpa_s->conf->pmf;
}
@@ -8284,6 +8441,19 @@
}
+#ifdef CONFIG_SAE
+bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const struct wpa_ie_data *ie)
+{
+ return wpa_s->conf->sae_check_mfp &&
+ (!(ie->capabilities &
+ (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) ||
+ wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION);
+}
+#endif /* CONFIG_SAE */
+
+
int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
{
if (wpa_s->current_ssid == NULL ||
@@ -8304,7 +8474,8 @@
}
-void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason,
+ const u8 *bssid)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
int dur;
@@ -8374,6 +8545,9 @@
ssid->auth_failures, dur, reason);
wpas_notify_ssid_temp_disabled(wpa_s, msg);
os_free(msg);
+
+ if (bssid)
+ os_memcpy(ssid->disabled_due_to, bssid, ETH_ALEN);
}
@@ -8390,8 +8564,15 @@
}
ssid->disabled_until.sec = 0;
ssid->disabled_until.usec = 0;
- if (clear_failures)
+ if (clear_failures) {
ssid->auth_failures = 0;
+ } else if (!is_zero_ether_addr(ssid->disabled_due_to)) {
+ wpa_printf(MSG_DEBUG, "Mark BSSID " MACSTR
+ " ignored to allow a lower priority BSS, if any, to be tried next",
+ MAC2STR(ssid->disabled_due_to));
+ wpa_bssid_ignore_add(wpa_s, ssid->disabled_due_to);
+ os_memset(ssid->disabled_due_to, 0, ETH_ALEN);
+ }
}
@@ -8505,7 +8686,7 @@
*/
int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs_data,
- unsigned int len)
+ unsigned int len, bool exclude_current)
{
struct wpa_supplicant *ifs;
u8 bssid[ETH_ALEN];
@@ -8521,6 +8702,9 @@
if (idx == len)
break;
+ if (exclude_current && ifs == wpa_s)
+ continue;
+
if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
continue;
@@ -8558,7 +8742,8 @@
* are using the same radio as the current interface.
*/
int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
- int *freq_array, unsigned int len)
+ int *freq_array, unsigned int len,
+ bool exclude_current)
{
struct wpa_used_freq_data *freqs_data;
int num, i;
@@ -8569,7 +8754,8 @@
if (!freqs_data)
return -1;
- num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+ num = get_shared_radio_freqs_data(wpa_s, freqs_data, len,
+ exclude_current);
for (i = 0; i < num; i++)
freq_array[i] = freqs_data[i].freq;
@@ -8894,17 +9080,17 @@
continue;
wpa_printf(MSG_DEBUG,
"Override driver signal_poll information: current_signal: %d->%d avg_signal: %d->%d avg_beacon_signal: %d->%d current_noise: %d->%d",
- si->current_signal,
+ si->data.signal,
dso->si_current_signal,
- si->avg_signal,
+ si->data.avg_signal,
dso->si_avg_signal,
- si->avg_beacon_signal,
+ si->data.avg_beacon_signal,
dso->si_avg_beacon_signal,
si->current_noise,
dso->si_current_noise);
- si->current_signal = dso->si_current_signal;
- si->avg_signal = dso->si_avg_signal;
- si->avg_beacon_signal = dso->si_avg_beacon_signal;
+ si->data.signal = dso->si_current_signal;
+ si->data.avg_signal = dso->si_avg_signal;
+ si->data.avg_beacon_signal = dso->si_avg_beacon_signal;
si->current_noise = dso->si_current_noise;
break;
}
@@ -8955,3 +9141,43 @@
return scan_res;
}
+
+
+static bool wpas_ap_link_address(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ int i;
+
+ if (!wpa_s->valid_links)
+ return false;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(wpa_s->valid_links & BIT(i)))
+ continue;
+
+ if (os_memcmp(wpa_s->links[i].bssid, addr, ETH_ALEN) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+
+int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *src,
+ const u8 *bssid, const u8 *data, size_t data_len,
+ int no_cck)
+{
+ if (!wpa_s->driver->send_action)
+ return -1;
+
+ if (data_len > 0 && data[0] != WLAN_ACTION_PUBLIC) {
+ if (wpas_ap_link_address(wpa_s, dst))
+ dst = wpa_s->ap_mld_addr;
+
+ if (wpas_ap_link_address(wpa_s, bssid))
+ bssid = wpa_s->ap_mld_addr;
+ }
+
+ return wpa_s->driver->send_action(wpa_s->drv_priv, freq, wait, dst, src,
+ bssid, data, data_len, no_cck);
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index b6a1a74..7a5f9cb 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -419,6 +419,34 @@
# RSN.
#pmf=0
+# sae_check_mfp: Require PMF support to select SAE key_mgmt
+# 0 = Do not check PMF for SAE (default)
+# 1 = Limit SAE when PMF is not enabled
+#
+# When enabled SAE will not be selected if PMF will not be used
+# for the connection.
+# Scenarios where this check will limit SAE:
+# 1) ieee80211w=0 is set for the network
+# 2) The AP does not have PMF enabled.
+# 3) ieee80211w is unset, pmf=1 is enabled globally, and
+# the device does not support the BIP cipher.
+# Consider the configuration of global parameterss sae_check_mfp=1, pmf=1 and a
+# network configured with ieee80211w unset and key_mgmt=SAE WPA-PSK.
+# In the example WPA-PSK will be used if the device does not support
+# the BIP cipher or the AP has PMF disabled.
+# Limiting SAE with this check can avoid failing to associate to an AP
+# that is configured with sae_requires_mfp=1 if the device does
+# not support PMF due to lack of the BIP cipher.
+#
+# Enabling this check helps with compliance of the WPA3
+# specification for WPA3-Personal transition mode.
+# The WPA3 specification section 2.3 "WPA3-Personal transition mode" item 8
+# states "A STA shall negotiate PMF when associating to an AP using SAE".
+# With this check WPA3 capable devices when connecting
+# to transition mode APs that do not advertise PMF support
+# will not use SAE and instead fallback to PSK.
+#sae_check_mfp=0
+
# Enabled SAE finite cyclic groups in preference order
# By default (if this parameter is not set), the mandatory group 19 (ECC group
# defined over a 256-bit prime order field, NIST P-256) is preferred and groups
@@ -655,7 +683,26 @@
# be used to configure alternative FQDNs that will be considered home
# networks.
#
+# home_ois: Home OI(s)
+# This string field contains one or more comma delimited OIs (hexdump)
+# identifying the access the access points that support authentication
+# with this credential. There are an alternative to the use of the realm
+# parameter. When using Home OIs to match the network, the EAP parameters
+# need to be pre-configured with the credentials since the NAI Realm
+# information may not be available or fetched.
+# A successful authentication with the access point is possible as soon
+# as at least one Home OI from the list matches an OI in the Roaming
+# Consortium advertised by the access point.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
+#
+# required_home_ois: Required Home OI(s)
+# This string field contains the set of Home OI(s) (hexdump) that are
+# required to be advertised by the AP for the credential to be considered
+# matching.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
+#
# roaming_consortium: Roaming Consortium OI
+# Deprecated: use home_ois instead.
# If roaming_consortium_len is non-zero, this field contains the
# Roaming Consortium OI that can be used to determine which access
# points support authentication with this credential. This is an
@@ -665,6 +712,7 @@
# may not be available or fetched.
#
# required_roaming_consortium: Required Roaming Consortium OI
+# Deprecated: use required_home_ois instead.
# 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.
@@ -770,7 +818,7 @@
# password="password"
# ca_cert="/etc/wpa_supplicant/ca.pem"
# domain="example.com"
-# roaming_consortium=223344
+# home_ois="223344"
# eap=TTLS
# phase2="auth=MSCHAPV2"
#}
@@ -1635,6 +1683,10 @@
# 2: MCS 0-9
# 3: not supported
+# disable_eht: Whether EHT should be disabled.
+# 0 = EHT enabled (if supported) (default)
+# 1 = EHT disabled
+
# multi_ap_backhaul_sta: Multi-AP backhaul STA functionality
# 0 = normal STA (default)
# 1 = backhaul STA
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index e673a9c..d364212 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -22,6 +22,7 @@
#include "wmm_ac.h"
#include <netinet/in.h>
#include <netinet/in6.h>
+#include "pasn/pasn_common.h"
extern const char *const wpa_supplicant_version;
extern const char *const wpa_supplicant_license;
@@ -541,61 +542,6 @@
int num_policies;
};
-#ifdef CONFIG_PASN
-
-struct pasn_fils {
- u8 nonce[FILS_NONCE_LEN];
- u8 anonce[FILS_NONCE_LEN];
- u8 session[FILS_SESSION_LEN];
- u8 erp_pmkid[PMKID_LEN];
- bool completed;
-};
-
-struct wpas_pasn {
- int akmp;
- int cipher;
- u16 group;
- bool secure_ltf;
- int freq;
- size_t kdk_len;
-
- u8 trans_seq;
- u8 status;
-
- u8 own_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
- size_t pmk_len;
- u8 pmk[PMK_LEN_MAX];
- bool using_pmksa;
-
- u8 hash[SHA384_MAC_LEN];
-
- struct wpabuf *beacon_rsne_rsnxe;
- struct wpa_ptk ptk;
- struct crypto_ecdh *ecdh;
-
- struct wpabuf *comeback;
- u16 comeback_after;
-
-#ifdef CONFIG_SAE
- struct sae_data sae;
-#endif /* CONFIG_SAE */
-
- struct wpa_ssid *ssid;
-
-#ifdef CONFIG_FILS
- struct pasn_fils fils;
-#endif /* CONFIG_FILS */
-
-#ifdef CONFIG_IEEE80211R
- u8 pmk_r1[PMK_LEN_MAX];
- size_t pmk_r1_len;
- u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-#endif /* CONFIG_IEEE80211R */
-};
-#endif /* CONFIG_PASN */
-
-
enum ip_version {
IPV4 = 4,
IPV6 = 6,
@@ -757,8 +703,9 @@
int ap_ies_from_associnfo;
unsigned int assoc_freq;
u8 ap_mld_addr[ETH_ALEN];
- u8 valid_links; /* bitmap of valid MLO link IDs */
- struct {
+ u8 mlo_assoc_link_id;
+ u16 valid_links; /* bitmap of valid MLO link IDs */
+ struct ml_sta_link_info {
u8 addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
unsigned int freq;
@@ -1002,7 +949,7 @@
unsigned int connection_11b_only:1;
struct os_reltime last_mac_addr_change;
- int last_mac_addr_style;
+ enum wpas_mac_addr_style last_mac_addr_style;
struct ibss_rsn *ibss_rsn;
@@ -1054,6 +1001,7 @@
struct wpa_ssid *ext_auth_wpa_ssid;
u8 ext_auth_ssid[SSID_MAX_LEN];
size_t ext_auth_ssid_len;
+ int ext_auth_key_mgmt;
int *sae_rejected_groups;
#endif /* CONFIG_SAE */
} sme;
@@ -1389,7 +1337,6 @@
unsigned int oci_freq_override_ft_assoc;
unsigned int oci_freq_override_fils_assoc;
unsigned int oci_freq_override_wnm_sleep;
- int force_hunting_and_pecking_pwe;
unsigned int disable_eapol_g2_tx;
#endif /* CONFIG_TESTING_OPTIONS */
@@ -1580,8 +1527,12 @@
struct robust_av_data robust_av;
bool mscs_setup_done;
+ bool wps_scan_done; /* Set upon receiving scan results event */
+ bool supp_pbc_active; /* Set for interface when PBC is triggered */
+ bool wps_overlap;
+
#ifdef CONFIG_PASN
- struct wpas_pasn pasn;
+ struct pasn_data pasn;
struct wpa_radio_work *pasn_auth_work;
unsigned int pasn_count;
struct pasn_auth *pasn_params;
@@ -1599,6 +1550,8 @@
unsigned int enable_dscp_policy_capa:1;
unsigned int connection_dscp:1;
unsigned int wait_for_dscp_req:1;
+
+ struct wpa_signal_info last_signal_info;
};
@@ -1612,6 +1565,9 @@
void wpa_supplicant_apply_he_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params);
+void wpa_supplicant_apply_eht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params);
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
@@ -1628,7 +1584,8 @@
struct wpa_ssid *ssid, struct wpa_ie_data *ie);
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
- u8 *wpa_ie, size_t *wpa_ie_len);
+ u8 *wpa_ie, size_t *wpa_ie_len,
+ bool skip_default_rsne);
int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s);
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss,
@@ -1706,7 +1663,8 @@
void fils_pmksa_cache_flush(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);
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason,
+ const u8 *bssid);
void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int clear_failures);
int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
@@ -1715,7 +1673,8 @@
void wpas_request_connection(struct wpa_supplicant *wpa_s);
void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style,
+ struct wpa_ssid *ssid);
int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
void add_freq(int *freqs, int *num_freqs, int freq);
@@ -1840,6 +1799,7 @@
void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
+int wpa_wps_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);
@@ -1848,6 +1808,7 @@
int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
struct wpa_bss *current_bss,
struct wpa_bss *seleceted);
+void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s);
/* eap_register.c */
int eap_register_methods(void);
@@ -1885,6 +1846,10 @@
int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid);
+bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const struct wpa_ie_data *ie);
+
int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
@@ -1893,9 +1858,10 @@
int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs_data,
- unsigned int len);
+ unsigned int len, bool exclude_current);
int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
- int *freq_array, unsigned int len);
+ int *freq_array, unsigned int len,
+ bool exclude_current);
void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 556f9cf..7d5ea4c 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -250,7 +250,7 @@
else
wpa_s->group_cipher = cipher;
}
- return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+ return wpa_drv_set_key(wpa_s, -1, WPA_ALG_WEP,
unicast ? wpa_s->bssid : NULL,
keyidx, unicast, NULL, 0, key, keylen,
unicast ? KEY_FLAG_PAIRWISE_RX_TX :
@@ -375,7 +375,7 @@
wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
"handshake", pmk, pmk_len);
- if (wpa_drv_set_key(wpa_s, 0, NULL, 0, 0, NULL, 0, pmk,
+ if (wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0, NULL, 0, pmk,
pmk_len, KEY_FLAG_PMK)) {
wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
}
@@ -537,7 +537,7 @@
}
-static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
+static int wpa_supplicant_set_key(void *_wpa_s, int link_id, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
@@ -566,8 +566,8 @@
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, key_flag);
+ return wpa_drv_set_key(wpa_s, link_id, alg, addr, key_idx, set_tx, seq,
+ seq_len, key, key_len, key_flag);
}
@@ -952,28 +952,9 @@
void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const char *field_name, const char *txt)
{
- char *buf;
- size_t buflen;
- int len;
-
- buflen = 100 + os_strlen(txt) + ssid->ssid_len;
- buf = os_malloc(buflen);
- if (buf == NULL)
- return;
- len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ",
- field_name, ssid->id, txt);
- if (os_snprintf_error(buflen, len)) {
- os_free(buf);
- return;
- }
- if (ssid->ssid && buflen > len + ssid->ssid_len) {
- os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
- len += ssid->ssid_len;
- buf[len] = '\0';
- }
- buf[buflen - 1] = '\0';
- wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf);
- os_free(buf);
+ wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s-%d:%s needed for SSID %s",
+ field_name, ssid->id, txt,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
}
@@ -1294,7 +1275,7 @@
if (wpa_s->conf->key_mgmt_offload &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
- return wpa_drv_set_key(wpa_s, 0, NULL, 0, 0,
+ return wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0,
NULL, 0, pmk, pmk_len, KEY_FLAG_PMK);
else
return 0;
@@ -1342,9 +1323,8 @@
}
-static void wpa_supplicant_transition_disable(void *_wpa_s, u8 bitmap)
+void wpas_transition_disable(struct wpa_supplicant *wpa_s, u8 bitmap)
{
- struct wpa_supplicant *wpa_s = _wpa_s;
struct wpa_ssid *ssid;
int changed = 0;
@@ -1427,13 +1407,20 @@
}
+static void wpa_supplicant_transition_disable(void *_wpa_s, u8 bitmap)
+{
+ struct wpa_supplicant *wpa_s = _wpa_s;
+ wpas_transition_disable(wpa_s, bitmap);
+}
+
+
static void wpa_supplicant_store_ptk(void *ctx, u8 *addr, int cipher,
u32 life_time, const struct wpa_ptk *ptk)
{
struct wpa_supplicant *wpa_s = ctx;
ptksa_cache_add(wpa_s->ptksa, wpa_s->own_addr, addr, cipher, life_time,
- ptk, NULL, NULL);
+ ptk, NULL, NULL, 0);
}
#endif /* CONFIG_NO_WPA */
@@ -1454,6 +1441,16 @@
#endif /* CONFIG_PASN */
+static void
+wpa_supplicant_notify_pmksa_cache_entry(void *_wpa_s,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct wpa_supplicant *wpa_s = _wpa_s;
+
+ wpas_notify_pmk_cache_added(wpa_s, entry);
+}
+
+
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
{
#ifndef CONFIG_NO_WPA
@@ -1519,6 +1516,7 @@
#ifdef CONFIG_PASN
ctx->set_ltf_keyseed = wpa_supplicant_set_ltf_keyseed;
#endif /* CONFIG_PASN */
+ ctx->notify_pmksa_cache_entry = wpa_supplicant_notify_pmksa_cache_entry;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {
diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h
index 338af4e..dd692b9 100644
--- a/wpa_supplicant/wpas_glue.h
+++ b/wpa_supplicant/wpas_glue.h
@@ -27,4 +27,6 @@
void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const char *field_name, const char *txt);
+void wpas_transition_disable(struct wpa_supplicant *wpa_s, u8 bitmap);
+
#endif /* WPAS_GLUE_H */
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 7428f02..de7dc98 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -79,6 +79,18 @@
}
+static struct wpabuf * wpas_wps_get_wps_ie(struct wpa_bss *bss)
+{
+ /* Return the latest receive WPS IE from the AP regardless of whether
+ * it was from a Beacon frame or Probe Response frame to avoid using
+ * stale information. */
+ if (bss->beacon_newer)
+ return wpa_bss_get_vendor_ie_multi_beacon(bss,
+ WPS_IE_VENDOR_TYPE);
+ return wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+}
+
+
int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
{
if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
@@ -141,8 +153,7 @@
struct wpabuf *wps;
struct wps_parse_attr attr;
- wps = wpa_bss_get_vendor_ie_multi(bss,
- WPS_IE_VENDOR_TYPE);
+ wps = wpas_wps_get_wps_ie(bss);
if (wps && wps_parse_msg(wps, &attr) == 0 &&
attr.wps_state &&
*attr.wps_state == WPS_STATE_CONFIGURED)
@@ -1002,6 +1013,7 @@
* during an EAP-WSC exchange).
*/
wpas_notify_wps_event_fail(wpa_s, &data.fail);
+ wpa_s->supp_pbc_active = false;
wpas_clear_wps(wpa_s);
}
@@ -1204,6 +1216,8 @@
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
if (multi_ap_backhaul_sta)
ssid->multi_ap_backhaul_sta = 1;
+ wpa_s->supp_pbc_active = true;
+ wpa_s->wps_overlap = false;
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);
@@ -1371,6 +1385,7 @@
wpas_clear_wps(wpa_s);
}
+ wpa_s->supp_pbc_active = false;
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CANCEL);
wpa_s->after_wps = 0;
@@ -1705,7 +1720,7 @@
if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
return -1;
- wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpas_wps_get_wps_ie(bss);
if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
if (!wps_ie) {
wpa_printf(MSG_DEBUG, " skip - non-WPS AP");
@@ -1779,13 +1794,13 @@
int ret = 0;
if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
- wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpas_wps_get_wps_ie(bss);
if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
/* allow wildcard SSID for WPS PBC */
ret = 1;
}
} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
- wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpas_wps_get_wps_ie(bss);
if (wps_ie &&
(wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
@@ -1828,9 +1843,40 @@
}
+static bool wpas_wps_is_pbc_overlap(struct wps_ap_info *ap,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid,
+ const u8 *sel_uuid)
+{
+ if (!ap->pbc_active ||
+ os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
+ return false;
+
+ if (!is_zero_ether_addr(ssid->bssid) &&
+ os_memcmp(ap->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore another BSS " MACSTR
+ " in active PBC mode due to local BSSID limitation",
+ MAC2STR(ap->bssid));
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " MACSTR,
+ MAC2STR(ap->bssid));
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
+ ap->uuid, UUID_LEN);
+ if (!sel_uuid || os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0)
+ return true;
+
+ /* TODO: verify that this is reasonable dual-band situation */
+
+ return false;
+}
+
+
int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected, struct wpa_ssid *ssid)
{
+ struct wpa_supplicant *iface;
const u8 *sel_uuid;
struct wpabuf *wps_ie;
int ret = 0;
@@ -1859,36 +1905,21 @@
sel_uuid = NULL;
}
- for (i = 0; i < wpa_s->num_wps_ap; i++) {
- struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+ for (i = 0; i < iface->num_wps_ap; i++) {
+ struct wps_ap_info *ap = &iface->wps_ap[i];
- if (!ap->pbc_active ||
- os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
- continue;
-
- if (!is_zero_ether_addr(ssid->bssid) &&
- os_memcmp(ap->bssid, ssid->bssid, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "WPS: Ignore another BSS " MACSTR
- " in active PBC mode due to local BSSID limitation",
- MAC2STR(ap->bssid));
- continue;
+ if (wpas_wps_is_pbc_overlap(ap, selected, ssid,
+ sel_uuid)) {
+ ret = 1; /* PBC overlap */
+ wpa_msg(iface, MSG_INFO,
+ "WPS: PBC overlap detected: "
+ MACSTR " and " MACSTR,
+ MAC2STR(selected->bssid),
+ MAC2STR(ap->bssid));
+ break;
+ }
}
-
- wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
- MACSTR, MAC2STR(ap->bssid));
- wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
- ap->uuid, UUID_LEN);
- if (sel_uuid == NULL ||
- os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
- ret = 1; /* PBC overlap */
- wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
- MACSTR " and " MACSTR,
- MAC2STR(selected->bssid),
- MAC2STR(ap->bssid));
- break;
- }
-
- /* TODO: verify that this is reasonable dual-band situation */
}
wpabuf_free(wps_ie);
@@ -1907,7 +1938,8 @@
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
struct wpabuf *ie;
- ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+
+ ie = wpas_wps_get_wps_ie(bss);
if (!ie)
continue;
if (wps_is_selected_pbc_registrar(ie))
@@ -2995,6 +3027,48 @@
}
+bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_global *global = wpa_s->global;
+ struct wpa_supplicant *iface;
+
+ for (iface = global->ifaces; iface; iface = iface->next) {
+ if (iface == wpa_s)
+ continue;
+
+ if (!iface->supp_pbc_active)
+ continue;
+
+ /* Scan results are available for both links. While the current
+ * link will proceed for network selection, ensure the partner
+ * link also gets an attempt at network selection and connect
+ * with the selected BSS. */
+ if (iface->wps_scan_done)
+ wpa_wps_supplicant_fast_associate(iface);
+ else
+ return false;
+ }
+
+ return true;
+}
+
+
+bool wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_global *global = wpa_s->global;
+ struct wpa_supplicant *iface;
+
+ for (iface = global->ifaces; iface; iface = iface->next) {
+ if (iface == wpa_s)
+ continue;
+ if (iface->wps_overlap)
+ return true;
+ }
+
+ return false;
+}
+
+
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wps_ap_info *ap;
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 41b9298..5c8b833 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -87,6 +87,8 @@
const struct wpabuf *sel);
void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
+bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s);
+bool wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s);
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s);
@@ -146,6 +148,17 @@
{
}
+static inline bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s)
+{
+ return true;
+}
+
+static inline bool
+wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s)
+{
+ return false;
+}
+
static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s,
const u8 *bssid)
{