Cumulative patch from commit 7bf86e44161fe1bcadaf4ca2cc3b6358fb7f3bf9
7bf86e441 Define test config QCA vendor attribute for HE MU EDCA params
6cc77193c Define test config QCA vendor attribute for Tx beamformee NSTS
73d3f8841 HS 2.0: CoA-Request from Terms and Conditions server
abed6136a RADIUS: Add DAC implementation in hostapd(AS)
72aad113c RADIUS: Allow 0.0.0.0 to be used as wildard radius_das_client
2122fc83a RADIUS server: Add current_sessions SQLite table
d4e39c51f HS 2.0: Move Terms and Conditions Server URL generation from AP to AS
de3885fcc HS 2.0: Process Credential/UsernamePassword/EAPMethod nodes in PPS MO
3a8dd390c SAE: Allow authentication restart on AP
a10f5714c HS 2.0: Allow Hotspot 2.0 version number to be overridden for build
e8cf9fc33 P2P: Use more compact debug print of common group frequencies
26eac0a99 P2P: Improve common group channel selection if GO needs to be moved
616178a2a P2P/AP: More detailed debug prints on HT/VHT parameter selection
c6f01fbf2 nl80211: Add more command/event names to debug prints
4a0e01156 AP: Fix HT 20/40 co-ex transition timer cancellation on iface removal
b915f2cdf nl80211: Handle NL80211_CMD_WIPHY_REG_CHANGE event
c3e4f40cd FT: Derive PMKR0Name/PMKR1Name using SHA-384 with AKM 00-0F-AC:13
e8d08cf37 SAE: Do not drop STA entry on reauthentication in infrastructure BSS
ef13b33af FT: Support BIP-CMAC-256, BIP-GMAC-128, BIP-GMAC-256 in STA case
ff168f8c2 FT: Support 256-bit IGTK in AP case
2cf36d608 FT: Handle AssocResp generation failures as fatal errors
657b4c45a FILS: Fix KEK2 use in FT-FILS use cases
88bf44be4 FT: Fix potential NULL pointer dereference in MDE addition
661afb2ed FT: Fix RRB error path handling
df3bf6870 FT: Debug print of IE parsing element details
d8e8c992c FT: Add key management value FT-EAP-SHA384 for wpa_supplicant
c6f2eceb8 FT: Add key management value FT-EAP-SHA384 for hostapd
3d9cd0841 FT: Add support for variable length PMK-R1 receiving in RRB
a7968ea56 FT: FTE generation for SHA384-based AKM on AP
06f128660 FT: FTE generation for SHA384-based AKM on STA
9a33737a0 FT: FTE parsing for SHA384-based AKM
8c2715b35 FT: Connection settings for SHA384-based AKM
994eac7e6 FT: PMK-R0 derivation using SHA384-based AKM
1655e81c9 FT: PMKID derivation using SHA384-based AKM
40a2eb116 FT: PTK derivation using SHA384-based AKM
7880a6a2b FT: PMK-R1 derivation using SHA384-based AKM
b327026a7 FT: FTE MIC calculation using SHA384-based AKM
c49a9d6b9 FT: EAPOL-Key MIC calculation using SHA384-based AKM
9f12271b2 FT: XXKey derivation for SHA384-based AKM
a3e18dbb6 FT: Support variable length keys
c22bb5bba FT: SHA384-based AKM in RSNE processing
3a11c69e0 EAP-TLS: Derive Session-Id using TLS-Exporter when TLS v1.3 is used
2d26434ac EAP-TLS server: Fix EMSK derivation with TLS v1.3
f8aed720e EAP-TLS: Update key derivation label per draft-ietf-emu-eap-tls13-00
20c2ea412 wpa_supplicant: Fix parsing of max_oper_chwidth
8fd29a043 mesh: Fix crash with CONFIG_TAXONOMY enabled
8518326b2 Add some missing driver flags strings
b375b04b6 WNM: Fix time_zone advertisement without time_zone configuration
0928b6294 nl80211: Allow mesh interface to send channel switch request
f5a602168 HS 2.0: Allow OSEN connection to be used in an RSN BSS
8d660a4ba HS 2.0: Allow OSEN connection to be enabled in an RSN BSS
0fe3ede0a macsec_qca: Fix byte order of TX SCI port
22ac3dfeb EAP-pwd: Mask timing of PWE derivation
b8acd5011 EAP-pwd peer: Add SHA512 hash based salt mechanism
fb3675848 EAP-pwd peer: Add SHA256 hash based salt mechanism
7ef8adc7d EAP-pwd peer: Add SHA-1 hash based salt mechanism
7280723fb EAP-pwd peer: Check for unexpected state for ID exchange
44136f6ca EAP-pwd peer: Add support for salted password databases
0744e303c EAP-pwd peer: Move PWE derivation to commit exchange
d52ead3db EAP-pwd server: Add support for salted password databases
a8712ce5b EAP-pwd: Pre-processing method definitions from RFC 8146
2a5c29188 EAP-pwd: Move EC group initialization to earlier step
b829e4b69 Add a QCA vendor command attribute to enable/disable GTX
833bb2ab1 FT: Disable PMKSA caching with FT
91db940ff fst: Fix compile error in fst_ctrl_aux.h with C++ compilers
9be19d0b9 SAE: Add support for using the optional Password Identifier
d6a65a83f mesh: Register msg_ctx for hostapd/AP code
4e47eec5a Add QCA NAN vendor attributes to provide IPv6 information
997312964 wolfSSL: Fix crypto_bignum_rshift() wrapper
4b2e03c42 wolfSSL: DH initialization to call TEST_FAIL() for error tests
2b01270c8 wolfSSL: Fix ECDH set peer to use the index when importing point
ffff7f709 OWE: Mark connection failed in the unlikely no-bss-entry case
0cc41ee63 Move wpa_supplicant_event() EVENT_ASSOC_REJECT handling into a function
bda9c0859 hostapd: Fix CHAN_SWITCH command for VHT20 and VHT40
16d5c9637 OWE: Get the bss from bssid of assoc_reject to try for next group
c4dd3afa3 ACS: Avoid invalid interference factor when survey channel time is zero
6965705bf Fix style issues in qca-vendor.h
7d66e7c40 Define new QCA feature flag for self managed regulatory support
38f60142d Define test config vendor attribute for Tx beamformee configuration
d109aa6ca SAE: Flush PMKSA if an assoc reject without timeout is received
46053a4c9 wext: Avoid gcc warnings on restricted pointer aliasing
ce2a9644b Silence a gcc warning on switch statement fallthrough
d267bdf9b Silence new gcc warnings on switch statement fallthroughs
60a5737e8 FT: Clear SME FT data on disassoc
98b806b03 Add QCA vendor attribute for spectral hardware generation
d57e06170 wpa_supplicant: Make channel switch event available for non-AP builds
d7f038ded wpa_supplicant: Add ieee80211ac information in STATUS
ab35793ec wolfSSL: Fix EAP-FAST key derivation
71faf06cb wolfSSL: Do not free cert store after setting it
b7f5b0ec6 wolfSSL: Fix OCSP ifdefs
ca620a364 wolfSSL: Fix altSubjectName handling
7be462084 wolfSSL: Use defines from wolfssl/options.h
312212174 wolfSSL: Remove aes-omac1.o from hostapd build
385dd7189 wolfSSL: Use wolfSSL memory allocation in dh5_init()
6590d8466 wolfSSL: Load certificates using 'chain' APIs
fc5e88e3e wolfSSL: Changes for memory allocation failure testing
06657d316 wolfSSL: Fix crypto_hash_init() memory clearing
d39605710 wolfSSL: Fix crypto_ec_point_y_sqr()
e3501ac18 wolfSSL: Fix crypto_ec_point_solve_y_coord()
187ad3a30 wolfSSL: Add crypto_ecdh_*()
3d2f638d6 wolfSSL: Use new digest namespace
847665ebe wolfSSL: Fix conditional EAP-FAST compilation issue
d501c27cf EAP-TLS server: Disable TLS v1.3 by default
e8a7af9a3 EAP-TLS peer: Disable TLS v1.3 by default
bbbc7e801 EAP-TLS: Extend TLS version config to allow TLS v1.3 to be disabled
53b34578f OpenSSL: Move server vs. client information into connection data
0de820b33 EAP-TLS peer: MSK/EMSK derivation with TLS v1.3
1854981c7 EAP-TLS peer: Allow NewSessionTicket after Client Finished with TLS v1.3
6dd98483e EAP-TLS peer: Support fragmentation of last message
a80423b52 EAP-TLS server: MSK/EMSK derivation with TLS v1.3
bac1bdba3 EAP-TLS peer: Determine whether TLS v1.3 or newer is used
fe7b06c5e EAP-TLS server: Determine whether TLS v1.3 or newer is used
c26ac1895 Mark eap_server_tls_derive_key() label argument const
34c6949fb EAP-TLS server: Remove trailing whitespace
4a576c473 OpenSSL: Terminate TLS handshake if ClientHello cannot be generated
2439714f9 DPP: Fix testing code for invalid keys with OpenSSL 1.1.1
c456e6e3f HS 2.0: Terms and Conditions server and management
42f416916 HS 2.0: Update server SQL DB initialization to cover new fields
5bd5eb54d HS 2.0: Update server instructions for Ubuntu 16.04
04ee197f8 HS 2.0: Maintain a database of pending T&C acceptance sessions
625a8c86b wpa_cli: Indicate HS20-T-C-ACCEPTANCE to action scripts
6b21df0bb Add QCA vendor command/attr to filter frames from other BSSs
452603809 HS 2.0: Terms and Conditions testing feature in authentication server
f456940ef HS 2.0: CoA-Request processing for Terms and Conditions filtering
d239ab396 DFS: Mark channels required DFS based on reg-domain info from the driver
7fc6a024f HS 2.0: Process received Terms and Conditions Acceptance notification
8760b9848 HS 2.0: Send Terms and Conditions Acceptance notification
6cb8f4f38 HS 2.0: Terms and Conditions attributes in Access-Request messages
1952b626b hostapd: Add ctrl iface indications for WDS STA interface
2598e6930 FILS: Enable SHA256 KDF even without PMF/SAE in the build
1baa130bd nl80211: Print NL80211_CMD_SET_POWER_SAVE errors in debug log
72b6e5d1e Do not remove CCMP group cipher if any CCMP/GCMP cipher is enabled
c4315e662 AP: Handle AP initalization failure in async flow
86c998d37 FT: Add FT auth algorithm to connect params when roaming
3dc3afe29 FT: Add MDE to assoc request IEs in connect params
b55c623e4 Make CENTER_FRQ1 available independently in SIGNAL_POLL
4204669c6 HS 2.0: Add Roaming Consortium Selection element into AssocReq
6311547e7 HS 2.0: Add Roaming Consortium Selection network profile parameter
5b7695275 HS 2.0: Use roaming_consortiums list to match OIs for access
2e88032f1 HS 2.0: OSU client to send HomeSP/RoamingConsortiumOI to wpa_supplicant
909a948b0 HS 2.0: Add a new cred block parameter roaming_consortiums
9b6f93e4d HS 2.0: Document credential parameter required_roaming_consortium
67cca3464 HS 2.0: Copy Roaming Consortium OI from (Re)AssocReq to Access-Request
0e450db28 HS 2.0: Allow configuration of operator icons
6a8a04d74 HS 2.0: Add fetching of Operator Icon Metadata ANQP-element
37547ad63 wpa_supplicant: Increase authentication timeout if CAC is started
2dd5fbbff wpa_supplicant: Rename wpas_event_*() to wpas_ap_event_*()
f875da044 nl80211: Add MLME auth work-around for -EEXIST errno
4449efeb1 Fix building nt_password_hash with gnutls
5ecdf06c8 DPP: Fix build with LibreSSL v2.5
4b603f01d DPP: Fix X509_ALGOR_get0() use with LibreSSL
dee566d98 OpenSSL: Skip SSL_OP_NO_TLSv1_3 if not defined to fix LibreSSL build
ba3658cff Fix build with LibreSSL 2.7
a2ab37308 Fix sae_password documentation in wpa_supplicant to refer correct field
d5906fbb7 mesh: Properly handle sae_password
d6d7debb5 Fix wpa_supplicant build with CONFIG_NO_WPA
852b2f273 SAE: Only allow SAE AKMP for PMKSA caching attempts
06b1a1043 SAE: Fix default PMK configuration for PMKSA caching case
8e402d165 WPA: Fix a typo in a debug message
1bd131105 Clear pmk_len more consistently for extra protection
66dbc8d9c Add more debug prints for wpa_sm_set_pmk() calls
26e0ada47 Define new test config attribute for HE LTF configuration
4d6eb9f2e Fix hostapd wmm_enabled setup on config reload path
a6509e850 FT: Add session_timeout to push/resp
3cb286ca4 FT: Add set/get session_timeout callback functions
13f118dc5 Convert STA session_timeout to os_reltime
069b4e304 FT: Include identity and radius_cui in pull/resp frames
9fbe292ff FT: Move wpa_ft_rrb_build_r0() special case to caller
8c345234f FT: Add set/get identity/radius_cui callback functions
17a8a9893 Fix potential memory leak with identity/radius_cui
17010c38d FT: Add IEEE VLAN support (including tagged VLANs)
47a039b01 FT: Add set_vlan()/get_vlan() callback functions
3a3e28320 FT: Add expiration to PMK-R0 and PMK-R1 cache
09211c989 FT: Use dl_list for PMK-R0/R1 cache
89dea17e1 FT: Add helper function for FILS key storing
83fe4bd3b FT: Convert r0_key_lifetime to seconds
ee2c6bb5e Remove CONFIG_IEEE80211R_AP=y build option from wpa_supplicant
c6b5b9a33 hostapd: Add more authentication error case debugging
e2fc13d0a hostapd: Add logging around Michael MIC related failures
e7525a295 hostapd: Add send_auth_reply() caller info in debug logging
d89edb611 wpa_supplicant: Don't reply to EAPOL if pkt_type is PACKET_OTHERHOST
8fb2b3573 Clean up setting of iface->p2p_mgmt flag
d53401c54 dbus: Redirect signal processing to the management device if present
e48021276 dbus: Add FILS to global capabilities
208263c01 Add config information related to MACsec
e360010c3 dbus: Add mesh to global capabilities
9f917339c tests: Fix clearing of IGTK PN in handshake retransmit test functions
45f7574d3 Propagate the EAP method error code
77a020a11 wpa_supplicant: Fix auth failure when the MAC is updated externally
2ff9696d3 Add definitions for RADIUS attributes standardised in RFC 7055
c17915462 nl80211: Add DFS offload support using upstream nl80211 definitions
1841086ad Sync with mac80211-next.git include/uapi/linux/nl80211.h
7cfe2f001 Add SAR V2 power selection capability (QCA vendor attributes)
7e1d3ee96 Add hostapd.conf venue_url to set Venue URL ANQP-element
64624f31c OWE: Fix CONFIG_OWE=y build without CONFIG_IEEE80211R=y
d555c39c4 FILS: Fix CONFIG_FILS=y build without CONFIG_IEEE80211R=y
f6de72534 Add NOTE control interface command for hostapd
2f3738781 FILS: Add more complete support for FT-FILS use cases
4ddca8142 FT: Derive PMK-R1 locally if requested PMKR0Name is found
1dc0945cc FT: Do not send PMK-R1 pull request to own R0KH address
1778f1e9a SAE: Fix PTK derivation to use KDF-SHA256
9d94e4bb6 SAE: Fix PMKID in EAPOL-Key msg 1/4
a03f9d17e SAE: Fix FT-SAE key derivation for a case where PMKID in msg 1/4 matches
0fa669bca Fix a resource leak on hostapd maclist parsing error path
10949e248 Do not disable 40 MHz based on co-ex report with matching primary channel
8cbd9c3ed Support multiple 20/40 BSS Intolerant Channel Report elements
8127a0ac0 Ignore intra-BSS 20/40 BSS Coexistence Management from not-associated STA
8aa599d45 Add more debug prints for 20/40 BSS Coexistence Management frame Rx
d58c3bd8b hostapd: Ignore LOW_ACK event for co-operative steering clients
ec2b5173c Make STA opmode change event available to upper layers
e8ada1600 nl80211: Add support for STA opmode change events
72123a84c hostapd: Add last_ack_rssi into ctrl iface cmd STA
2df73f52e Add hostapd_cli poll_sta command
f5701cc66 OWE: Clean up pointer check in a testing code path
ebe61e564 Sync with mac80211-next.git include/uapi/linux/nl80211.h
8179ae3a2 DPP: Support retrieving of configurator's private key
4bc801ab4 SAE: Fix EAPOL-Key integrity and key-wrap algorithm selection
d74963d41 DPP: Extend dpp_test 89 functionality to transmit side
f8bfc9cbd Use correct WPA_ALG_* values to compare for enum wpa_alg
cce16e43f mka: Mark ieee802_1x_kay_create_mka() ckn and cak arguments const
22151b111 wpa_supplicant: Fix memory leaks in ieee802_1x_create_preshared_mka()
3a52f6b38 mka: Do not print contents of SAK to debug log
77977b3d5 mka: Detect duplicate MAC addresses during key server election
5762855ab mka: Loss of live peers to result in connect PENDING not AUTHENTICATED
8fb546d8e mka: Ignore MACsec SAK Use Old Key parameter if we don't have our old key
b54b53e64 mka: When matching CKNs ensure that lengths are identical
fec03f983 Add support for wolfSSL cryptographic library
92eb00aec Extend ACL check for Probe Request frames
01542e651 Add new WiFi test config attributes to configure BA params
ba6080441 Add new WiFi test config attribute to allow WEP/TKIP in HE
c05f96a38 FILS: Check kde more consistently to avoid static analyzer warnings
087474512 SAE: Debug print group support in the crypto library
f5e0a3324 SAE: Fix potential infinite loop in mismatching PMK case on AP
427729ee6 Reject eap_server_erp hostapd.conf parameter without CONFIG_ERP=y
c54cc8bb1 BoringSSL: Set appropriate sigalgs for Suite B RSA 3K cases
aa6de8e6b BoringSSL: Map OpenSSL SUITEB192 cipher into appropriate sigalgs
7a47f34b1 BoringSSL: Map OpenSSL SUITEB192 cipher into appropriate parameters
355250234 OpenSSL: Replace SSL_set1_curves_list() with SSL_set1_curves()
4ab0f11b8 Allow HT40 on 5 GHz channels 165 and 169
299d21e8e nl80211: Use the new NL80211_MFP_OPTIONAL option
b8e88d357 wpa_supplicant: Handle port authorized event
a8c45d47d nl80211: Handle port authorized event
0a20bd7d9 driver: Add port authorized event
05fc7c68f nl80211: Add API to set the PMK to the driver
0ff08f963 nl80211: Check 4-way handshake offload support
730c5a1d0 nl80211: Support passing PSK on connect
14dcb22a5 wpa_passphrase: Include $(LIBS) for linking
4b07484c3 DPP: Do not include common/dpp.h without CONFIG_DPP=y
9ec0dfa31 Define host_to_le64() for Windows builds
6e3726c09 Fix a typo in disassoc_low_ack documentation
19e20c14f Add ap_isolate configuration option for wpa_supplicant AP mode
a4016163e Extend APF interface for read/write and enable/disable ops
b2e4074ca OpenSSL: Fix EAP-FAST with OpenSSL 1.1.1-pre1
a22e235fd OWE: Add testing RSNE for OWE assoc response with driver SME/MLME
aca4d84e3 DPP: Use wildcard BSSID in GAS query frames
0887215d9 nl80211: Do not try to add too large NL80211_ATTR_PMK for set/del PMKSA
e7f6e6ee1 nl80211: Print NL80211_CMD_{SET,DEL}_PMKSA failures in debug log
3988046de hostapd: Dynamic MAC ACL management over control interface
6a252ece2 DPP: Fix GAS query removal race condition on DPP_STOP_LISTEN
3b50f8a46 DPP: PKEX initiation on other bands
659ac96d7 ieee802_11_mgmt: Handle frame info more consistently
458d8984d SAE: Reject request with mismatching PMKID (no PMKSA cache entry)
5ac434642 Define new QCA vendor sub command for wifi test configuration
70e190137 DPP: Require use of PMF for DPP AKM
79ce2d519 OWE: Rename function to match use (driver-SME/MLME)
759da93a8 OWE: Check for ECDH availability before use (driver-SME/MLME)
af65ef28f OWE: Add RSNE when not using PMKSA caching (driver-SME/MLME)
5850cba38 OWE: Support DH groups 20 and 21 with driver-SME/MLME
f9854c183 hostapd: Fix wpa_psk_file support for FT-PSK
3bd35b681 wpa_supplicant: Fix parsing errors on additional config file
9c5fe742a wpa_supplicant: Free config only if it was allocated in same call
9f8d459d4 OWE: Fix association IEs for transition mode open AP connection
c6096c6ee wpa_cli: Enable add/remove/get vendor elements without P2P
d1e3d40d0 common: Fix the description of wpa_ctrl_request() function
82424732a Fix compiler issue with CONFIG_TESTING_OPTIONS
ebf404373 D-Bus: Report error on starting P2P find
66b9f3ca8 wpa_cli: Fix cred_fields[] declaration
5ff39c138 SAE: Support external authentication offload for driver-SME cases
40a68f338 nl80211: Create a netlink socket handle for the Connect interface
ba71cb821 nl80211: Introduce the interface for external authentication
8678b14fa Add new QCA vendor commands for thermal shutdown
99576f6f3 Sync with mac80211-next.git include/uapi/linux/nl80211.h
cd483be25 OWE: Use PMKSA caching if available with driver AP MLME
a4668c681 OWE: Handle unsupported finite cyclic group with driver MLME
f811cc83b atheros: Send correct SSID length to the driver
e005725a6 nl80211: Add DPP and OWE AKM selectors to CONNECT/ASSOC request
2cc2a0a7e Extend QCA vendor NDP params to support schedule update indication
a7769f6da Update QCA vendor commands to match ASOP
18e3e9c6e OWE: Transition mode with non-AP-MLME
92b6e0c58 hostapd: Send broadcast Public Action frame with wildcard BSSID address
fe3f81e6a DPP: Update PKEX role-specific points
b4cd8b7e1 Add QCA vendor command and attributes for MSDU queue depth threshold
703470bfa FILS: Fix extended capability bit setting for FILS in AP mode
9e834fc64 EAP-SIM/AKA: Separate identity for MK derivation
b6b5e3554 DPP: Get rid of compiler warnings on signed/unsigned comparison
630ea1334 DPP: Track M.x/N.x/L.x availability for ke derivation
c1790a5ff OWE: Allow station in transition mode to connect to an open BSS
c103c0b51 Extend NUD Stats to collect the data packet statistics
5d5ee699a Copy WLAN-Reason-Code value from Access-Reject to Deauthentication
f75ed556c RADIUS: Add WLAN-Reason-Code attribute to Access-Reject
727e9aacb HS 2.0: Set appropriate permission(s) for cert file/folders on Android
feba5848b Replace RSNE group key management mismatch status/reason codes
9cc8303d3 Add QCA vendor command to get the WLAN MAC information
fd9209549 DPP: Report reception of Config Request to upper layers
80f71b71d DPP: Authentication exchange retries and channel iteration in hostapd
c5cc7a59a Report offchannel RX frame frequency to hostapd
b7ed94834 GnuTLS: Add option to build with libnettle instead of libgcrypt
c36d82241 GnuTLS: Suite B validation
dd227e67a GnuTLS: Add support for disabling TLS versions
5d292fcfb GnuTLS: Implement tls_get_cipher()
5791d2117 GnuTLS: Make debug prints clearer for cert/key parsing
622d4d49e GnuTLS: Add TEST_FAIL() to crypto routines for testing purposes
f8697e810 GnuTLS: Implement tls_get_version()
133f8d09f GnuTLS: Implement HMAC functions using libgcrypt
85c12a62e GnuTLS: Implement sha{256,384,512}_vector() using libgcrypt
cc3e7bfc3 GnuTLS: Use a helper function for hash functions
2cb40e9f4 OWE: Try all supported DH groups automatically on STA
02b38d0ad Fix MFP-enabled test for disallowed TKIP
ba3d435fe SAE: Add option to require MFP for SAE associations
c4fc7e31c SAE: Set special Sc value when moving to Accepted state
abcbd0604 SAE: Add Rc variable and peer send-confirm validation
9249afc8e SAE: Print state changes in debug log
d8b841eba SAE: Make dot11RSNASAESync configurable
9596a7565 PAE: Remove OpenSSL header dependency
04b1bcc5f EAP-pwd: Use abstract crypto API
0c3d49afd EAP-EKE: Use abstract crypto API
c335507c8 Enhance crypto abstract API for DH
0e02f2a9f crypto: Implement new crypto API functions for DH
eac084cb3 OpenSSL: Implement new crypto API functions
23ff5d73d Enhance crypto abstract API
441e8bfbb Fix copy-paste errors in function comments
47e966aba DH: Remove trailing whitespace
3546ef510 EAP-pwd: Remove trailing empty line
61536b5f4 Add new QCA vendor attribute for getting preferred channel
1a2cb20d5 Fix couple of QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS comments
2fca2d214 Vendor parameter for forcing RSNE override
12bce14b1 wpa_cli: Add completion for get/set cred commands
6240424a7 dbus: Add FILS key mgmt values into BSS security properties
2d942ec4a Define a QCA vendor command to retrieve SAR Power limits
d98038bb0 FILS: Driver configuration to disable/enable FILS features
af832aa99 hostapd: Add average channel utilization in STATUS
802c0fd0c hostapd: Update BSS load update period dynamically
778d87054 Fix error handling in bss_load_update_period parser
dff5ab97e hostapd_cli: Add dpp_listen and dpp_stop_listen
5f30b69cd OWE: Allow DH Parameters element overriding with driver SME
04ded82ef OWE: Fix error case handling with drivers that implement AP SME
c23e87d0d OpenSSL: Replace EVP_PKEY_paramgen() with EC_KEY_new_by_curve_name()
7641d485d BoringSSL: Use EC_KEY_new_by_curve_name() to simplify implementation
a5da39607 Revert "BoringSSL: Add DPP special cases regardless of claimed version number"
0f3084472 Revert "BoringSSL: Define RSA_bits() helper"
a2c442be2 OpenSSL: Allow cipher list to be overridden for tls_suiteb=1 case
e5c7c2f28 Fix ap-mgmt-fuzzer build
63942cf0f OpenSSL: Avoid SSL*_use_default_passwd_cb()
149143e31 OpenSSL: Remove unnecessary os_strdup() from password callback
b65353a76 Android: Set CONFIG_NO_RANDOM_POOL=y
ff28df739 Android: Move hostapd to vendor partition
9b0de99fa Add new QCA vendor attribute for WLAN Latency Module (WLM)
055cd3978 tests: DPP P-256 test vectors
f55269753 DPP: Allow protocol key to be overridden for testing purposes
afd3cf14a WPS: Add GCMP-256 and CCMP-256 cipher options on Enrollee
5f481b6fd WPS: Check BSS table against current BSSID if credential does not match
402c8e00b WPS: Map GCMP-256 and CCMP-256 to AES encryption type
a2660890a WPS: Allow WPS to be enabled in CCMP-256 and GCMP-256 only cases
163fc3d56 DPP: Indicate to upper layers whether mutual authentication was used
34603767b DPP: Extend protocol testing to allow exchange termination
ed2c493e9 DPP: Change Authentication Response retry time to 1 second
963d3149a nl80211: Fix NL80211_MESHCONF_AUTO_OPEN_PLINKS encoding
a2426829c nl80211: Fix NL80211_ATTR_SMPS_MODE encoding
f0a383a98 DPP: Extend dpp_test with invalid E-Nonce in Config Req
55c6c8585 DPP: Extend dpp_test with invalid Transaction ID in Peer Disc Req
364039d22 Add QCA vendor command and attributes for RROP
0e19300d5 nl80211: Use consistent "0x" prefix for the cookie values
fa5c90749 DPP: Call wpas_dpp_stop() from wpas_dpp_deinit()
1866dfb52 DPP: Stop pending GAS client operation on DPP_STOP_LISTEN
f981ce729 DPP: Do not continue if public key hash derivation fails
8a37d54ea DPP: Fix memory leak on dpp_auth_build_conf() error paths
281f480c5 JSON: Fix a memory leak on an error path
622934128 DPP: Fix a memory leak on an error path
ed62d4011 DPP: Deinit PKEX instance on DPP_STOP_LISTEN
634a130a5 DPP: Clear authentication instance on configuration completion in AP
d3cb7ebe1 DPP: Do not process dpp_auth_ok_on_ack multiple times
dc2f24f1b DPP: Fix compilation without CONFIG_TESTING_OPTIONS=y
82feacced DPP: Ignore GAS server status callback for unknown response
a2588be82 DPP: Add DPP_CONFIGURATOR_SIGN support to hostapd
7eb6bfb45 DPP: Move hostapd Configurator/bootstrap data into global context
73f21929a DPP: Auto-generate Initiator bootstrapping info if needed
725a953ae nl80211: Filter global events based on wiphy
0bd7f104c hostapd: Add supported rate information into STATUS and STA
1489fcf87 FILS: Do not leave error value in left counter
beae255a7 WPA: Check wpa_eapol_key_mic() result on TX
acc555f9e DPP: Allow PKEX x/X and y/Y keypairs to be overridden
d722c50d0 DPP: Print more interim EC_POINT results into debug log
2bdc47a94 DPP: Allow PKEX own/peer MAC addresses to be overridden
af4103e5e DPP: Provide peer_mac to PKEX Initiator through function argument
918a2ac40 DPP: Work around missing EVP_PKEY_CTX_set_ec_param_enc()
3ca4be1ea DPP: Remove compiler warnings about signed/unsigned comparisons
116454f46 DPP: Fix error return value in dpp_auth_conf_rx()
63dc0f9c4 hostapd: Disassoc STA without WPA/RSN IE if AP proto is WPA/RSN
cc79e06f0 hostapd: Add wpa_msg_ctrl() to report Probe Request frames from STA
0db637ca8 DPP: Fix number of Authentication Request retry cases
921f5acd1 DPP: Take response wait time into account for init retries
248264c62 DPP: Stop Authentication Request attempts if no response after ACK
e3a5882b3 DPP: Add SAE credential support to Configurator
5dd745b73 DPP: Add akm=sae and akm=psk+sae support in Enrollee role
a44467395 DPP: Protocol testing capability to send invalid I-Nonce in Auth Req
00d2d13db DPP: Retry PKEX Exchange Request frame up to five times
ab0375508 TDLS: Add testing capability to send TPK M2 twice
4b8de0c92 DPP: Protocol testing for invalid Peer Discovery Req/Resp values
f9cf7d03f DPP: Protocol testing for invalid Config Attrib Object value
3f35ec2dc DPP: Protocol testing for invalid DPP Status value
9efa53149 DPP: Use helper functions to build Bootstrap Key Hash attributes
acdf703d5 DPP: Replace custom undefined attr with DPP Status in after-wrapped data
56f24d1da DPP: Use a helper function to build DPP Status attribute
65ecce87f DPP: Protocol testing for writing invalid I/R Bootstrap Key Hash
b6b4226bd DPP: Protocol testing capability to generate invalid Protocol Key
c6eb3e34b Stronger GTK derivation routine
94619905c DPP: Fix dpp_test_gen_invalid_key() with BoringSSL
746c1792a DPP: Build bootstrapping key DER encoding using custom routine
f2d27ef94 DPP: Use a helper function to DER encode bootstrapping key
c1564149a BoringSSL: Add AES support with 192-bit keys
edd72f55f OpenSSL: Debug message if requested AES key length is not supported
5548453a2 BoringSSL: Add DPP special cases regardless of claimed version number
f29761297 BoringSSL: Implement crypto_ecdh_init()
7721fe082 BoringSSL: Comment out SSL_set_default_passwd_cb*() calls
b9dc63c26 BoringSSL: Comment out SSL_set1_sigalgs_list() call
3cfbd3b0f BoringSSL: Define RSA_bits() helper
27781c0ab Allow group cipher selection to be overridden
af6614ca1 Fix block comment style in QCA vendor attribute definition
2115603a4 Add QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING
27987b67f Add new QCA vendor attribute for LL stats
41db74cf7 atheros: Process SAE authentication frames using EVENT_RX_MGMT
3d9dd4b77 atheros: Generate EVENT_TX_STATUS events for management frames
f5b74b966 common: Avoid conflict with __bitwise macro from linux/types.h
4109555ef DPP: Fix compiler warning of testing code
1d624a070 Reject PMK-to-PTK derivation with unsupported cipher
762fb4f06 DPP: Testing capability to send unexpected Authentication Response
03abb6b54 DPP: Reject unexpected Req/Resp message based on Auth/PKEX role
95b0104a3 DPP: Retransmit DPP Authentication Response frame if it is not ACKed
c1d377396 DPP: Stop authentication exchange of DPP_STOP_LISTEN
d1f082644 DPP: Allowed initiator to indicate either role
f97ace34c DPP: Support multiple channels for initiating DPP Authentication
de0298618 DPP: Share a helper function for PKEX final steps
a306ed5a5 DPP: Protocol testing to allow missing attributes in peer discovery
1fafdf112 DPP: Add DPP_LISTEN and DPP_STOP_LISTEN to hostapd
fbfceef3a Add QCA vendor commands for spectral scan
bb9808fa9 P2P: Continue P2P_WAIT_PEER_(IDLE/CONNECT) sequence on a listen cancel
37ed3254d P2P: ACS offload for the autonomous GO
962b8fcf4 Add new QCA vendor attributes for MAC counters
af7f10fcd DPP: Protocol testing for invalid Config Resp attribute values
8c99e6264 DPP: Report Config Request/Response failure reasons on control interface
f411ad1b8 DPP: Protocol testing to remove attributes from Config Req/Resp
7e0ebe21b DPP: Protocol testing - invalid I/R-Auth value in PKEX Commit-Reveal
89d0bf678 DPP: Protocol testing - invalid Bootstrap Key value in PKEX Commit-Reveal
f31ef96dc DPP: Protocol testing - invalid Status value in PKEX Exchange Response
d05c82c4d DPP: Move PKEX z derivation on Responder to earlier phase
578c9ea1a DPP: Fix a typo in a debug print
5f5fff436 DPP: Explicitly check that PKEX Qr is not the point-at-infinity
29ab69e4b DPP: PKEX counter t
039b8e736 DPP: Terminate PKEX exchange on detection of a mismatching code
fc0efa2a1 DPP: Use dpp_bn2bin_pad() helper to simplify code
e0247e798 DPP: PKEX and STATUS_BAD_GROUP
2265353a4 DPP: Remove obsolete TODO comment on discovery object
fe12ae777 Fix Status Code in TKIP countermeasures case
1cfcbd32a DPP: Testing capability to generate invalid PKEX encrypted key (M and N)
d7e7b7122 DPP: Report PKEX failure reasons over control interface
61f9f27f8 DPP: Extend protocol testing to cover missing attributes in PKEX
b3e4cc5cb DPP: Move PKEX Commit-Reveal Response building to a helper function
b0626c2a6 DPP: Move PKEX Commit-Reveal Request building to a helper function
a5c3b41b2 DPP: Move PKEX Exchange Response building to a helper function
60b9dd86f DPP: Fix couple of typos in debug messages
06f2df069 DPP: Fix hostapd control interface events for initiator case
219d4c9fc DPP: Report possible PKEX code mismatch in control interface
69d8d029f DPP: Enable PMF when adding wpa_supplicant network profile
0c3bc1be0 Fix test build breakage when not compiling with ieee80211w support
ea4ace9c7 hostapd: Add max_txpower into STATUS command
bf6c65afc hostapd: Add Beacon interval and DTIM period into STATUS command
c7ae2b310 hostapd: Add HT/VHT capability info into STATUS command
1f91a8bde hostapd: Add HT/VHT capability info into STA command
65f9db6bc hostapd: Add extended capabilities into STA command
d1f3a8144 hostapd: Add [HT] flag into STA command
ba72b4b12 hostapd: Add Min/Max Transmit Power Capability into STA command
33c8bbd8c OWE: Add AP mode handling of OWE with drivers that implement SME
28d126413 Check hostapd current_mode before dereferencing it in additional places
348c93847 AP-side workaround for WNM-Sleep Mode GTK/IGTK reinstallation issues
3f5a1860a wpa_auth: Deplete group rekey eloop handler for strict rekeying
92662fb28 Allow forcing group rekeying for testing purposes
d27092069 DPP: Negotiation channel change request from Initiator
b7dddab7b DPP: Allow testing override values to be cleared
e85b66012 DPP: Add DPP Status attribute into Peer Discovery Response
19ef4289c DPP: Process Authentication Confirm failure cases
7d917ab04 DPP: Send Authentication Confirm failure reports
978bc3f2a DPP: Auth Resp/Conf incorrect attribute values for protocol testing
9b5111203 DPP: Allow Responder to decide not to use mutual authentication
dcdaeab79 DPP: Report Auth Conf failures in control interface
f9c7d7702 DPP: Omission of Auth Conf attributes for protocol testing
26806abe8 DPP: Report invalid messages and failure conditions in control interface
ce9acce00 DPP: Omission of Auth Resp attributes for protocol testing
a03406dbe DPP: Move Authentication Response building into a separate function
0e7cb8c6c DPP: Omission of Auth Req attributes for protocol testing
606a8e8d5 DPP: Move Authentication Request building into a separate function
3749ad0e1 DPP: Explicitly check and reject 0x00 and 0x03 I/R-capab role
af48810ba DPP: Report transmitted messages as control interface events
a70739349 DPP: Report received messages as control interface events
27fefbbb7 DPP: Remove unnecessary Wrapped Data checks from callers
0c881807b DPP: Verify that Wrapped Data attribute is the last one in the message
60239f60a DPP: Protocol testing framework
7ed5337d8 Fix a typo in a debug message
5f7c9e50f EAP server: Add event messages for more EAP states
a68e0d869 Fix a typo in a comment (the variable is ptk, not pkt)
de41b960b wpa_supplicant: Increase UDP control interface RX buffer
74e55b658 Fix test build breakage when not compiling with mesh support
3d0fb9558 WNM: Ignore BSS Transition Management frames in bss_transition=0 case
114f2830d WNM: Ignore WNM-Sleep Mode Request in wnm_sleep_mode=0 case
Bug: 111131936
Test: Device boots up and connects to wifi networks.
Test: Able to turn on/off softap.
Test: Sent for regression tests (b/111209415).
Change-Id: I85cbeeee6a4ff52a5e23f8917b15633c896c0cf3
Signed-off-by: Roshan Pius <rpius@google.com>
diff --git a/src/ap/Makefile b/src/ap/Makefile
index b8c167c..9b07ee1 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -10,6 +10,7 @@
CFLAGS += -DHOSTAPD
CFLAGS += -DNEED_AP_MLME
+CFLAGS += -DCONFIG_ETH_P_OUI
CFLAGS += -DCONFIG_HS20
CFLAGS += -DCONFIG_INTERWORKING
CFLAGS += -DCONFIG_IEEE80211R
@@ -34,6 +35,7 @@
dhcp_snoop.o \
drv_callbacks.o \
eap_user_db.o \
+ eth_p_oui.o \
gas_serv.o \
hostapd.o \
hs20.o \
diff --git a/src/ap/acs.c b/src/ap/acs.c
index aa59058..6d4c041 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -314,7 +314,7 @@
/* TODO: figure out the best multiplier for noise floor base */
factor = pow(10, survey->nf / 5.0L) +
- (busy / total) *
+ (total ? (busy / total) : 0) *
pow(2, pow(10, (long double) survey->nf / 10.0L) -
pow(10, (long double) min_nf / 10.0L));
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 07310f9..820cba9 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -10,6 +10,7 @@
#include "utils/common.h"
#include "crypto/sha1.h"
+#include "crypto/tls.h"
#include "radius/radius_client.h"
#include "common/ieee802_11_defs.h"
#include "common/eapol_common.h"
@@ -103,11 +104,13 @@
bss->rkh_neg_timeout = 60;
bss->rkh_pull_timeout = 1000;
bss->rkh_pull_retries = 4;
+ bss->r0_key_lifetime = 1209600;
#endif /* CONFIG_IEEE80211R_AP */
bss->radius_das_time_window = 300;
bss->sae_anti_clogging_threshold = 5;
+ bss->sae_sync = 5;
bss->gas_frag_limit = 1400;
@@ -123,6 +126,11 @@
#ifdef CONFIG_MBO
bss->mbo_cell_data_conn_pref = -1;
#endif /* CONFIG_MBO */
+
+ /* Disable TLS v1.3 by default for now to avoid interoperability issue.
+ * This can be enabled by default once the implementation has been fully
+ * completed and tested with other implementations. */
+ bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
}
@@ -407,6 +415,7 @@
hostapd_config_free_radius_attr(user->accept_attr);
os_free(user->identity);
bin_clear_free(user->password, user->password_len);
+ bin_clear_free(user->salt, user->salt_len);
os_free(user);
}
@@ -473,6 +482,22 @@
}
+static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
+{
+ struct sae_password_entry *pw, *tmp;
+
+ pw = conf->sae_passwords;
+ conf->sae_passwords = NULL;
+ while (pw) {
+ tmp = pw;
+ pw = pw->next;
+ str_clear_free(tmp->password);
+ os_free(tmp->identifier);
+ os_free(tmp);
+ }
+}
+
+
void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
if (conf == NULL)
@@ -576,6 +601,7 @@
os_free(conf->roaming_consortium);
os_free(conf->venue_name);
+ os_free(conf->venue_url);
os_free(conf->nai_realm_data);
os_free(conf->network_auth_type);
os_free(conf->anqp_3gpp_cell_net);
@@ -609,7 +635,16 @@
}
os_free(conf->hs20_osu_providers);
}
+ if (conf->hs20_operator_icon) {
+ size_t i;
+
+ for (i = 0; i < conf->hs20_operator_icon_count; i++)
+ os_free(conf->hs20_operator_icon[i]);
+ os_free(conf->hs20_operator_icon);
+ }
os_free(conf->subscr_remediation_url);
+ os_free(conf->t_c_filename);
+ os_free(conf->t_c_server_url);
#endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements);
@@ -640,7 +675,7 @@
wpabuf_free(conf->dpp_csign);
#endif /* CONFIG_DPP */
- os_free(conf->sae_password);
+ hostapd_config_free_sae_passwords(conf);
os_free(conf);
}
@@ -936,7 +971,9 @@
if (full_config && bss->wps_state && bss->wpa &&
(!(bss->wpa & 2) ||
- !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)))) {
wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
"WPA2/CCMP/GCMP forced WPS to be disabled");
bss->wps_state = 0;
@@ -1046,8 +1083,12 @@
if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
bss->rsn_pairwise = bss->wpa_pairwise;
- bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
- bss->rsn_pairwise);
+ if (bss->group_cipher)
+ bss->wpa_group = bss->group_cipher;
+ else
+ bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+ bss->wpa_pairwise,
+ bss->rsn_pairwise);
if (!bss->wpa_group_rekey_set)
bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
600 : 86400;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 89bf289..5b71126 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -160,6 +160,8 @@
} methods[EAP_MAX_METHODS];
u8 *password;
size_t password_len;
+ u8 *salt;
+ size_t salt_len; /* non-zero when password is salted */
int phase2;
int force_version;
unsigned int wildcard_prefix:1;
@@ -169,6 +171,7 @@
unsigned int macacl:1;
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
struct hostapd_radius_attr *accept_attr;
+ u32 t_c_timestamp;
};
struct hostapd_radius_attr {
@@ -201,6 +204,12 @@
u8 name[252];
};
+struct hostapd_venue_url {
+ u8 venue_number;
+ u8 url_len;
+ u8 url[254];
+};
+
#define MAX_NAI_REALMS 10
#define MAX_NAI_REALMLEN 255
#define MAX_NAI_EAP_METHODS 5
@@ -230,6 +239,12 @@
char realm[];
};
+struct sae_password_entry {
+ struct sae_password_entry *next;
+ char *password;
+ char *identifier;
+ u8 peer_addr[ETH_ALEN];
+};
/**
* struct hostapd_bss_config - Per-BSS configuration
@@ -248,7 +263,8 @@
int max_num_sta; /* maximum number of STAs in station table */
int dtim_period;
- int bss_load_update_period;
+ unsigned int bss_load_update_period;
+ unsigned int chan_util_avg_period;
int ieee802_1x; /* use IEEE 802.1X */
int eapol_version;
@@ -325,6 +341,7 @@
PSK_RADIUS_REQUIRED = 2
} wpa_psk_radius;
int wpa_pairwise;
+ int group_cipher; /* wpa_group value override from configuation */
int wpa_group;
int wpa_group_rekey;
int wpa_group_rekey_set;
@@ -342,7 +359,7 @@
/* IEEE 802.11r - Fast BSS Transition */
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r1_key_holder[FT_R1KH_ID_LEN];
- u32 r0_key_lifetime;
+ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
int rkh_pos_timeout;
int rkh_neg_timeout;
int rkh_pull_timeout; /* ms */
@@ -353,6 +370,7 @@
int pmk_r1_push;
int ft_over_ds;
int ft_psk_generate_local;
+ int r1_max_key_lifetime;
#endif /* CONFIG_IEEE80211R_AP */
char *ctrl_interface; /* directory for UNIX domain sockets */
@@ -479,6 +497,7 @@
int time_advertisement;
char *time_zone;
int wnm_sleep_mode;
+ int wnm_sleep_mode_no_keys;
int bss_transition;
/* IEEE 802.11u - Interworking */
@@ -501,6 +520,10 @@
unsigned int venue_name_count;
struct hostapd_lang_string *venue_name;
+ /* Venue URL duples */
+ unsigned int venue_url_count;
+ struct hostapd_venue_url *venue_url;
+
/* IEEE 802.11u - Network Authentication Type */
u8 *network_auth_type;
size_t network_auth_type_len;
@@ -566,9 +589,14 @@
struct hostapd_lang_string *service_desc;
} *hs20_osu_providers, *last_osu;
size_t hs20_osu_providers_count;
+ char **hs20_operator_icon;
+ size_t hs20_operator_icon_count;
unsigned int hs20_deauth_req_timeout;
char *subscr_remediation_url;
u8 subscr_remediation_method;
+ char *t_c_filename;
+ u32 t_c_timestamp;
+ char *t_c_server_url;
#endif /* CONFIG_HS20 */
u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
@@ -581,8 +609,10 @@
struct wpabuf *assocresp_elements;
unsigned int sae_anti_clogging_threshold;
+ unsigned int sae_sync;
+ int sae_require_mfp;
int *sae_groups;
- char *sae_password;
+ struct sae_password_entry *sae_passwords;
char *wowlan_triggers; /* Wake-on-WLAN triggers */
@@ -796,6 +826,11 @@
struct he_phy_capabilities_info he_phy_capab;
struct he_operation he_op;
#endif /* CONFIG_IEEE80211AX */
+
+ /* VHT enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_VHT_ENABLED BIT(0)
+#define CH_SWITCH_VHT_DISABLED BIT(1)
+ unsigned int ch_switch_vht_config;
};
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 8f4d839..728d7f0 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -185,6 +185,13 @@
}
#endif /* CONFIG_MBO */
+#ifdef CONFIG_OWE
+ pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+#endif /* CONFIG_OWE */
+
add_buf(&beacon, hapd->conf->vendor_elements);
add_buf(&proberesp, hapd->conf->vendor_elements);
add_buf(&assocresp, hapd->conf->assocresp_elements);
@@ -736,6 +743,15 @@
sta = ap_get_sta(hapd, dst);
if (!sta || !(sta->flags & WLAN_STA_ASSOC))
bssid = wildcard_bssid;
+ } else if (is_broadcast_ether_addr(dst) &&
+ len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+ /*
+ * The only current use case of Public Action frames with
+ * broadcast destination address is DPP PKEX. That case is
+ * directing all devices and not just the STAs within the BSS,
+ * so have to use the wildcard BSSID value.
+ */
+ bssid = wildcard_bssid;
}
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
hapd->own_addr, bssid, data, len, 0);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index bf8169d..db93fde 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -103,6 +103,14 @@
unsigned int freq,
unsigned int wait, const u8 *dst,
const u8 *data, size_t len);
+static inline void
+hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
+{
+ if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
+ !hapd->drv_priv)
+ return;
+ hapd->driver->send_action_cancel_wait(hapd->drv_priv);
+}
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
u16 auth_alg);
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index a20f49f..66d255c 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -77,12 +77,20 @@
goto out;
user->password_len = eap_user->password_len;
user->password_hash = eap_user->password_hash;
+ if (eap_user->salt && eap_user->salt_len) {
+ user->salt = os_memdup(eap_user->salt,
+ eap_user->salt_len);
+ if (!user->salt)
+ goto out;
+ user->salt_len = eap_user->salt_len;
+ }
}
user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth;
user->remediation = eap_user->remediation;
user->accept_attr = eap_user->accept_attr;
+ user->t_c_timestamp = eap_user->t_c_timestamp;
rv = 0;
out:
@@ -128,6 +136,7 @@
#ifdef CONFIG_HS20
srv.subscr_remediation_url = conf->subscr_remediation_url;
srv.subscr_remediation_method = conf->subscr_remediation_method;
+ srv.t_c_server_url = conf->t_c_server_url;
#endif /* CONFIG_HS20 */
srv.erp = conf->eap_server_erp;
srv.erp_domain = conf->erp_domain;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 3ea28a7..7d079d2 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -16,6 +16,7 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "p2p/p2p.h"
#include "hostapd.h"
@@ -30,6 +31,7 @@
#include "hs20.h"
#include "dfs.h"
#include "taxonomy.h"
+#include "ieee802_11_auth.h"
#ifdef NEED_AP_MLME
@@ -363,67 +365,6 @@
}
-#ifdef CONFIG_OWE
-static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
-{
- return hapd->conf->owe_transition_ssid_len > 0 &&
- !is_zero_ether_addr(hapd->conf->owe_transition_bssid);
-}
-#endif /* CONFIG_OWE */
-
-
-static size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
-{
-#ifdef CONFIG_OWE
- if (!hostapd_eid_owe_trans_enabled(hapd))
- return 0;
- return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
-#else /* CONFIG_OWE */
- return 0;
-#endif /* CONFIG_OWE */
-}
-
-
-static u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
- size_t len)
-{
-#ifdef CONFIG_OWE
- u8 *pos = eid;
- size_t elen;
-
- if (hapd->conf->owe_transition_ifname[0] &&
- !hostapd_eid_owe_trans_enabled(hapd))
- hostapd_owe_trans_get_info(hapd);
-
- if (!hostapd_eid_owe_trans_enabled(hapd))
- return pos;
-
- elen = hostapd_eid_owe_trans_len(hapd);
- if (len < elen) {
- wpa_printf(MSG_DEBUG,
- "OWE: Not enough room in the buffer for OWE IE");
- return pos;
- }
-
- *pos++ = WLAN_EID_VENDOR_SPECIFIC;
- *pos++ = elen - 2;
- WPA_PUT_BE24(pos, OUI_WFA);
- pos += 3;
- *pos++ = OWE_OUI_TYPE;
- os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
- pos += ETH_ALEN;
- *pos++ = hapd->conf->owe_transition_ssid_len;
- os_memcpy(pos, hapd->conf->owe_transition_ssid,
- hapd->conf->owe_transition_ssid_len);
- pos += hapd->conf->owe_transition_ssid_len;
-
- return pos;
-#else /* CONFIG_OWE */
- return eid;
-#endif /* CONFIG_OWE */
-}
-
-
static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
const struct ieee80211_mgmt *req,
int is_p2p, size_t *resp_len)
@@ -791,6 +732,11 @@
int ret;
u16 csa_offs[2];
size_t csa_offs_len;
+ u32 session_timeout, acct_interim_interval;
+ struct vlan_description vlan_id;
+ struct hostapd_sta_wpa_psk_short *psk = NULL;
+ char *identity = NULL;
+ char *radius_cui = NULL;
if (len < IEEE80211_HDRLEN)
return;
@@ -799,6 +745,17 @@
sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
ie_len = len - IEEE80211_HDRLEN;
+ ret = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
+ &session_timeout,
+ &acct_interim_interval, &vlan_id,
+ &psk, &identity, &radius_cui, 1);
+ if (ret == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Probe Request frame from " MACSTR
+ " due to ACL reject ", MAC2STR(mgmt->sa));
+ return;
+ }
+
for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
mgmt->sa, mgmt->da, mgmt->bssid,
@@ -993,6 +950,9 @@
}
#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
+ " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
+
resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
&resp_len);
if (resp == NULL)
diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
index fb63942..725d3cd 100644
--- a/src/ap/bss_load.c
+++ b/src/ap/bss_load.c
@@ -16,11 +16,35 @@
#include "beacon.h"
+static int get_bss_load_update_timeout(struct hostapd_data *hapd,
+ unsigned int *sec, unsigned int *usec)
+{
+ unsigned int update_period = hapd->conf->bss_load_update_period;
+ unsigned int beacon_int = hapd->iconf->beacon_int;
+ unsigned int update_timeout;
+
+ if (!update_period || !beacon_int) {
+ wpa_printf(MSG_ERROR,
+ "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
+ update_period, beacon_int);
+ return -1;
+ }
+
+ update_timeout = update_period * beacon_int;
+
+ *sec = ((update_timeout / 1000) * 1024) / 1000;
+ *usec = (update_timeout % 1000) * 1024;
+
+ return 0;
+}
+
+
static void update_channel_utilization(void *eloop_data, void *user_data)
{
struct hostapd_data *hapd = eloop_data;
unsigned int sec, usec;
int err;
+ struct hostapd_iface *iface = hapd->iface;
if (!(hapd->beacon_set_done && hapd->started))
return;
@@ -33,8 +57,24 @@
ieee802_11_set_beacon(hapd);
- sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
- usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
+ return;
+
+ if (hapd->conf->chan_util_avg_period) {
+ iface->chan_util_samples_sum += iface->channel_utilization;
+ iface->chan_util_num_sample_periods +=
+ hapd->conf->bss_load_update_period;
+ if (iface->chan_util_num_sample_periods >=
+ hapd->conf->chan_util_avg_period) {
+ iface->chan_util_average =
+ iface->chan_util_samples_sum /
+ (iface->chan_util_num_sample_periods /
+ hapd->conf->bss_load_update_period);
+ iface->chan_util_samples_sum = 0;
+ iface->chan_util_num_sample_periods = 0;
+ }
+ }
+
eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
NULL);
}
@@ -42,17 +82,11 @@
int bss_load_update_init(struct hostapd_data *hapd)
{
- struct hostapd_bss_config *conf = hapd->conf;
- struct hostapd_config *iconf = hapd->iconf;
unsigned int sec, usec;
- if (!conf->bss_load_update_period || !iconf->beacon_int)
+ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
return -1;
- hapd->bss_load_update_timeout = conf->bss_load_update_period *
- iconf->beacon_int;
- sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
- usec = (hapd->bss_load_update_timeout % 1000) * 1024;
eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
NULL);
return 0;
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 1a2b4e5..21b813e 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -26,6 +26,30 @@
#include "taxonomy.h"
+static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+ size_t curr_len, const u8 *mcs_set)
+{
+ int ret;
+ size_t len = curr_len;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "ht_mcs_bitmask=");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* 77 first bits (+ 3 reserved bits) */
+ len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
+
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (os_snprintf_error(buflen - len, ret))
+ return curr_len;
+ len += ret;
+
+ return len;
+}
+
+
static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
struct sta_info *sta,
char *buf, size_t buflen)
@@ -111,6 +135,31 @@
if (!os_snprintf_error(buflen - len, ret))
len += ret;
+ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "rx_vht_mcs_map=%04x\n"
+ "tx_vht_mcs_map=%04x\n",
+ le_to_host16(sta->vht_capabilities->
+ vht_supported_mcs_set.rx_map),
+ le_to_host16(sta->vht_capabilities->
+ vht_supported_mcs_set.tx_map));
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ sta->ht_capabilities->
+ supported_mcs_set);
+ }
+
+ if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "last_ack_signal=%d\n", data.last_ack_rssi);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
return len;
}
@@ -245,6 +294,53 @@
len += os_snprintf(buf + len, buflen - len, "\n");
}
+ if (sta->power_capab) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "min_txpower=%d\n"
+ "max_txpower=%d\n",
+ sta->min_tx_power, sta->max_tx_power);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+#ifdef CONFIG_IEEE80211AC
+ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+ res = os_snprintf(buf + len, buflen - len,
+ "vht_caps_info=0x%08x\n",
+ le_to_host32(sta->vht_capabilities->
+ vht_capabilities_info));
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211N
+ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+ res = os_snprintf(buf + len, buflen - len,
+ "ht_caps_info=0x%04x\n",
+ le_to_host16(sta->ht_capabilities->
+ ht_capabilities_info));
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211N */
+
+ if (sta->ext_capability &&
+ buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
+ len += os_snprintf(buf + len, buflen - len, "ext_capab=");
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ sta->ext_capability + 1,
+ sta->ext_capability[0]);
+ len += os_snprintf(buf + len, buflen - len, "\n");
+ }
+
+ if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "wds_sta_ifname=%s\n", sta->ifname_wds);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
return len;
}
@@ -546,7 +642,8 @@
size_t buflen)
{
struct hostapd_iface *iface = hapd->iface;
- int len = 0, ret;
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ int len = 0, ret, j;
size_t i;
ret = os_snprintf(buf + len, buflen - len,
@@ -606,13 +703,17 @@
"channel=%u\n"
"secondary_channel=%d\n"
"ieee80211n=%d\n"
- "ieee80211ac=%d\n",
+ "ieee80211ac=%d\n"
+ "beacon_int=%u\n"
+ "dtim_period=%d\n",
iface->conf->channel,
iface->conf->ieee80211n && !hapd->conf->disable_11n ?
iface->conf->secondary_channel : 0,
iface->conf->ieee80211n && !hapd->conf->disable_11n,
iface->conf->ieee80211ac &&
- !hapd->conf->disable_11ac);
+ !hapd->conf->disable_11ac,
+ iface->conf->beacon_int,
+ hapd->conf->dtim_period);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -620,15 +721,76 @@
ret = os_snprintf(buf + len, buflen - len,
"vht_oper_chwidth=%d\n"
"vht_oper_centr_freq_seg0_idx=%d\n"
- "vht_oper_centr_freq_seg1_idx=%d\n",
+ "vht_oper_centr_freq_seg1_idx=%d\n"
+ "vht_caps_info=%08x\n",
iface->conf->vht_oper_chwidth,
iface->conf->vht_oper_centr_freq_seg0_idx,
- iface->conf->vht_oper_centr_freq_seg1_idx);
+ iface->conf->vht_oper_centr_freq_seg1_idx,
+ iface->conf->vht_capab);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
+ if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
+ u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
+ u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "rx_vht_mcs_map=%04x\n"
+ "tx_vht_mcs_map=%04x\n",
+ rxmap, txmap);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "ht_caps_info=%04x\n",
+ hapd->iconf->ht_capab);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ mode->mcs_set);
+ }
+
+ if (iface->current_rates && iface->num_rates) {
+ ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ for (j = 0; j < iface->num_rates; j++) {
+ ret = os_snprintf(buf + len, buflen - len, "%s%02x",
+ j > 0 ? " " : "",
+ iface->current_rates[j].rate / 5);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ for (j = 0; mode && j < mode->num_channels; j++) {
+ if (mode->channels[j].freq == iface->freq) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "max_txpower=%u\n",
+ mode->channels[j].max_tx_power);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ break;
+ }
+ }
+
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *bss = iface->bss[i];
ret = os_snprintf(buf + len, buflen - len,
@@ -647,6 +809,15 @@
len += ret;
}
+ if (hapd->conf->chan_util_avg_period) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "chan_util_avg=%u\n",
+ iface->chan_util_average);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
return len;
}
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 5a0d781..993dd19 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1111,7 +1111,8 @@
return 1;
}
- if (ieee80211_is_dfs(iface->freq)) {
+ if (ieee80211_is_dfs(iface->freq, iface->hw_features,
+ iface->num_hw_features)) {
wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
__func__, iface->freq);
return 0;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index aae2910..4d2b71a 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -20,7 +20,10 @@
#include "dpp_hostapd.h"
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -30,7 +33,7 @@
{
struct dpp_configurator *conf;
- dl_list_for_each(conf, &hapd->dpp_configurator,
+ dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator,
struct dpp_configurator, list) {
if (conf->id == id)
return conf;
@@ -44,8 +47,8 @@
struct dpp_bootstrap_info *bi;
unsigned int max_id = 0;
- dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
- list) {
+ dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
+ struct dpp_bootstrap_info, list) {
if (bi->id > max_id)
max_id = bi->id;
}
@@ -69,12 +72,16 @@
return -1;
bi->id = hapd_dpp_next_id(hapd);
- dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+ dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
if (auth && auth->response_pending &&
dpp_notify_new_qr_code(auth, bi) == 1) {
wpa_printf(MSG_DEBUG,
"DPP: Sending out pending authentication response");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
hostapd_drv_send_action(hapd, auth->curr_freq, 0,
auth->peer_mac_addr,
wpabuf_head(hapd->dpp_auth->resp_msg),
@@ -175,7 +182,7 @@
info ? "I:" : "", info ? info : "", info ? ";" : "",
pk);
bi->id = hapd_dpp_next_id(hapd);
- dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+ dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
ret = bi->id;
bi = NULL;
fail:
@@ -196,8 +203,8 @@
{
struct dpp_bootstrap_info *bi;
- dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
- list) {
+ dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
+ struct dpp_bootstrap_info, list) {
if (bi->id == id)
return bi;
}
@@ -205,12 +212,12 @@
}
-static int dpp_bootstrap_del(struct hostapd_data *hapd, unsigned int id)
+static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id)
{
struct dpp_bootstrap_info *bi, *tmp;
int found = 0;
- dl_list_for_each_safe(bi, tmp, &hapd->dpp_bootstrap,
+ dl_list_for_each_safe(bi, tmp, &ifaces->dpp_bootstrap,
struct dpp_bootstrap_info, list) {
if (id && bi->id != id)
continue;
@@ -237,7 +244,7 @@
return -1;
}
- return dpp_bootstrap_del(hapd, id_val);
+ return dpp_bootstrap_del(hapd->iface->interfaces, id_val);
}
@@ -274,11 +281,72 @@
}
+static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->resp_msg)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Retry Authentication Response after timeout");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr,
+ wpabuf_head(auth->resp_msg),
+ wpabuf_len(auth->resp_msg));
+}
+
+
+static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ unsigned int wait_time, max_tries;
+
+ if (!auth || !auth->resp_msg)
+ return;
+
+ if (hapd->dpp_resp_max_tries)
+ max_tries = hapd->dpp_resp_max_tries;
+ else
+ max_tries = 5;
+ auth->auth_resp_tries++;
+ if (auth->auth_resp_tries >= max_tries) {
+ wpa_printf(MSG_INFO,
+ "DPP: No confirm received from initiator - stopping exchange");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+
+ if (hapd->dpp_resp_retry_time)
+ wait_time = hapd->dpp_resp_retry_time;
+ else
+ wait_time = 1000;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Schedule retransmission of Authentication Response frame in %u ms",
+ wait_time);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+ eloop_register_timeout(wait_time / 1000,
+ (wait_time % 1000) * 1000,
+ hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+}
+
+
void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
const u8 *data, size_t data_len, int ok)
{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
MAC2STR(dst), ok);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+ " result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED");
if (!hapd->dpp_auth) {
wpa_printf(MSG_DEBUG,
@@ -289,6 +357,12 @@
if (hapd->dpp_auth->remove_on_tx_status) {
wpa_printf(MSG_DEBUG,
"DPP: Terminate authentication exchange due to an earlier error");
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+ NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return;
@@ -296,6 +370,120 @@
if (hapd->dpp_auth_ok_on_ack)
hostapd_dpp_auth_success(hapd, 1);
+
+ if (!is_broadcast_ether_addr(dst) && !ok) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unicast DPP Action frame was not ACKed");
+ if (auth->waiting_auth_resp) {
+ /* In case of DPP Authentication Request frame, move to
+ * the next channel immediately. */
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_auth_init_next(hapd);
+ return;
+ }
+ if (auth->waiting_auth_conf) {
+ hostapd_dpp_auth_resp_retry(hapd);
+ return;
+ }
+ }
+
+ if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) {
+ /* Allow timeout handling to stop iteration if no response is
+ * received from a peer that has ACKed a request. */
+ auth->auth_req_ack = 1;
+ }
+
+ if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 &&
+ hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
+ hapd->dpp_auth->curr_freq,
+ hapd->dpp_auth->neg_freq);
+ hostapd_drv_send_action_cancel_wait(hapd);
+
+ if (hapd->dpp_auth->neg_freq !=
+ (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ hapd->dpp_auth->neg_freq, hapd->iface->freq);
+ }
+ }
+
+ if (hapd->dpp_auth_ok_on_ack)
+ hapd->dpp_auth_ok_on_ack = 0;
+}
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ unsigned int freq;
+ struct os_reltime now, diff;
+ unsigned int wait_time, diff_ms;
+
+ if (!auth || !auth->waiting_auth_resp)
+ return;
+
+ wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &hapd->dpp_last_init, &diff);
+ diff_ms = diff.sec * 1000 + diff.usec / 1000;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Reply wait timeout - wait_time=%u diff_ms=%u",
+ wait_time, diff_ms);
+
+ if (auth->auth_req_ack && diff_ms >= wait_time) {
+ /* Peer ACK'ed Authentication Request frame, but did not reply
+ * with Authentication Response frame within two seconds. */
+ wpa_printf(MSG_INFO,
+ "DPP: No response received from responder - stopping initiation attempt");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+
+ if (diff_ms >= wait_time) {
+ /* Authentication Request frame was not ACK'ed and no reply
+ * was receiving within two seconds. */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue Initiator channel iteration");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ hostapd_dpp_auth_init_next(hapd);
+ return;
+ }
+
+ /* Driver did not support 2000 ms long wait_time with TX command, so
+ * schedule listen operation to continue waiting for the response.
+ *
+ * DPP listen operations continue until stopped, so simply schedule a
+ * new call to this function at the point when the two second reply
+ * wait has expired. */
+ wait_time -= diff_ms;
+
+ freq = auth->curr_freq;
+ if (auth->neg_freq > 0)
+ freq = auth->neg_freq;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue reply wait on channel %u MHz for %u ms",
+ freq, wait_time);
+ hapd->dpp_in_response_listen = 1;
+
+ if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ freq, hapd->iface->freq);
+ }
+
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reply_wait_timeout, hapd, NULL);
}
@@ -371,8 +559,15 @@
goto fail;
os_memcpy(conf_sta->ssid, ssid, ssid_len);
conf_sta->ssid_len = ssid_len;
- if (os_strstr(cmd, " conf=sta-psk")) {
- conf_sta->dpp = 0;
+ if (os_strstr(cmd, " conf=sta-psk") ||
+ os_strstr(cmd, " conf=sta-sae") ||
+ os_strstr(cmd, " conf=sta-psk-sae")) {
+ if (os_strstr(cmd, " conf=sta-psk-sae"))
+ conf_sta->akm = DPP_AKM_PSK_SAE;
+ else if (os_strstr(cmd, " conf=sta-sae"))
+ conf_sta->akm = DPP_AKM_SAE;
+ else
+ conf_sta->akm = DPP_AKM_PSK;
if (psk_set) {
os_memcpy(conf_sta->psk, psk, PMK_LEN);
} else {
@@ -381,7 +576,7 @@
goto fail;
}
} else if (os_strstr(cmd, " conf=sta-dpp")) {
- conf_sta->dpp = 1;
+ conf_sta->akm = DPP_AKM_DPP;
} else {
goto fail;
}
@@ -393,8 +588,15 @@
goto fail;
os_memcpy(conf_ap->ssid, ssid, ssid_len);
conf_ap->ssid_len = ssid_len;
- if (os_strstr(cmd, " conf=ap-psk")) {
- conf_ap->dpp = 0;
+ if (os_strstr(cmd, " conf=ap-psk") ||
+ os_strstr(cmd, " conf=ap-sae") ||
+ os_strstr(cmd, " conf=ap-psk-sae")) {
+ if (os_strstr(cmd, " conf=ap-psk-sae"))
+ conf_ap->akm = DPP_AKM_PSK_SAE;
+ else if (os_strstr(cmd, " conf=ap-sae"))
+ conf_ap->akm = DPP_AKM_SAE;
+ else
+ conf_ap->akm = DPP_AKM_PSK;
if (psk_set) {
os_memcpy(conf_ap->psk, psk, PMK_LEN);
} else {
@@ -403,7 +605,7 @@
goto fail;
}
} else if (os_strstr(cmd, " conf=ap-dpp")) {
- conf_ap->dpp = 1;
+ conf_ap->akm = DPP_AKM_DPP;
} else {
goto fail;
}
@@ -446,14 +648,110 @@
}
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ if (!hapd->dpp_auth)
+ return;
+ wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
+ hostapd_dpp_auth_init_next(hapd);
+}
+
+
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ const u8 *dst;
+ unsigned int wait_time, max_wait_time, freq, max_tries, used;
+ struct os_reltime now, diff;
+
+ if (!auth)
+ return -1;
+
+ if (auth->freq_idx == 0)
+ os_get_reltime(&hapd->dpp_init_iter_start);
+
+ if (auth->freq_idx >= auth->num_freq) {
+ auth->num_freq_iters++;
+ if (hapd->dpp_init_max_tries)
+ max_tries = hapd->dpp_init_max_tries;
+ else
+ max_tries = 5;
+ if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) {
+ wpa_printf(MSG_INFO,
+ "DPP: No response received from responder - stopping initiation attempt");
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_AUTH_INIT_FAILED);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return -1;
+ }
+ auth->freq_idx = 0;
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ if (hapd->dpp_init_retry_time)
+ wait_time = hapd->dpp_init_retry_time;
+ else
+ wait_time = 10000;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff);
+ used = diff.sec * 1000 + diff.usec / 1000;
+ if (used > wait_time)
+ wait_time = 0;
+ else
+ wait_time -= used;
+ wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms",
+ wait_time);
+ eloop_register_timeout(wait_time / 1000,
+ (wait_time % 1000) * 1000,
+ hostapd_dpp_init_timeout, hapd,
+ NULL);
+ return 0;
+ }
+ freq = auth->freq[auth->freq_idx++];
+ auth->curr_freq = freq;
+
+ if (is_zero_ether_addr(auth->peer_bi->mac_addr))
+ dst = broadcast;
+ else
+ dst = auth->peer_bi->mac_addr;
+ hapd->dpp_auth_ok_on_ack = 0;
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+ max_wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ if (wait_time > max_wait_time)
+ wait_time = max_wait_time;
+ wait_time += 10; /* give the driver some extra time to complete */
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ wait_time -= 10;
+ if (auth->neg_freq > 0 && freq != auth->neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
+ freq, auth->neg_freq);
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
+ auth->auth_req_ack = 0;
+ os_get_reltime(&hapd->dpp_last_init);
+ return hostapd_drv_send_action(hapd, freq, wait_time,
+ dst,
+ wpabuf_head(hapd->dpp_auth->req_msg),
+ wpabuf_len(hapd->dpp_auth->req_msg));
+}
+
+
int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
{
const char *pos;
struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
- const u8 *dst;
- int res;
- int configurator = 1;
- struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+ u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ unsigned int neg_freq = 0;
pos = os_strstr(cmd, " peer=");
if (!pos)
@@ -488,77 +786,102 @@
if (pos) {
pos += 6;
if (os_strncmp(pos, "configurator", 12) == 0)
- configurator = 1;
+ allowed_roles = DPP_CAPAB_CONFIGURATOR;
else if (os_strncmp(pos, "enrollee", 8) == 0)
- configurator = 0;
+ allowed_roles = DPP_CAPAB_ENROLLEE;
+ else if (os_strncmp(pos, "either", 6) == 0)
+ allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
else
goto fail;
}
- if (hapd->dpp_auth)
+ pos = os_strstr(cmd, " neg_freq=");
+ if (pos)
+ neg_freq = atoi(pos + 10);
+
+ if (hapd->dpp_auth) {
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+ NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
- hapd->dpp_auth = dpp_auth_init(hapd, peer_bi, own_bi, configurator);
+ }
+
+ hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi,
+ allowed_roles, neg_freq,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
if (!hapd->dpp_auth)
goto fail;
hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd);
- /* TODO: Support iteration over all frequencies and filtering of
- * frequencies based on locally enabled channels that allow initiation
- * of transmission. */
- if (peer_bi->num_freq > 0)
- hapd->dpp_auth->curr_freq = peer_bi->freq[0];
- else
- hapd->dpp_auth->curr_freq = 2412;
+ hapd->dpp_auth->neg_freq = neg_freq;
- if (is_zero_ether_addr(peer_bi->mac_addr)) {
- dst = broadcast;
- } else {
- dst = peer_bi->mac_addr;
+ if (!is_zero_ether_addr(peer_bi->mac_addr))
os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
ETH_ALEN);
- }
- hapd->dpp_auth_ok_on_ack = 0;
- res = hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
- dst, wpabuf_head(hapd->dpp_auth->req_msg),
- wpabuf_len(hapd->dpp_auth->req_msg));
-
- return res;
+ return hostapd_dpp_auth_init_next(hapd);
fail:
- dpp_configuration_free(conf_sta);
- dpp_configuration_free(conf_ap);
return -1;
}
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd)
+{
+ int freq;
+
+ freq = atoi(cmd);
+ if (freq <= 0)
+ return -1;
+
+ if (os_strstr(cmd, " role=configurator"))
+ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strstr(cmd, " role=enrollee"))
+ hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+ else
+ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+
+ if (freq != hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ freq, hapd->iface->freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd)
+{
+ /* TODO: Stop listen operation on non-operating channel */
+}
+
+
static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
- const u8 *r_bootstrap, *i_bootstrap, *wrapped_data;
- u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len;
+ const u8 *r_bootstrap, *i_bootstrap;
+ u16 r_bootstrap_len, i_bootstrap_len;
struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
MAC2STR(src));
- wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing required Wrapped data attribute");
- return;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data",
- wrapped_data, wrapped_data_len);
-
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap > wrapped_data ||
- r_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
@@ -566,10 +889,9 @@
i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
- if (!i_bootstrap || i_bootstrap > wrapped_data ||
- i_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+ if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
@@ -577,8 +899,8 @@
/* Try to find own and peer bootstrapping key matches based on the
* received hash values */
- dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
- list) {
+ dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
+ struct dpp_bootstrap_info, list) {
if (!own_bi && bi->own &&
os_memcmp(bi->pubkey_hash, r_bootstrap,
SHA256_MAC_LEN) == 0) {
@@ -600,22 +922,21 @@
}
if (!own_bi) {
- wpa_printf(MSG_DEBUG,
- "DPP: No matching own bootstrapping key found - ignore message");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "No matching own bootstrapping key found - ignore message");
return;
}
if (hapd->dpp_auth) {
- wpa_printf(MSG_DEBUG,
- "DPP: Already in DPP authentication exchange - ignore new one");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Already in DPP authentication exchange - ignore new one");
return;
}
hapd->dpp_auth_ok_on_ack = 0;
hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles,
hapd->dpp_qr_mutual,
- peer_bi, own_bi, freq, hdr, buf,
- wrapped_data, wrapped_data_len);
+ peer_bi, own_bi, freq, hdr, buf, len);
if (!hapd->dpp_auth) {
wpa_printf(MSG_DEBUG, "DPP: No response generated");
return;
@@ -625,55 +946,22 @@
hapd->dpp_configurator_params);
os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), hapd->dpp_auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
src, wpabuf_head(hapd->dpp_auth->resp_msg),
wpabuf_len(hapd->dpp_auth->resp_msg));
}
-static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
- enum gas_query_ap_result result,
- const struct wpabuf *adv_proto,
- const struct wpabuf *resp, u16 status_code)
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+ struct dpp_authentication *auth)
{
- struct hostapd_data *hapd = ctx;
- const u8 *pos;
- struct dpp_authentication *auth = hapd->dpp_auth;
-
- if (!auth || !auth->auth_success) {
- wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
- return;
- }
- if (!resp || status_code != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
- goto fail;
- }
-
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
- adv_proto);
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
- resp);
-
- if (wpabuf_len(adv_proto) != 10 ||
- !(pos = wpabuf_head(adv_proto)) ||
- pos[0] != WLAN_EID_ADV_PROTO ||
- pos[1] != 8 ||
- pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
- pos[4] != 5 ||
- WPA_GET_BE24(&pos[5]) != OUI_WFA ||
- pos[8] != 0x1a ||
- pos[9] != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Not a DPP Advertisement Protocol ID");
- goto fail;
- }
-
- if (dpp_conf_resp_rx(auth, resp) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
- goto fail;
- }
-
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
+ dpp_akm_str(auth->akm));
if (auth->ssid_len)
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
wpa_ssid_txt(auth->ssid, auth->ssid_len));
@@ -736,6 +1024,52 @@
os_free(hex);
}
}
+}
+
+
+static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct hostapd_data *hapd = ctx;
+ const u8 *pos;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->auth_success) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return;
+ }
+ if (!resp || status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+ goto fail;
+ }
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+ adv_proto);
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+ resp);
+
+ if (wpabuf_len(adv_proto) != 10 ||
+ !(pos = wpabuf_head(adv_proto)) ||
+ pos[0] != WLAN_EID_ADV_PROTO ||
+ pos[1] != 8 ||
+ pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[4] != 5 ||
+ WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+ pos[8] != 0x1a ||
+ pos[9] != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Not a DPP Advertisement Protocol ID");
+ goto fail;
+ }
+
+ if (dpp_conf_resp_rx(auth, resp) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+ goto fail;
+ }
+
+ hostapd_dpp_handle_config_obj(hapd, auth);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return;
@@ -811,6 +1145,17 @@
wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ if (hapd->dpp_auth->configurator) {
+ /* Prevent GAS response */
+ hapd->dpp_auth->auth_success = 0;
+ }
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
if (!hapd->dpp_auth->configurator)
hostapd_dpp_start_gas_client(hapd);
@@ -818,7 +1163,8 @@
static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
- const u8 *hdr, const u8 *buf, size_t len)
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
{
struct dpp_authentication *auth = hapd->dpp_auth;
struct wpabuf *msg;
@@ -839,6 +1185,15 @@
return;
}
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+
+ if (auth->curr_freq != freq && auth->neg_freq == freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder accepted request for different negotiation channel");
+ auth->curr_freq = freq;
+ }
+
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
msg = dpp_auth_resp_rx(auth, hdr, buf, len);
if (!msg) {
if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
@@ -850,6 +1205,9 @@
}
os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_CONF);
hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
@@ -886,6 +1244,98 @@
}
+static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
+ const u8 *src, unsigned int freq,
+ u8 trans_id,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+
+ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
+ 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector));
+ if (!msg)
+ return;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID");
+ goto skip_trans_id;
+ }
+ if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID");
+ trans_id ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, trans_id);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_trans_id:
+ if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ }
+ if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
+ goto skip_connector;
+ }
+ if (status == DPP_STATUS_OK &&
+ dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) {
+ char *connector;
+
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector");
+ connector = dpp_corrupt_connector_signature(
+ hapd->conf->dpp_connector);
+ if (!connector) {
+ wpabuf_free(msg);
+ return;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(connector));
+ wpabuf_put_str(msg, connector);
+ os_free(connector);
+ goto skip_connector;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Connector */
+ if (status == DPP_STATUS_OK) {
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+ wpabuf_put_str(msg, hapd->conf->dpp_connector);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_connector:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
+ " status=%d", MAC2STR(src), status);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d status=%d", MAC2STR(src), freq,
+ DPP_PA_PEER_DISCOVERY_RESP, status);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+}
+
+
static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
const u8 *src,
const u8 *buf, size_t len,
@@ -897,7 +1347,7 @@
struct dpp_introduction intro;
os_time_t expire;
int expiration;
- struct wpabuf *msg;
+ enum dpp_status_error res;
wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
MAC2STR(src));
@@ -917,7 +1367,7 @@
os_get_time(&now);
if (hapd->conf->dpp_netaccesskey_expiry &&
- hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+ (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
return;
}
@@ -937,18 +1387,28 @@
return;
}
- if (dpp_peer_intro(&intro, hapd->conf->dpp_connector,
- wpabuf_head(hapd->conf->dpp_netaccesskey),
- wpabuf_len(hapd->conf->dpp_netaccesskey),
- wpabuf_head(hapd->conf->dpp_csign),
- wpabuf_len(hapd->conf->dpp_csign),
- connector, connector_len, &expire) < 0) {
+ res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey),
+ wpabuf_head(hapd->conf->dpp_csign),
+ wpabuf_len(hapd->conf->dpp_csign),
+ connector, connector_len, &expire);
+ if (res == 255) {
wpa_printf(MSG_INFO,
- "DPP: Network Introduction protocol resulted in failure");
+ "DPP: Network Introduction protocol resulted in internal failure (peer "
+ MACSTR ")", MAC2STR(src));
+ return;
+ }
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in failure (peer "
+ MACSTR " status %d)", MAC2STR(src), res);
+ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+ res);
return;
}
- if (!expire || hapd->conf->dpp_netaccesskey_expiry < expire)
+ if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
expire = hapd->conf->dpp_netaccesskey_expiry;
if (expire)
expiration = expire - now.sec;
@@ -962,26 +1422,8 @@
return;
}
- msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
- 5 + 4 + os_strlen(hapd->conf->dpp_connector));
- if (!msg)
- return;
-
- /* Transaction ID */
- wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, trans_id[0]);
-
- /* DPP Connector */
- wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
- wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
- wpabuf_put_str(msg, hapd->conf->dpp_connector);
-
- wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR,
- MAC2STR(src));
- hostapd_drv_send_action(hapd, freq, 0, src,
- wpabuf_head(msg), wpabuf_len(msg));
- wpabuf_free(msg);
+ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+ DPP_STATUS_OK);
}
@@ -1011,7 +1453,8 @@
return;
}
- hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->dpp_pkex_bi,
+ hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx,
+ hapd->dpp_pkex_bi,
hapd->own_addr, src,
hapd->dpp_pkex_identifier,
hapd->dpp_pkex_code,
@@ -1023,8 +1466,19 @@
}
msg = hapd->dpp_pkex->exchange_resp;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_EXCHANGE_RESP);
hostapd_drv_send_action(hapd, freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
+ if (hapd->dpp_pkex->failed) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate PKEX exchange due to an earlier error");
+ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+ hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ }
}
@@ -1046,8 +1500,7 @@
return;
}
- os_memcpy(hapd->dpp_pkex->peer_mac, src, ETH_ALEN);
- msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, buf, len);
+ msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len);
if (!msg) {
wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
return;
@@ -1056,6 +1509,9 @@
wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
MAC2STR(src));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_COMMIT_REVEAL_REQ);
hostapd_drv_send_action(hapd, freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
@@ -1082,12 +1538,23 @@
msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
if (!msg) {
wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+ if (hapd->dpp_pkex->failed) {
+ wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange");
+ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+ hapd->dpp_pkex->own_bi->pkex_t =
+ hapd->dpp_pkex->t;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ }
return;
}
wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
MACSTR, MAC2STR(src));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_COMMIT_REVEAL_RESP);
hostapd_drv_send_action(hapd, freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
@@ -1109,7 +1576,7 @@
dpp_bootstrap_info_free(bi);
return;
}
- dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+ dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
}
@@ -1156,7 +1623,7 @@
dpp_bootstrap_info_free(bi);
return;
}
- dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+ dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
bi->id,
@@ -1178,6 +1645,7 @@
u8 crypto_suite;
enum dpp_public_action_frame_type type;
const u8 *hdr;
+ unsigned int pkex_t;
if (len < DPP_HDR_LEN)
return;
@@ -1197,18 +1665,27 @@
if (crypto_suite != 1) {
wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
crypto_suite);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d ignore=unsupported-crypto-suite",
+ MAC2STR(src), freq, type);
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
- if (dpp_check_attrs(buf, len) < 0)
+ if (dpp_check_attrs(buf, len) < 0) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d ignore=invalid-attributes",
+ MAC2STR(src), freq, type);
return;
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq, type);
switch (type) {
case DPP_PA_AUTHENTICATION_REQ:
hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
break;
case DPP_PA_AUTHENTICATION_RESP:
- hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len);
+ hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq);
break;
case DPP_PA_AUTHENTICATION_CONF:
hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
@@ -1235,6 +1712,17 @@
"DPP: Ignored unsupported frame subtype %d", type);
break;
}
+
+ if (hapd->dpp_pkex)
+ pkex_t = hapd->dpp_pkex->t;
+ else if (hapd->dpp_pkex_bi)
+ pkex_t = hapd->dpp_pkex_bi->pkex_t;
+ else
+ pkex_t = 0;
+ if (pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0");
+ hostapd_dpp_pkex_remove(hapd, "*");
+ }
}
@@ -1254,6 +1742,8 @@
wpa_hexdump(MSG_DEBUG,
"DPP: Received Configuration Request (GAS Query Request)",
query, query_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
+ MAC2STR(sa));
resp = dpp_conf_req_rx(auth, query, query_len);
if (!resp)
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
@@ -1261,12 +1751,30 @@
}
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
+{
+ if (!hapd->dpp_auth)
+ return;
+
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
+
+ if (ok)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+}
+
+
static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd)
{
struct dpp_configurator *conf;
unsigned int max_id = 0;
- dl_list_for_each(conf, &hapd->dpp_configurator,
+ dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator,
struct dpp_configurator, list) {
if (conf->id > max_id)
max_id = conf->id;
@@ -1300,7 +1808,7 @@
goto fail;
conf->id = hostapd_dpp_next_configurator_id(hapd);
- dl_list_add(&hapd->dpp_configurator, &conf->list);
+ dl_list_add(&hapd->iface->interfaces->dpp_configurator, &conf->list);
ret = conf->id;
conf = NULL;
fail:
@@ -1312,12 +1820,12 @@
}
-static int dpp_configurator_del(struct hostapd_data *hapd, unsigned int id)
+static int dpp_configurator_del(struct hapd_interfaces *ifaces, unsigned int id)
{
struct dpp_configurator *conf, *tmp;
int found = 0;
- dl_list_for_each_safe(conf, tmp, &hapd->dpp_configurator,
+ dl_list_for_each_safe(conf, tmp, &ifaces->dpp_configurator,
struct dpp_configurator, list) {
if (id && conf->id != id)
continue;
@@ -1344,7 +1852,45 @@
return -1;
}
- return dpp_configurator_del(hapd, id_val);
+ return dpp_configurator_del(hapd->iface->interfaces, id_val);
+}
+
+
+int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_authentication *auth;
+ int ret = -1;
+ char *curve = NULL;
+
+ auth = os_zalloc(sizeof(*auth));
+ if (!auth)
+ return -1;
+
+ curve = get_param(cmd, " curve=");
+ hostapd_dpp_set_configurator(hapd, auth, cmd);
+
+ if (dpp_configurator_own_config(auth, curve, 1) == 0) {
+ hostapd_dpp_handle_config_obj(hapd, auth);
+ ret = 0;
+ }
+
+ dpp_auth_deinit(auth);
+ os_free(curve);
+
+ return ret;
+}
+
+
+int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
+ char *buf, size_t buflen)
+{
+ struct dpp_configurator *conf;
+
+ conf = hostapd_dpp_configurator_get_id(hapd, id);
+ if (!conf)
+ return -1;
+
+ return dpp_configurator_get_key(conf, buf, buflen);
}
@@ -1369,6 +1915,7 @@
return -1;
}
hapd->dpp_pkex_bi = own_bi;
+ own_bi->pkex_t = 0; /* clear pending errors on new code */
os_free(hapd->dpp_pkex_identifier);
hapd->dpp_pkex_identifier = NULL;
@@ -1398,7 +1945,8 @@
wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
dpp_pkex_free(hapd->dpp_pkex);
- hapd->dpp_pkex = dpp_pkex_init(own_bi, hapd->own_addr,
+ hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi,
+ hapd->own_addr,
hapd->dpp_pkex_identifier,
hapd->dpp_pkex_code);
if (!hapd->dpp_pkex)
@@ -1406,6 +1954,9 @@
msg = hapd->dpp_pkex->exchange_req;
/* TODO: Which channel to use? */
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(broadcast), 2437,
+ DPP_PA_PKEX_EXCHANGE_REQ);
hostapd_drv_send_action(hapd, 2437, 0, broadcast,
wpabuf_head(msg), wpabuf_len(msg));
}
@@ -1449,10 +2000,17 @@
}
+void hostapd_dpp_stop(struct hostapd_data *hapd)
+{
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+}
+
+
int hostapd_dpp_init(struct hostapd_data *hapd)
{
- dl_list_init(&hapd->dpp_bootstrap);
- dl_list_init(&hapd->dpp_configurator);
hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
hapd->dpp_init_done = 1;
return 0;
@@ -1472,8 +2030,9 @@
#endif /* CONFIG_TESTING_OPTIONS */
if (!hapd->dpp_init_done)
return;
- dpp_bootstrap_del(hapd, 0);
- dpp_configurator_del(hapd, 0);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
hostapd_dpp_pkex_remove(hapd, "*");
@@ -1481,3 +2040,20 @@
os_free(hapd->dpp_configurator_params);
hapd->dpp_configurator_params = NULL;
}
+
+
+void hostapd_dpp_init_global(struct hapd_interfaces *ifaces)
+{
+ dl_list_init(&ifaces->dpp_bootstrap);
+ dl_list_init(&ifaces->dpp_configurator);
+ ifaces->dpp_init_done = 1;
+}
+
+
+void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces)
+{
+ if (!ifaces->dpp_init_done)
+ return;
+ dpp_bootstrap_del(ifaces, 0);
+ dpp_configurator_del(ifaces, 0);
+}
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index d870b20..3ef7c14 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -17,6 +17,8 @@
int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
char *reply, int reply_size);
int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
const u8 *buf, size_t len, unsigned int freq);
void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -24,11 +26,18 @@
struct wpabuf *
hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
const u8 *query, size_t query_len);
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
+int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
+ char *buf, size_t buflen);
int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
+void hostapd_dpp_stop(struct hostapd_data *hapd);
int hostapd_dpp_init(struct hostapd_data *hapd);
void hostapd_dpp_deinit(struct hostapd_data *hapd);
+void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
+void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
#endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 648f20e..3994278 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -235,6 +235,14 @@
elems.hs20_len - 4);
} else
sta->hs20_ie = NULL;
+
+ wpabuf_free(sta->roaming_consortium);
+ if (elems.roaming_cons_sel)
+ sta->roaming_consortium = wpabuf_alloc_copy(
+ elems.roaming_cons_sel + 4,
+ elems.roaming_cons_sel_len - 4);
+ else
+ sta->roaming_consortium = NULL;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FST
@@ -262,7 +270,9 @@
#endif /* CONFIG_WPS */
wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
- return -1;
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ goto fail;
}
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
@@ -317,8 +327,8 @@
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
} else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
- reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
- status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
+ status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
}
#endif /* CONFIG_IEEE80211W */
else {
@@ -443,6 +453,10 @@
#ifdef CONFIG_IEEE80211R_AP
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
sta->auth_alg, req_ies, req_ies_len);
+ if (!p) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_FILS
@@ -520,7 +534,32 @@
}
#endif /* CONFIG_FILS */
-#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS)
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ elems.owe_dh) {
+ u8 *npos;
+
+ npos = owe_assoc_req_process(hapd, sta,
+ elems.owe_dh, elems.owe_dh_len,
+ p, sizeof(buf) - (p - buf),
+ &reason);
+ if (npos)
+ p = npos;
+ if (!npos &&
+ reason == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
+ status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+ p - buf);
+ return 0;
+ }
+
+ if (!npos || reason != WLAN_STATUS_SUCCESS)
+ goto fail;
+ }
+#endif /* CONFIG_OWE */
+
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
if (sta->auth_alg == WLAN_AUTH_FT ||
@@ -615,7 +654,7 @@
{
struct sta_info *sta = ap_get_sta(hapd, addr);
- if (!sta || !hapd->conf->disassoc_low_ack)
+ if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer)
return;
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
@@ -626,6 +665,73 @@
}
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+ enum smps_mode smps_mode,
+ enum chan_width chan_width, u8 rx_nss)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ const char *txt;
+
+ if (!sta)
+ return;
+
+ switch (smps_mode) {
+ case SMPS_AUTOMATIC:
+ txt = "automatic";
+ break;
+ case SMPS_OFF:
+ txt = "off";
+ break;
+ case SMPS_DYNAMIC:
+ txt = "dynamic";
+ break;
+ case SMPS_STATIC:
+ txt = "static";
+ break;
+ default:
+ txt = NULL;
+ break;
+ }
+ if (txt) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED
+ MACSTR " %s", MAC2STR(addr), txt);
+ }
+
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ txt = "20(no-HT)";
+ break;
+ case CHAN_WIDTH_20:
+ txt = "20";
+ break;
+ case CHAN_WIDTH_40:
+ txt = "40";
+ break;
+ case CHAN_WIDTH_80:
+ txt = "80";
+ break;
+ case CHAN_WIDTH_80P80:
+ txt = "80+80";
+ break;
+ case CHAN_WIDTH_160:
+ txt = "160";
+ break;
+ default:
+ txt = NULL;
+ break;
+ }
+ if (txt) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED
+ MACSTR " %s", MAC2STR(addr), txt);
+ }
+
+ if (rx_nss != 0xff) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED
+ MACSTR " %d", MAC2STR(addr), rx_nss);
+ }
+}
+
+
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
int offset, int width, int cf1, int cf2)
{
@@ -635,9 +741,9 @@
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
- freq, ht, offset, width, channel_width_to_string(width),
- cf1, cf2);
+ "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+ freq, ht, hapd->iconf->ch_switch_vht_config, offset,
+ width, channel_width_to_string(width), cf1, cf2);
hapd->iface->freq = freq;
@@ -682,14 +788,26 @@
hapd->iconf->channel = channel;
hapd->iconf->ieee80211n = ht;
- if (!ht)
+ if (!ht) {
hapd->iconf->ieee80211ac = 0;
+ } else if (hapd->iconf->ch_switch_vht_config) {
+ /* CHAN_SWITCH VHT config */
+ if (hapd->iconf->ch_switch_vht_config &
+ CH_SWITCH_VHT_ENABLED)
+ hapd->iconf->ieee80211ac = 1;
+ else if (hapd->iconf->ch_switch_vht_config &
+ CH_SWITCH_VHT_DISABLED)
+ hapd->iconf->ieee80211ac = 0;
+ }
+ hapd->iconf->ch_switch_vht_config = 0;
+
hapd->iconf->secondary_channel = offset;
hapd->iconf->vht_oper_chwidth = chwidth;
hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
- is_dfs = ieee80211_is_dfs(freq);
+ is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
if (hapd->csa_in_progress &&
freq == hapd->cs_freq_params.freq) {
@@ -1095,6 +1213,7 @@
}
os_memset(&fi, 0, sizeof(fi));
+ fi.freq = rx_mgmt->freq;
fi.datarate = rx_mgmt->datarate;
fi.ssi_signal = rx_mgmt->ssi_signal;
@@ -1378,6 +1497,28 @@
#endif /* NEED_AP_MLME */
+static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd,
+ int istatus,
+ const char *ifname,
+ const u8 *addr)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (sta) {
+ os_free(sta->ifname_wds);
+ if (istatus == INTERFACE_ADDED)
+ sta->ifname_wds = os_strdup(ifname);
+ else
+ sta->ifname_wds = NULL;
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR,
+ istatus == INTERFACE_ADDED ?
+ WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED,
+ ifname, MAC2STR(addr));
+}
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
@@ -1586,6 +1727,18 @@
&data->acs_selected_channels);
break;
#endif /* CONFIG_ACS */
+ case EVENT_STATION_OPMODE_CHANGED:
+ hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr,
+ data->sta_opmode.smps_mode,
+ data->sta_opmode.chan_width,
+ data->sta_opmode.rx_nss);
+ break;
+ case EVENT_WDS_STA_INTERFACE_STATUS:
+ hostapd_event_wds_sta_interface_status(
+ hapd, data->wds_sta_interface.istatus,
+ data->wds_sta_interface.ifname,
+ data->wds_sta_interface.sta_addr);
+ break;
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index 082d0f5..fab307f 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -91,6 +91,8 @@
set_user_methods(user, argv[i]);
} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
user->remediation = strlen(argv[i]) > 0;
+ } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
+ user->t_c_timestamp = strtol(argv[i], 0, 10);
}
}
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index fadb740..04fb3e1 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -183,6 +183,8 @@
wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
if (hapd->conf->hs20_icons_count)
wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+ if (hapd->conf->hs20_operator_icon_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
gas_anqp_set_element_len(buf, len);
}
#endif /* CONFIG_HS20 */
@@ -288,7 +290,7 @@
#endif /* CONFIG_FILS */
if (get_anqp_elem(hapd, ANQP_CAG))
wpabuf_put_le16(buf, ANQP_CAG);
- if (get_anqp_elem(hapd, ANQP_VENUE_URL))
+ if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
wpabuf_put_le16(buf, ANQP_VENUE_URL);
if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
@@ -328,6 +330,29 @@
}
+static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+ if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
+ return;
+
+ if (hapd->conf->venue_url) {
+ u8 *len;
+ unsigned int i;
+
+ len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
+ for (i = 0; i < hapd->conf->venue_url_count; i++) {
+ struct hostapd_venue_url *url;
+
+ url = &hapd->conf->venue_url[i];
+ wpabuf_put_u8(buf, 1 + url->url_len);
+ wpabuf_put_u8(buf, url->venue_number);
+ wpabuf_put_data(buf, url->url, url->url_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
static void anqp_add_network_auth_type(struct hostapd_data *hapd,
struct wpabuf *buf)
{
@@ -680,6 +705,29 @@
}
+static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
+ const char *name)
+{
+ size_t j;
+ struct hs20_icon *icon = NULL;
+
+ for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+ if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
+ icon = &bss->hs20_icons[j];
+ }
+ if (!icon)
+ return; /* icon info not found */
+
+ wpabuf_put_le16(buf, icon->width);
+ wpabuf_put_le16(buf, icon->height);
+ wpabuf_put_data(buf, icon->language, 3);
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_u8(buf, os_strlen(icon->name));
+ wpabuf_put_str(buf, icon->name);
+}
+
+
static void anqp_add_osu_provider(struct wpabuf *buf,
struct hostapd_bss_config *bss,
struct hs20_osu_provider *p)
@@ -714,26 +762,8 @@
/* Icons Available */
len2 = wpabuf_put(buf, 2);
- for (i = 0; i < p->icons_count; i++) {
- size_t j;
- struct hs20_icon *icon = NULL;
-
- for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
- if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
- 0)
- icon = &bss->hs20_icons[j];
- }
- if (!icon)
- continue; /* icon info not found */
-
- wpabuf_put_le16(buf, icon->width);
- wpabuf_put_le16(buf, icon->height);
- wpabuf_put_data(buf, icon->language, 3);
- wpabuf_put_u8(buf, os_strlen(icon->type));
- wpabuf_put_str(buf, icon->type);
- wpabuf_put_u8(buf, os_strlen(icon->name));
- wpabuf_put_str(buf, icon->name);
- }
+ for (i = 0; i < p->icons_count; i++)
+ anqp_add_icon(buf, bss, p->icons[i]);
WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
/* OSU_NAI */
@@ -842,6 +872,30 @@
gas_anqp_set_element_len(buf, len);
}
+
+static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ struct hostapd_bss_config *bss = hapd->conf;
+ size_t i;
+ u8 *len;
+
+ if (!bss->hs20_operator_icon_count)
+ return;
+
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ for (i = 0; i < bss->hs20_operator_icon_count; i++)
+ anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
+
+ gas_anqp_set_element_len(buf, len);
+}
+
#endif /* CONFIG_HS20 */
@@ -946,6 +1000,10 @@
continue;
}
#endif /* CONFIG_FILS */
+ if (extra_req[i] == ANQP_VENUE_URL) {
+ anqp_add_venue_url(hapd, buf);
+ continue;
+ }
anqp_add_elem(hapd, buf, extra_req[i]);
}
@@ -964,6 +1022,8 @@
anqp_add_osu_providers_list(hapd, buf);
if (request & ANQP_REQ_ICON_REQUEST)
anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
+ if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
+ anqp_add_operator_icon_metadata(hapd, buf);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
@@ -1082,7 +1142,10 @@
"ANQP: FILS Realm Information (local)");
} else
#endif /* CONFIG_FILS */
- if (!get_anqp_elem(hapd, info_id)) {
+ if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Venue URL (local)");
+ } else if (!get_anqp_elem(hapd, info_id)) {
wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
info_id);
break;
@@ -1148,6 +1211,11 @@
set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
hapd->conf->hs20_osu_providers_count, qi);
break;
+ case HS20_STYPE_OPERATOR_ICON_METADATA:
+ set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
+ "Operator Icon Metadata",
+ hapd->conf->hs20_operator_icon_count, qi);
+ break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
subtype);
@@ -1460,7 +1528,7 @@
gas_serv_write_dpp_adv_proto(tx_buf);
wpabuf_put_le16(tx_buf, wpabuf_len(buf));
wpabuf_put_buf(tx_buf, buf);
- wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ hostapd_dpp_gas_status_handler(hapd, 1);
}
wpabuf_free(buf);
}
@@ -1702,7 +1770,7 @@
"SD response sent");
#ifdef CONFIG_DPP
if (dialog->dpp)
- wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ hostapd_dpp_gas_status_handler(hapd, 1);
#endif /* CONFIG_DPP */
gas_serv_dialog_clear(dialog);
gas_serv_free_dialogs(hapd, sa);
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 3a30298..0afdcb1 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -60,6 +60,8 @@
(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
#define ANQP_REQ_ICON_REQUEST \
(0x10000 << HS20_STYPE_ICON_REQUEST)
+#define ANQP_REQ_OPERATOR_ICON_METADATA \
+ (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
/* The first MBO ANQP-element can be included in the optimized bitmap. */
#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 12911df..f095586 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -49,6 +49,7 @@
#include "rrm.h"
#include "fils_hlp.h"
#include "acs.h"
+#include "hs20.h"
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -56,6 +57,8 @@
static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
static int setup_interface2(struct hostapd_iface *iface);
static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+ void *timeout_ctx);
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -82,6 +85,9 @@
if (!hapd->started)
return;
+ if (hapd->conf->wmm_enabled < 0)
+ hapd->conf->wmm_enabled = hapd->iconf->ieee80211n;
+
#ifndef CONFIG_NO_RADIUS
radius_client_reconfig(hapd->radius, hapd->conf->radius);
#endif /* CONFIG_NO_RADIUS */
@@ -426,6 +432,8 @@
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ NULL);
hostapd_cleanup_iface_partial(iface);
hostapd_config_free(iface->conf);
@@ -893,6 +901,48 @@
return RADIUS_DAS_SUCCESS;
}
+
+#ifdef CONFIG_HS20
+static enum radius_das_res
+hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ int multi;
+
+ if (hostapd_das_nas_mismatch(hapd, attr))
+ return RADIUS_DAS_NAS_MISMATCH;
+
+ sta = hostapd_das_find_sta(hapd, attr, &multi);
+ if (!sta) {
+ if (multi) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Multiple sessions match - not supported");
+ return RADIUS_DAS_MULTI_SESSION_MATCH;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+ return RADIUS_DAS_SESSION_NOT_FOUND;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+ " - CoA", MAC2STR(sta->addr));
+
+ if (attr->hs20_t_c_filtering) {
+ if (attr->hs20_t_c_filtering[0] & BIT(0)) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request");
+ return RADIUS_DAS_COA_FAILED;
+ }
+
+ hs20_t_c_filtering(hapd, sta, 0);
+ }
+
+ return RADIUS_DAS_SUCCESS;
+}
+#else /* CONFIG_HS20 */
+#define hostapd_das_coa NULL
+#endif /* CONFIG_HS20 */
+
#endif /* CONFIG_NO_RADIUS */
@@ -1067,6 +1117,7 @@
conf->radius_das_require_message_authenticator;
das_conf.ctx = hapd;
das_conf.disconnect = hostapd_das_disconnect;
+ das_conf.coa = hostapd_das_coa;
hapd->radius_das = radius_das_init(&das_conf);
if (hapd->radius_das == NULL) {
wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
@@ -1776,6 +1827,20 @@
}
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_iface *iface = eloop_ctx;
+ struct hostapd_data *hapd;
+
+ if (iface->num_bss < 1 || !iface->bss || !iface->bss[0])
+ return;
+ hapd = iface->bss[0];
+ if (hapd->setup_complete_cb)
+ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+}
+
+
static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
int err)
{
@@ -1976,8 +2041,19 @@
iface->fst = NULL;
}
#endif /* CONFIG_FST */
- if (iface->interfaces && iface->interfaces->terminate_on_error)
+
+ if (iface->interfaces && iface->interfaces->terminate_on_error) {
eloop_terminate();
+ } else if (hapd->setup_complete_cb) {
+ /*
+ * Calling hapd->setup_complete_cb directly may cause iface
+ * deinitialization which may be accessed later by the caller.
+ */
+ eloop_register_timeout(0, 0,
+ hostapd_interface_setup_failure_handler,
+ iface, NULL);
+ }
+
return -1;
}
@@ -2159,12 +2235,6 @@
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
-#ifdef CONFIG_IEEE80211N
-#ifdef NEED_AP_MLME
- hostapd_stop_setup_timers(iface);
- eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
-#endif /* NEED_AP_MLME */
-#endif /* CONFIG_IEEE80211N */
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
iface->wait_channel_update = 0;
@@ -2180,6 +2250,13 @@
break;
hostapd_bss_deinit(iface->bss[j]);
}
+
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
}
@@ -3294,6 +3371,19 @@
}
+void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled)
+{
+ if (vht_enabled)
+ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED;
+ else
+ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED;
+
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x",
+ hapd->iconf->ch_switch_vht_config);
+}
+
+
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings)
{
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index d7c6720..6c41726 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -57,6 +57,12 @@
struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
#endif /* CONFIG_ETH_P_OUI */
int eloop_initialized;
+
+#ifdef CONFIG_DPP
+ int dpp_init_done;
+ struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
+ struct dl_list dpp_configurator; /* struct dpp_configurator */
+#endif /* CONFIG_DPP */
};
enum hostapd_chan_status {
@@ -79,6 +85,7 @@
};
struct hostapd_frame_info {
+ unsigned int freq;
u32 channel;
u32 datarate;
int ssi_signal; /* dBm */
@@ -257,9 +264,6 @@
unsigned int cs_c_off_ecsa_beacon;
unsigned int cs_c_off_ecsa_proberesp;
- /* BSS Load */
- unsigned int bss_load_update_timeout;
-
#ifdef CONFIG_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
@@ -338,13 +342,12 @@
int dhcp_sock; /* UDP socket used with the DHCP server */
#ifdef CONFIG_DPP
- struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
- struct dl_list dpp_configurator; /* struct dpp_configurator */
int dpp_init_done;
struct dpp_authentication *dpp_auth;
u8 dpp_allowed_roles;
int dpp_qr_mutual;
int dpp_auth_ok_on_ack;
+ int dpp_in_response_listen;
struct gas_query_ap *gas;
struct dpp_pkex *dpp_pkex;
struct dpp_bootstrap_info *dpp_pkex_bi;
@@ -352,6 +355,13 @@
char *dpp_pkex_identifier;
char *dpp_pkex_auth_cmd;
char *dpp_configurator_params;
+ struct os_reltime dpp_last_init;
+ struct os_reltime dpp_init_iter_start;
+ unsigned int dpp_init_max_tries;
+ unsigned int dpp_init_retry_time;
+ unsigned int dpp_resp_wait_time;
+ unsigned int dpp_resp_max_tries;
+ unsigned int dpp_resp_retry_time;
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
char *dpp_discovery_override;
@@ -497,6 +507,10 @@
u64 last_channel_time_busy;
u8 channel_utilization;
+ unsigned int chan_util_samples_sum;
+ unsigned int chan_util_num_sample_periods;
+ unsigned int chan_util_average;
+
/* eCSA IE will be added only if operating class is specified */
u8 cs_oper_class;
@@ -551,6 +565,7 @@
void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
const char * hostapd_state_text(enum hostapd_iface_state s);
int hostapd_csa_in_progress(struct hostapd_iface *iface);
+void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled);
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings);
void
@@ -595,6 +610,9 @@
struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
const char *ifname);
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+ enum smps_mode smps_mode,
+ enum chan_width chan_width, u8 rx_nss);
#ifdef CONFIG_FST
void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index d7909fa..98d016d 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -11,9 +11,11 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "sta_info.h"
#include "hs20.h"
@@ -175,3 +177,71 @@
return ret;
}
+
+
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+ const u8 *addr, const char *url)
+{
+ struct wpabuf *buf;
+ int ret;
+ size_t url_len = os_strlen(url);
+
+ if (!url) {
+ wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
+ return -1;
+ }
+
+ if (5 + url_len > 255) {
+ wpa_printf(MSG_INFO,
+ "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
+ url);
+ return -1;
+ }
+
+ buf = wpabuf_alloc(4 + 7 + url_len);
+ if (!buf)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Terms and Conditions Acceptance subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + 1 + url_len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
+ wpabuf_put_u8(buf, url_len);
+ wpabuf_put_str(buf, url);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+ int enabled)
+{
+ if (enabled) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering required for "
+ MACSTR, MAC2STR(sta->addr));
+ sta->hs20_t_c_filtering = 1;
+ /* TODO: Enable firewall filtering for the STA */
+ wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
+ MAC2STR(sta->addr));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering not required for "
+ MACSTR, MAC2STR(sta->addr));
+ sta->hs20_t_c_filtering = 0;
+ /* TODO: Disable firewall filtering for the STA */
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
+ }
+}
diff --git a/src/ap/hs20.h b/src/ap/hs20.h
index 152439f..e99e26e 100644
--- a/src/ap/hs20.h
+++ b/src/ap/hs20.h
@@ -18,5 +18,9 @@
int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
const u8 *addr,
const struct wpabuf *payload);
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+ const u8 *addr, const char *url);
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+ int enabled);
#endif /* HS20_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 65c4d88..d7e7210 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -277,7 +277,7 @@
static int send_auth_reply(struct hostapd_data *hapd,
const u8 *dst, const u8 *bssid,
u16 auth_alg, u16 auth_transaction, u16 resp,
- const u8 *ies, size_t ies_len)
+ const u8 *ies, size_t ies_len, const char *dbg)
{
struct ieee80211_mgmt *reply;
u8 *buf;
@@ -304,9 +304,9 @@
os_memcpy(reply->u.auth.variable, ies, ies_len);
wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
- " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
+ " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
MAC2STR(dst), auth_alg, auth_transaction,
- resp, (unsigned long) ies_len);
+ resp, (unsigned long) ies_len, dbg);
if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
wpa_printf(MSG_INFO, "send_auth_reply: send failed");
else
@@ -328,7 +328,8 @@
int reply_res;
reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT,
- auth_transaction, status, ies, ies_len);
+ auth_transaction, status, ies, ies_len,
+ "auth-ft-finish");
sta = ap_get_sta(hapd, dst);
if (sta == NULL)
@@ -354,16 +355,39 @@
#ifdef CONFIG_SAE
-#define dot11RSNASAESync 5 /* attempts */
+static void sae_set_state(struct sta_info *sta, enum sae_state state,
+ const char *reason)
+{
+ wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
+ sae_state_txt(sta->sae->state), sae_state_txt(state),
+ MAC2STR(sta->addr), reason);
+ sta->sae->state = state;
+}
static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
struct sta_info *sta, int update)
{
struct wpabuf *buf;
- const char *password;
+ const char *password = NULL;
+ struct sae_password_entry *pw;
+ const char *rx_id = NULL;
- password = hapd->conf->sae_password;
+ if (sta->sae->tmp)
+ rx_id = sta->sae->tmp->pw_id;
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (!is_broadcast_ether_addr(pw->peer_addr) &&
+ os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
+ continue;
+ if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
+ continue;
+ if (rx_id && pw->identifier &&
+ os_strcmp(rx_id, pw->identifier) != 0)
+ continue;
+ password = pw->password;
+ break;
+ }
if (!password)
password = hapd->conf->ssid.wpa_passphrase;
if (!password) {
@@ -373,17 +397,18 @@
if (update &&
sae_prepare_commit(hapd->own_addr, sta->addr,
- (u8 *) password, os_strlen(password),
+ (u8 *) password, os_strlen(password), rx_id,
sta->sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
- buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
+ buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
+ (rx_id ? 3 + os_strlen(rx_id) : 0));
if (buf == NULL)
return NULL;
sae_write_commit(sta->sae, buf, sta->sae->tmp ?
- sta->sae->tmp->anti_clogging_token : NULL);
+ sta->sae->tmp->anti_clogging_token : NULL, rx_id);
return buf;
}
@@ -412,12 +437,14 @@
int reply_res;
data = auth_build_sae_commit(hapd, sta, update);
+ if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
- wpabuf_len(data));
+ wpabuf_len(data), "sae-send-commit");
wpabuf_free(data);
@@ -438,7 +465,7 @@
reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
- wpabuf_len(data));
+ wpabuf_len(data), "sae-send-confirm");
wpabuf_free(data);
@@ -517,10 +544,10 @@
}
-static int sae_check_big_sync(struct sta_info *sta)
+static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
{
- if (sta->sae->sync > dot11RSNASAESync) {
- sta->sae->state = SAE_NOTHING;
+ if (sta->sae->sync > hapd->conf->sae_sync) {
+ sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
sta->sae->sync = 0;
return -1;
}
@@ -534,12 +561,13 @@
struct sta_info *sta = eloop_data;
int ret;
- if (sae_check_big_sync(sta))
+ if (sae_check_big_sync(hapd, sta))
return;
sta->sae->sync++;
wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
- " (sync=%d state=%d)",
- MAC2STR(sta->addr), sta->sae->sync, sta->sae->state);
+ " (sync=%d state=%s)",
+ MAC2STR(sta->addr), sta->sae->sync,
+ sae_state_txt(sta->sae->state));
switch (sta->sae->state) {
case SAE_COMMITTED:
@@ -588,7 +616,7 @@
sta->auth_alg = WLAN_AUTH_SAE;
mlme_authenticate_indication(hapd, sta);
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
- sta->sae->state = SAE_ACCEPTED;
+ sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
sta->sae->pmk, sta->sae->pmkid);
}
@@ -602,13 +630,16 @@
if (auth_transaction != 1 && auth_transaction != 2)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
+ MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
+ auth_transaction);
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
ret = auth_sae_send_commit(hapd, sta, bssid, 1);
if (ret)
return ret;
- sta->sae->state = SAE_COMMITTED;
+ sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -630,7 +661,8 @@
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret)
return ret;
- sta->sae->state = SAE_CONFIRMED;
+ sae_set_state(sta, SAE_CONFIRMED,
+ "Sent Confirm (mesh)");
} else {
/*
* For infrastructure BSS, send only the Commit
@@ -659,7 +691,7 @@
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret)
return ret;
- sta->sae->state = SAE_CONFIRMED;
+ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
} else if (hapd->conf->mesh & MESH_ENABLED) {
@@ -667,7 +699,7 @@
* In mesh case, follow SAE finite state machine and
* send Commit now, if sync count allows.
*/
- if (sae_check_big_sync(sta))
+ if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
@@ -686,7 +718,7 @@
if (ret)
return ret;
- sta->sae->state = SAE_CONFIRMED;
+ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
/*
* Since this was triggered on Confirm RX, run another
@@ -699,7 +731,7 @@
case SAE_CONFIRMED:
sae_clear_retransmit_timer(hapd, sta);
if (auth_transaction == 1) {
- if (sae_check_big_sync(sta))
+ if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
@@ -716,18 +748,31 @@
sae_set_retransmit_timer(hapd, sta);
} else {
+ sta->sae->send_confirm = 0xffff;
sae_accept_sta(hapd, sta);
}
break;
case SAE_ACCEPTED:
- if (auth_transaction == 1) {
+ if (auth_transaction == 1 &&
+ (hapd->conf->mesh & MESH_ENABLED)) {
wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
") doing reauthentication",
MAC2STR(sta->addr));
ap_free_sta(hapd, sta);
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+ } else if (auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+ if (ret)
+ return ret;
+ sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
} else {
- if (sae_check_big_sync(sta))
+ if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
@@ -799,7 +844,8 @@
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
- auth_transaction, resp, pos, end - pos);
+ auth_transaction, resp, pos, end - pos,
+ "auth-sae-reflection-attack");
goto remove_sta;
}
@@ -808,7 +854,8 @@
send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
wpabuf_head(hapd->conf->sae_commit_override),
- wpabuf_len(hapd->conf->sae_commit_override));
+ wpabuf_len(hapd->conf->sae_commit_override),
+ "sae-commit-override");
goto remove_sta;
}
#endif /* CONFIG_TESTING_OPTIONS */
@@ -823,7 +870,7 @@
resp = -1;
goto remove_sta;
}
- sta->sae->state = SAE_NOTHING;
+ sae_set_state(sta, SAE_NOTHING, "Init");
sta->sae->sync = 0;
}
@@ -886,7 +933,8 @@
"SAE: Failed to send commit message");
goto remove_sta;
}
- sta->sae->state = SAE_COMMITTED;
+ sae_set_state(sta, SAE_COMMITTED,
+ "Sent Commit (anti-clogging token case in mesh)");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
return;
@@ -905,6 +953,20 @@
if (status_code != WLAN_STATUS_SUCCESS)
goto remove_sta;
+ if (!(hapd->conf->mesh & MESH_ENABLED) &&
+ sta->sae->state == SAE_COMMITTED) {
+ /* This is needed in the infrastructure BSS case to
+ * address a sequence where a STA entry may remain in
+ * hostapd across two attempts to do SAE authentication
+ * by the same STA. The second attempt may end up trying
+ * to use a different group and that would not be
+ * allowed if we remain in Committed state with the
+ * previously set parameters. */
+ sae_set_state(sta, SAE_NOTHING,
+ "Clear existing state to allow restart");
+ sae_clear_data(sta->sae);
+ }
+
resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
((const u8 *) mgmt) + len -
mgmt->u.auth.variable, &token,
@@ -915,6 +977,17 @@
MAC2STR(sta->addr));
goto remove_sta;
}
+
+ if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+ MACSTR, MAC2STR(sta->addr));
+ sae_clear_retransmit_timer(hapd, sta);
+ sae_set_state(sta, SAE_NOTHING,
+ "Unknown Password Identifier");
+ goto remove_sta;
+ }
+
if (token && check_sae_token(hapd, sta->addr, token, token_len)
< 0) {
wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
@@ -935,7 +1008,8 @@
sta->addr);
resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
if (hapd->conf->mesh & MESH_ENABLED)
- sta->sae->state = SAE_NOTHING;
+ sae_set_state(sta, SAE_NOTHING,
+ "Request anti-clogging token case in mesh");
goto reply;
}
@@ -949,12 +1023,36 @@
goto remove_sta;
if (sta->sae->state >= SAE_CONFIRMED ||
!(hapd->conf->mesh & MESH_ENABLED)) {
- if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
- ((u8 *) mgmt) + len -
- mgmt->u.auth.variable) < 0) {
+ const u8 *var;
+ size_t var_len;
+ u16 peer_send_confirm;
+
+ var = mgmt->u.auth.variable;
+ var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
+ if (var_len < 2) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
+
+ peer_send_confirm = WPA_GET_LE16(var);
+
+ if (sta->sae->state == SAE_ACCEPTED &&
+ (peer_send_confirm <= sta->sae->rc ||
+ peer_send_confirm == 0xffff)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Silently ignore unexpected Confirm from peer "
+ MACSTR
+ " (peer-send-confirm=%u Rc=%u)",
+ MAC2STR(sta->addr),
+ peer_send_confirm, sta->sae->rc);
+ return;
+ }
+
+ if (sae_check_confirm(sta->sae, var, var_len) < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ sta->sae->rc = peer_send_confirm;
}
resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
} else {
@@ -972,7 +1070,7 @@
send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
- data ? wpabuf_len(data) : 0);
+ data ? wpabuf_len(data) : 0, "auth-sae");
}
remove_sta:
@@ -1009,7 +1107,7 @@
if (ret)
return -1;
- sta->sae->state = SAE_COMMITTED;
+ sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
@@ -1033,7 +1131,7 @@
if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
- return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
#endif /* CONFIG_IEEE80211W */
if (res == WPA_INVALID_MDIE)
return WLAN_STATUS_INVALID_MDIE;
@@ -1374,8 +1472,11 @@
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, wpabuf_put(data, 0),
+ res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384,
+ wpabuf_put(data, 0),
wpabuf_tailroom(data));
if (res < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1430,15 +1531,24 @@
if (sta->fils_erp_pmkid_set) {
/* TODO: get PMKLifetime from WPA parameters */
unsigned int dot11RSNAConfigPMKLifetime = 43200;
+ int session_timeout;
+
+ session_timeout = dot11RSNAConfigPMKLifetime;
+ if (sta->session_timeout_set) {
+ struct os_reltime now, diff;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&sta->session_timeout, &now,
+ &diff);
+ session_timeout = diff.sec;
+ }
sta->fils_erp_pmkid_set = 0;
if (wpa_auth_pmksa_add2(
hapd->wpa_auth, sta->addr,
pmk, pmk_len,
sta->fils_erp_pmkid,
- sta->session_timeout_set ?
- sta->session_timeout :
- dot11RSNAConfigPMKLifetime,
+ session_timeout,
wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
wpa_printf(MSG_ERROR,
"FILS: Failed to add PMKSA cache entry based on ERP");
@@ -1496,7 +1606,7 @@
WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp,
data ? wpabuf_head(data) : (u8 *) "",
- data ? wpabuf_len(data) : 0);
+ data ? wpabuf_len(data) : 0, "auth-fils-finish");
wpabuf_free(data);
if (resp == WLAN_STATUS_SUCCESS) {
@@ -1538,20 +1648,21 @@
#endif /* CONFIG_FILS */
-static int
+int
ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
const u8 *msg, size_t len, u32 *session_timeout,
u32 *acct_interim_interval,
struct vlan_description *vlan_id,
struct hostapd_sta_wpa_psk_short **psk,
- char **identity, char **radius_cui)
+ char **identity, char **radius_cui, int is_probe_req)
{
int res;
os_memset(vlan_id, 0, sizeof(*vlan_id));
res = hostapd_allowed_address(hapd, addr, msg, len,
session_timeout, acct_interim_interval,
- vlan_id, psk, identity, radius_cui);
+ vlan_id, psk, identity, radius_cui,
+ is_probe_req);
if (res == HOSTAPD_ACL_REJECT) {
wpa_printf(MSG_INFO,
@@ -1605,17 +1716,25 @@
sta->psk = NULL;
}
+ os_free(sta->identity);
sta->identity = *identity;
*identity = NULL;
+
+ os_free(sta->radius_cui);
sta->radius_cui = *radius_cui;
*radius_cui = NULL;
if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
sta->acct_interim_interval = acct_interim_interval;
- if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
+ sta->session_timeout_set = 1;
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
ap_sta_session_timeout(hapd, sta, session_timeout);
- else
+ } else {
+ sta->session_timeout_set = 0;
ap_sta_no_session_timeout(hapd, sta);
+ }
return 0;
}
@@ -1686,7 +1805,9 @@
#endif /* CONFIG_NO_RC4 */
if (hapd->tkip_countermeasures) {
- resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ wpa_printf(MSG_DEBUG,
+ "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -1787,8 +1908,12 @@
res = ieee802_11_allowed_address(
hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout,
- &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui);
+ &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui,
+ 0);
if (res == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Authentication frame from " MACSTR
+ " due to ACL reject", MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -1839,6 +1964,7 @@
sta = ap_sta_add(hapd, mgmt->sa);
if (!sta) {
+ wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
@@ -1850,6 +1976,7 @@
hapd, sta, res, session_timeout, acct_interim_interval,
&vlan_id, &psk, &identity, &radius_cui);
if (res) {
+ wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -1918,6 +2045,9 @@
case WLAN_AUTH_SHARED_KEY:
resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
fc & WLAN_FC_ISWEP);
+ if (resp != 0)
+ wpa_printf(MSG_DEBUG,
+ "auth_shared_key() failed: status=%d", resp);
sta->auth_alg = WLAN_AUTH_SHARED_KEY;
mlme_authenticate_indication(hapd, sta);
if (sta->challenge && auth_transaction == 1) {
@@ -1988,7 +2118,7 @@
reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
auth_transaction + 1, resp, resp_ies,
- resp_ies_len);
+ resp_ies_len, "handle-auth");
if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
reply_res != WLAN_STATUS_SUCCESS)) {
@@ -2123,8 +2253,16 @@
}
#endif /* CONFIG_INTERWORKING */
- if (ext_capab_ie_len > 0)
+ if (ext_capab_ie_len > 0) {
sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+ os_free(sta->ext_capability);
+ sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
+ if (sta->ext_capability) {
+ sta->ext_capability[0] = ext_capab_ie_len;
+ os_memcpy(sta->ext_capability + 1, ext_capab_ie,
+ ext_capab_ie_len);
+ }
+ }
return WLAN_STATUS_SUCCESS;
}
@@ -2486,6 +2624,10 @@
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
+ if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
+ sta->sae->state == SAE_ACCEPTED)
+ wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
+
if (wpa_auth_uses_sae(sta->wpa_sm) &&
sta->auth_alg == WLAN_AUTH_OPEN) {
struct rsn_pmksa_cache_entry *sa;
@@ -2568,6 +2710,14 @@
elems.hs20_len - 4);
} else
sta->hs20_ie = NULL;
+
+ wpabuf_free(sta->roaming_consortium);
+ if (elems.roaming_cons_sel)
+ sta->roaming_consortium = wpabuf_alloc_copy(
+ elems.roaming_cons_sel + 4,
+ elems.roaming_cons_sel_len - 4);
+ else
+ sta->roaming_consortium = NULL;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FST
@@ -2599,6 +2749,14 @@
os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
sizeof(sta->rrm_enabled_capa));
+ if (elems.power_capab) {
+ sta->min_tx_power = elems.power_capab[0];
+ sta->max_tx_power = elems.power_capab[1];
+ sta->power_capab = 1;
+ } else {
+ sta->power_capab = 0;
+ }
+
return WLAN_STATUS_SUCCESS;
}
@@ -2747,6 +2905,12 @@
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
buf + buflen - p,
sta->auth_alg, ies, ies_len);
+ if (!p) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to write AssocResp IEs");
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -2932,6 +3096,61 @@
}
+#ifdef CONFIG_OWE
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *owe_dh, u8 owe_dh_len,
+ u8 *owe_buf, size_t owe_buf_len, u16 *reason)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->own_ie_override) {
+ wpa_printf(MSG_DEBUG, "OWE: Using IE override");
+ *reason = WLAN_STATUS_SUCCESS;
+ return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+ *reason = WLAN_STATUS_SUCCESS;
+ return owe_buf;
+ }
+
+ *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+ if (*reason != WLAN_STATUS_SUCCESS)
+ return NULL;
+
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+
+ if (sta->owe_ecdh && owe_buf) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ *reason = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return owe_buf;
+ }
+
+ /* OWE Diffie-Hellman Parameter element */
+ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+ */
+ WPA_PUT_LE16(owe_buf, sta->owe_group);
+ owe_buf += 2;
+ os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+ owe_buf += wpabuf_len(pub);
+ wpabuf_free(pub);
+ }
+
+ return owe_buf;
+}
+#endif /* CONFIG_OWE */
+
+
#ifdef CONFIG_FILS
void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
@@ -3079,8 +3298,12 @@
acl_res = ieee802_11_allowed_address(
hapd, mgmt->sa, (const u8 *) mgmt, len,
&session_timeout, &acct_interim_interval,
- &vlan_id, &psk, &identity, &radius_cui);
+ &vlan_id, &psk, &identity, &radius_cui, 0);
if (acl_res == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Association Request frame from "
+ MACSTR " due to ACL reject",
+ MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -3144,7 +3367,7 @@
WLAN_FC_STYPE_ASSOC_REQ;
if (hapd->tkip_countermeasures) {
- resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -3174,6 +3397,8 @@
if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ int res;
+
/* The end of the payload is encrypted. Need to decrypt it
* before parsing. */
@@ -3183,13 +3408,14 @@
goto fail;
}
- left = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
- len, tmp, left);
- if (left < 0) {
+ res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+ len, tmp, left);
+ if (res < 0) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
pos = tmp;
+ left = res;
}
#endif /* CONFIG_FILS */
@@ -3208,7 +3434,8 @@
sta->listen_interval = listen_interval;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
sta->flags |= WLAN_STA_NONERP;
for (i = 0; i < sta->supported_rates_len; i++) {
if ((sta->supported_rates[i] & 0x7f) > 22) {
@@ -3227,7 +3454,8 @@
!sta->no_short_slot_time_set) {
sta->no_short_slot_time_set = 1;
hapd->iface->num_sta_no_short_slot_time++;
- if (hapd->iface->current_mode->mode ==
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode ==
HOSTAPD_MODE_IEEE80211G &&
hapd->iface->num_sta_no_short_slot_time == 1)
ieee802_11_set_beacons(hapd->iface);
@@ -3242,7 +3470,8 @@
!sta->no_short_preamble_set) {
sta->no_short_preamble_set = 1;
hapd->iface->num_sta_no_short_preamble++;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_preamble == 1)
ieee802_11_set_beacons(hapd->iface);
}
@@ -3512,7 +3741,8 @@
static int handle_action(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ unsigned int freq)
{
struct sta_info *sta;
sta = ap_get_sta(hapd, mgmt->sa);
@@ -3601,9 +3831,6 @@
if (len >= IEEE80211_HDRLEN + 2 &&
mgmt->u.action.u.public_action.action ==
WLAN_PA_20_40_BSS_COEX) {
- wpa_printf(MSG_DEBUG,
- "HT20/40 coex mgmt frame received from STA "
- MACSTR, MAC2STR(mgmt->sa));
hostapd_2040_coex_action(hapd, mgmt, len);
return 1;
}
@@ -3621,7 +3848,7 @@
pos = mgmt->u.action.u.vs_public_action.oui;
end = ((const u8 *) mgmt) + len;
hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
- hapd->iface->freq);
+ freq);
return 1;
}
if (len >= IEEE80211_HDRLEN + 2 &&
@@ -3720,10 +3947,17 @@
struct ieee80211_mgmt *mgmt;
u16 fc, stype;
int ret = 0;
+ unsigned int freq;
+ int ssi_signal = fi ? fi->ssi_signal : 0;
if (len < 24)
return 0;
+ if (fi && fi->freq)
+ freq = fi->freq;
+ else
+ freq = hapd->iface->freq;
+
mgmt = (struct ieee80211_mgmt *) buf;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
@@ -3750,7 +3984,7 @@
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
- handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
+ handle_probe_req(hapd, mgmt, len, ssi_signal);
return 1;
}
@@ -3765,7 +3999,7 @@
}
if (hapd->iconf->track_sta_max_num)
- sta_track_add(hapd->iface, mgmt->sa, fi->ssi_signal);
+ sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
switch (stype) {
case WLAN_FC_STYPE_AUTH:
@@ -3795,7 +4029,7 @@
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action");
- ret = handle_action(hapd, mgmt, len);
+ ret = handle_action(hapd, mgmt, len, freq);
break;
default:
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 3b381b4..2f3b4da 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -16,6 +16,8 @@
struct ieee80211_ht_capabilities;
struct ieee80211_vht_capabilities;
struct ieee80211_mgmt;
+struct vlan_description;
+struct hostapd_sta_wpa_psk_short;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@@ -142,6 +144,9 @@
struct sta_info *sta, int success,
struct wpabuf *erp_resp,
const u8 *msk, size_t msk_len);
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *owe_dh, u8 owe_dh_len,
+ u8 *owe_buf, size_t owe_buf_len, u16 *reason);
void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
@@ -151,4 +156,14 @@
struct sta_info *sta,
u16 resp, struct wpabuf *data, int pub));
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
+int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+ u32 *acct_interim_interval,
+ struct vlan_description *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui,
+ int is_probe_req);
+
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 3308398..5cb7fb1 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -244,6 +244,7 @@
* @psk: Linked list buffer for returning WPA PSK
* @identity: Buffer for returning identity (from RADIUS)
* @radius_cui: Buffer for returning CUI (from RADIUS)
+ * @is_probe_req: Whether this query for a Probe Request frame
* Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
*
* The caller is responsible for freeing the returned *identity and *radius_cui
@@ -254,7 +255,8 @@
u32 *acct_interim_interval,
struct vlan_description *vlan_id,
struct hostapd_sta_wpa_psk_short **psk,
- char **identity, char **radius_cui)
+ char **identity, char **radius_cui,
+ int is_probe_req)
{
int res;
@@ -281,6 +283,12 @@
#else /* CONFIG_NO_RADIUS */
struct hostapd_acl_query_data *query;
+ if (is_probe_req) {
+ /* Skip RADIUS queries for Probe Request frames to avoid
+ * excessive load on the authentication server. */
+ return HOSTAPD_ACL_ACCEPT;
+ };
+
/* Check whether ACL cache has an entry for this station */
res = hostapd_acl_cache_get(hapd, addr, session_timeout,
acct_interim_interval, vlan_id, psk,
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 71f53b9..5aece51 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -23,7 +23,8 @@
u32 *acct_interim_interval,
struct vlan_description *vlan_id,
struct hostapd_sta_wpa_psk_short **psk,
- char **identity, char **radius_cui);
+ char **identity, char **radius_cui,
+ int is_probe_req);
int hostapd_acl_init(struct hostapd_data *hapd);
void hostapd_acl_deinit(struct hostapd_data *hapd);
void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 146e447..214855d 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -236,17 +236,29 @@
int i;
const u8 *start = (const u8 *) mgmt;
const u8 *data = start + IEEE80211_HDRLEN + 2;
+ struct sta_info *sta;
+
+ wpa_printf(MSG_DEBUG,
+ "HT: Received 20/40 BSS Coexistence Management frame from "
+ MACSTR, MAC2STR(mgmt->sa));
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
mgmt->u.action.u.public_action.action);
- if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
return;
+ }
- if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
+ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore too short 20/40 BSS Coexistence Management frame");
return;
+ }
+ /* 20/40 BSS Coexistence element */
bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
bc_ie->length < 1) {
@@ -254,13 +266,35 @@
bc_ie->element_id, bc_ie->length);
return;
}
- if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
+ if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
+ wpa_printf(MSG_DEBUG,
+ "Truncated 20/40 BSS Coexistence element");
return;
+ }
data += 2 + bc_ie->length;
- wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
- bc_ie->coex_param);
+ wpa_printf(MSG_DEBUG,
+ "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
+ bc_ie->coex_param,
+ (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
+ (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
+ (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
+ (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
+ (bc_ie->coex_param & BIT(4)) ?
+ "[OBSSScanExemptionGrant]" : "",
+ (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
+ "[Reserved]" : "");
+
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+ /* Intra-BSS communication prohibiting 20/40 MHz BSS operation
+ */
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
+ return;
+ }
+
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -269,6 +303,8 @@
}
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+ /* Inter-BSS communication prohibiting 20/40 MHz BSS operation
+ */
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -276,12 +312,16 @@
is_ht40_allowed = 0;
}
- if (start + len - data >= 3 &&
- data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+ /* 20/40 BSS Intolerant Channel Report element (zero or more times) */
+ while (start + len - data >= 3 &&
+ data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
u8 ielen = data[1];
- if (ielen > start + len - data - 2)
+ if (ielen > start + len - data - 2) {
+ wpa_printf(MSG_DEBUG,
+ "Truncated 20/40 BSS Intolerant Channel Report element");
return;
+ }
ic_report = (struct ieee80211_2040_intol_chan_report *) data;
wpa_printf(MSG_DEBUG,
"20/40 BSS Intolerant Channel Report: Operating Class %u",
@@ -292,8 +332,10 @@
for (i = 0; i < ielen - 1; i++) {
u8 chan = ic_report->variable[i];
+ if (chan == iface->conf->channel)
+ continue; /* matching own primary channel */
if (is_40_allowed(iface, chan))
- continue;
+ continue; /* not within affected channels */
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -301,6 +343,8 @@
chan);
is_ht40_allowed = 0;
}
+
+ data += 2 + ielen;
}
wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
is_ht40_allowed, iface->num_sta_ht40_intolerant);
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 902f64f..a3f8609 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -225,6 +225,7 @@
*pos |= 0x40; /* Bit 70 - FTM responder */
if (hapd->conf->ftm_initiator)
*pos |= 0x80; /* Bit 71 - FTM initiator */
+ break;
case 9: /* Bits 72-79 */
#ifdef CONFIG_FILS
if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
@@ -445,7 +446,7 @@
{
size_t len;
- if (hapd->conf->time_advertisement != 2)
+ if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
return eid;
len = os_strlen(hapd->conf->time_zone);
@@ -607,6 +608,67 @@
#endif /* CONFIG_MBO */
+#ifdef CONFIG_OWE
+static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
+{
+ return hapd->conf->owe_transition_ssid_len > 0 &&
+ !is_zero_ether_addr(hapd->conf->owe_transition_bssid);
+}
+#endif /* CONFIG_OWE */
+
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_OWE
+ if (!hostapd_eid_owe_trans_enabled(hapd))
+ return 0;
+ return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
+#else /* CONFIG_OWE */
+ return 0;
+#endif /* CONFIG_OWE */
+}
+
+
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
+ size_t len)
+{
+#ifdef CONFIG_OWE
+ u8 *pos = eid;
+ size_t elen;
+
+ if (hapd->conf->owe_transition_ifname[0] &&
+ !hostapd_eid_owe_trans_enabled(hapd))
+ hostapd_owe_trans_get_info(hapd);
+
+ if (!hostapd_eid_owe_trans_enabled(hapd))
+ return pos;
+
+ elen = hostapd_eid_owe_trans_len(hapd);
+ if (len < elen) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Not enough room in the buffer for OWE IE");
+ return pos;
+ }
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = elen - 2;
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = OWE_OUI_TYPE;
+ os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ *pos++ = hapd->conf->owe_transition_ssid_len;
+ os_memcpy(pos, hapd->conf->owe_transition_ssid,
+ hapd->conf->owe_transition_ssid_len);
+ pos += hapd->conf->owe_transition_ssid_len;
+
+ return pos;
+#else /* CONFIG_OWE */
+ return eid;
+#endif /* CONFIG_OWE */
+}
+
+
void ap_copy_sta_supp_op_classes(struct sta_info *sta,
const u8 *supp_op_classes,
size_t supp_op_classes_len)
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 793d381..985f8b7 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -712,6 +712,41 @@
goto fail;
}
}
+
+ if (sta->roaming_consortium &&
+ !radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM,
+ wpabuf_head(sta->roaming_consortium),
+ wpabuf_len(sta->roaming_consortium))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 Roaming Consortium");
+ goto fail;
+ }
+
+ if (hapd->conf->t_c_filename) {
+ be32 timestamp;
+
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME,
+ (const u8 *) hapd->conf->t_c_filename,
+ os_strlen(hapd->conf->t_c_filename))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 T&C Filename");
+ goto fail;
+ }
+
+ timestamp = host_to_be32(hapd->conf->t_c_timestamp);
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP,
+ (const u8 *) ×tamp,
+ sizeof(timestamp))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 Timestamp");
+ goto fail;
+ }
+ }
}
#endif /* CONFIG_HS20 */
@@ -1177,7 +1212,7 @@
sta->eapol_sm->portValid = TRUE;
if (sta->eapol_sm->eap)
eap_sm_notify_cached(sta->eapol_sm->eap);
- /* TODO: get vlan_id from R0KH using RRB message */
+ ap_sta_bind_vlan(hapd, sta);
return;
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -1587,6 +1622,33 @@
ap_sta_session_warning_timeout(hapd, sta, warning_time);
}
+
+static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len)
+{
+ if (len < 4)
+ return; /* Malformed information */
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
+ pos[0], pos[1], pos[2], pos[3]);
+ hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
+}
+
+
+static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos, size_t len)
+{
+ os_free(sta->t_c_url);
+ sta->t_c_url = os_malloc(len + 1);
+ if (!sta->t_c_url)
+ return;
+ os_memcpy(sta->t_c_url, pos, len);
+ sta->t_c_url[len] = '\0';
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions URL %s", sta->t_c_url);
+}
+
#endif /* CONFIG_HS20 */
@@ -1634,6 +1696,12 @@
ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
session_timeout);
break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
+ ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
+ ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
+ break;
}
}
#endif /* CONFIG_HS20 */
@@ -1691,6 +1759,7 @@
struct sta_info *sta;
u32 session_timeout = 0, termination_action, acct_interim_interval;
int session_timeout_set;
+ u32 reason_code;
struct eapol_state_machine *sm;
int override_eapReq = 0;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
@@ -1816,14 +1885,17 @@
break;
sta->session_timeout_set = !!session_timeout_set;
- sta->session_timeout = session_timeout;
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
/* RFC 3580, Ch. 3.17 */
if (session_timeout_set && termination_action ==
- RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+ RADIUS_TERMINATION_ACTION_RADIUS_REQUEST)
sm->reAuthPeriod = session_timeout;
- } else if (session_timeout_set)
+ else if (session_timeout_set)
ap_sta_session_timeout(hapd, sta, session_timeout);
+ else
+ ap_sta_no_session_timeout(hapd, sta);
sm->eap_if->aaaSuccess = TRUE;
override_eapReq = 1;
@@ -1839,6 +1911,13 @@
case RADIUS_CODE_ACCESS_REJECT:
sm->eap_if->aaaFail = TRUE;
override_eapReq = 1;
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
+ &reason_code) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for "
+ MACSTR, reason_code, MAC2STR(sta->addr));
+ sta->disconnect_reason_code = reason_code;
+ }
break;
case RADIUS_CODE_ACCESS_CHALLENGE:
sm->eap_if->aaaEapReq = TRUE;
@@ -2081,6 +2160,13 @@
goto out;
user->password_len = eap_user->password_len;
user->password_hash = eap_user->password_hash;
+ if (eap_user->salt && eap_user->salt_len) {
+ user->salt = os_memdup(eap_user->salt,
+ eap_user->salt_len);
+ if (!user->salt)
+ goto out;
+ user->salt_len = eap_user->salt_len;
+ }
}
user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
@@ -2693,6 +2779,15 @@
hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
sta->hs20_deauth_req);
}
+
+ if (sta->hs20_t_c_filtering) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+ MACSTR " to indicate Terms and Conditions filtering",
+ MAC2STR(sta->addr));
+ hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url);
+ os_free(sta->t_c_url);
+ sta->t_c_url = NULL;
+ }
}
#endif /* CONFIG_HS20 */
@@ -2706,6 +2801,7 @@
/* TODO: get PMKLifetime from WPA parameters */
static const int dot11RSNAConfigPMKLifetime = 43200;
unsigned int session_timeout;
+ struct os_reltime now, remaining;
#ifdef CONFIG_HS20
if (remediation && !sta->remediation) {
@@ -2716,7 +2812,8 @@
sta->remediation_method = 1; /* SOAP-XML SPP */
}
- if (success && (sta->remediation || sta->hs20_deauth_req)) {
+ if (success && (sta->remediation || sta->hs20_deauth_req ||
+ sta->hs20_t_c_filtering)) {
wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
MACSTR " in 100 ms", MAC2STR(sta->addr));
eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
@@ -2726,10 +2823,13 @@
#endif /* CONFIG_HS20 */
key = ieee802_1x_get_key(sta->eapol_sm, &len);
- if (sta->session_timeout_set)
- session_timeout = sta->session_timeout;
- else
+ if (sta->session_timeout_set) {
+ os_get_reltime(&now);
+ os_reltime_sub(&sta->session_timeout, &now, &remaining);
+ session_timeout = (remaining.sec > 0) ? remaining.sec : 1;
+ } else {
session_timeout = dot11RSNAConfigPMKLifetime;
+ }
if (success && key && len >= PMK_LEN && !sta->remediation &&
!sta->hs20_deauth_requested &&
wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index b1fde3c..179cf43 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -197,7 +197,8 @@
if (sta->no_short_slot_time_set) {
sta->no_short_slot_time_set = 0;
hapd->iface->num_sta_no_short_slot_time--;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_slot_time == 0)
set_beacon++;
}
@@ -205,7 +206,8 @@
if (sta->no_short_preamble_set) {
sta->no_short_preamble_set = 0;
hapd->iface->num_sta_no_short_preamble--;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_preamble == 0)
set_beacon++;
}
@@ -319,6 +321,7 @@
wpabuf_free(sta->wps_ie);
wpabuf_free(sta->p2p_ie);
wpabuf_free(sta->hs20_ie);
+ wpabuf_free(sta->roaming_consortium);
#ifdef CONFIG_FST
wpabuf_free(sta->mb_ies);
#endif /* CONFIG_FST */
@@ -329,6 +332,7 @@
os_free(sta->identity);
os_free(sta->radius_cui);
os_free(sta->remediation_url);
+ os_free(sta->t_c_url);
wpabuf_free(sta->hs20_deauth_req);
os_free(sta->hs20_session_info_url);
@@ -357,6 +361,14 @@
crypto_ecdh_deinit(sta->owe_ecdh);
#endif /* CONFIG_OWE */
+ os_free(sta->ext_capability);
+
+#ifdef CONFIG_WNM_AP
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+#endif /* CONFIG_WNM_AP */
+
+ os_free(sta->ifname_wds);
+
os_free(sta);
}
@@ -1342,7 +1354,7 @@
int res;
buf[0] = '\0';
- res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1359,6 +1371,7 @@
(flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
(flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
(flags & WLAN_STA_GAS ? "[GAS]" : ""),
+ (flags & WLAN_STA_HT ? "[HT]" : ""),
(flags & WLAN_STA_VHT ? "[VHT]" : ""),
(flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
(flags & WLAN_STA_WNM_SLEEP_MODE ?
@@ -1374,13 +1387,16 @@
{
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = timeout_ctx;
+ u16 reason;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
"IEEE 802.1X: Scheduled disconnection of " MACSTR
" after EAP-Failure", MAC2STR(sta->addr));
- ap_sta_disconnect(hapd, sta, sta->addr,
- WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
+ reason = sta->disconnect_reason_code;
+ if (!reason)
+ reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
+ ap_sta_disconnect(hapd, sta, sta->addr, reason);
if (sta->flags & WLAN_STA_WPS)
hostapd_wps_eap_completed(hapd);
}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 3fb60f6..9cac6f1 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -9,11 +9,7 @@
#ifndef STA_INFO_H
#define STA_INFO_H
-#ifdef CONFIG_MESH
-/* needed for mesh_plink_state enum */
#include "common/defs.h"
-#endif /* CONFIG_MESH */
-
#include "list.h"
#include "vlan.h"
#include "common/wpa_common.h"
@@ -71,6 +67,7 @@
be32 ipaddr;
struct dl_list ip6addr; /* list head for struct ip6addr */
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+ u16 disconnect_reason_code; /* RADIUS server override */
u32 flags; /* Bitfield of WLAN_STA_* */
u16 capability;
u16 listen_interval; /* or beacon_int for APs */
@@ -117,6 +114,9 @@
unsigned int ecsa_supported:1;
unsigned int added_unassoc:1;
unsigned int pending_wds_enable:1;
+ unsigned int power_capab:1;
+ unsigned int agreed_to_steer:1;
+ unsigned int hs20_t_c_filtering:1;
u16 auth_alg;
@@ -183,8 +183,11 @@
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+ /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
+ struct wpabuf *roaming_consortium;
u8 remediation_method;
char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+ char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
struct wpabuf *hs20_deauth_req;
char *hs20_session_info_url;
int hs20_disassoc_timer;
@@ -199,7 +202,8 @@
unsigned int mesh_sae_pmksa_caching:1;
#endif /* CONFIG_SAE */
- u32 session_timeout; /* valid only if session_timeout_set == 1 */
+ /* valid only if session_timeout_set == 1 */
+ struct os_reltime session_timeout;
/* Last Authentication/(Re)Association Request/Action frame sequence
* control */
@@ -218,6 +222,9 @@
u8 rrm_enabled_capa[5];
+ s8 min_tx_power;
+ s8 max_tx_power;
+
#ifdef CONFIG_TAXONOMY
struct wpabuf *probe_ie_taxonomy;
struct wpabuf *assoc_ie_taxonomy;
@@ -251,6 +258,9 @@
u16 owe_group;
#endif /* CONFIG_OWE */
+ u8 *ext_capability;
+ char *ifname_wds; /* WDS ifname, if in use */
+
#ifdef CONFIG_TESTING_OPTIONS
enum wpa_alg last_tk_alg;
int last_tk_key_idx;
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 4725e2b..557570c 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -71,6 +71,11 @@
struct os_reltime now;
int ret = 0;
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Michael MIC failure detected in received frame%s",
+ local ? " (local)" : "");
+
if (addr && local) {
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta != NULL) {
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 7c4fde0..710fe50 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -109,6 +109,7 @@
pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
/* add key data if MFP is enabled */
if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ hapd->conf->wnm_sleep_mode_no_keys ||
action_type != WNM_SLEEP_MODE_EXIT) {
mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
} else {
@@ -173,7 +174,8 @@
wpa_set_wnmsleep(sta->wpa_sm, 0);
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
addr, NULL, NULL);
- if (!wpa_auth_uses_mfp(sta->wpa_sm))
+ if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ hapd->conf->wnm_sleep_mode_no_keys)
wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
}
} else
@@ -200,6 +202,13 @@
u8 *tfsreq_ie_end = NULL;
u16 tfsreq_ie_len = 0;
+ if (!hapd->conf->wnm_sleep_mode) {
+ wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
+ MACSTR " since WNM-Sleep Mode is disabled",
+ MAC2STR(addr));
+ return;
+ }
+
dialog_token = *pos++;
while (pos + 1 < frm + len) {
u8 ie_len = pos[1];
@@ -295,6 +304,20 @@
{
u8 dialog_token, reason;
const u8 *pos, *end;
+ int enabled = hapd->conf->bss_transition;
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ enabled = 1;
+#endif /* CONFIG_MBO */
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore BSS Transition Management Query from "
+ MACSTR
+ " since BSS Transition Management is disabled",
+ MAC2STR(addr));
+ return;
+ }
if (len < 2) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
@@ -318,12 +341,40 @@
}
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ if (sta->agreed_to_steer) {
+ wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->agreed_to_steer = 0;
+ }
+}
+
+
static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
const u8 *addr, const u8 *frm,
size_t len)
{
u8 dialog_token, status_code, bss_termination_delay;
const u8 *pos, *end;
+ int enabled = hapd->conf->bss_transition;
+ struct sta_info *sta;
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ enabled = 1;
+#endif /* CONFIG_MBO */
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore BSS Transition Management Response from "
+ MACSTR
+ " since BSS Transition Management is disabled",
+ MAC2STR(addr));
+ return;
+ }
if (len < 3) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
@@ -342,11 +393,23 @@
"bss_termination_delay=%u", MAC2STR(addr), dialog_token,
status_code, bss_termination_delay);
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for received BSS TM Response",
+ MAC2STR(addr));
+ return;
+ }
+
if (status_code == WNM_BSS_TM_ACCEPT) {
if (end - pos < ETH_ALEN) {
wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
return;
}
+ sta->agreed_to_steer = 1;
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+ eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+ hapd, sta);
wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
MAC2STR(pos));
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
@@ -356,6 +419,7 @@
MAC2STR(pos));
pos += ETH_ALEN;
} else {
+ sta->agreed_to_steer = 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
" status_code=%u bss_termination_delay=%u",
MAC2STR(addr), status_code, bss_termination_delay);
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index a44eadb..56d0f88 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -23,5 +23,6 @@
const u8 *bss_term_dur, const char *url,
const u8 *nei_rep, size_t nei_rep_len,
const u8 *mbo_attrs, size_t mbo_len);
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
#endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 8265fa1..126d98c 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -19,6 +19,7 @@
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/sha384.h"
#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "ap_config.h"
@@ -237,23 +238,6 @@
}
-static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
-{
- int ret = 0;
-#ifdef CONFIG_IEEE80211R_AP
- if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
- ret = 1;
-#endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211W
- if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
- ret = 1;
-#endif /* CONFIG_IEEE80211W */
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
- ret = 1;
- return ret;
-}
-
-
static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
@@ -698,9 +682,10 @@
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"strict rekeying - force GTK rekey since STA "
"is leaving");
- eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
- eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
- NULL);
+ if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
+ sm->wpa_auth, NULL) == -1)
+ eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
+ NULL);
}
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@@ -859,6 +844,12 @@
sm->p2p_dev_addr, pmk, &pmk_len);
if (pmk == NULL)
break;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+ sm->xxkey_len = pmk_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
} else {
pmk = sm->PMK;
pmk_len = sm->pmk_len;
@@ -873,7 +864,8 @@
break;
}
- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
@@ -1001,10 +993,8 @@
u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
if (sm->pairwise == WPA_CIPHER_CCMP ||
sm->pairwise == WPA_CIPHER_GCMP) {
- if (wpa_use_aes_cmac(sm) &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
- !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
- !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ if (wpa_use_cmac(sm->wpa_key_mgmt) &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
@@ -1014,11 +1004,8 @@
return;
}
- if (!wpa_use_aes_cmac(sm) &&
- !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
- !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_DPP &&
+ if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
@@ -1028,10 +1015,7 @@
}
}
- if ((wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
- wpa_key_mgmt_fils(sm->wpa_key_mgmt) ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) &&
+ if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
@@ -1317,7 +1301,7 @@
static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
const u8 *gnonce, u8 *gtk, size_t gtk_len)
{
- u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+ u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN];
u8 *pos;
int ret = 0;
@@ -1328,21 +1312,30 @@
* is done only at the Authenticator and as such, does not need to be
* exactly same.
*/
+ os_memset(data, 0, sizeof(data));
os_memcpy(data, addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
pos = data + ETH_ALEN + WPA_NONCE_LEN;
wpa_get_ntp_timestamp(pos);
pos += 8;
- if (random_get_bytes(pos, 16) < 0)
+ if (random_get_bytes(pos, gtk_len) < 0)
ret = -1;
-#ifdef CONFIG_IEEE80211W
- sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
-#else /* CONFIG_IEEE80211W */
- if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
- < 0)
+#ifdef CONFIG_SHA384
+ if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
ret = -1;
-#endif /* CONFIG_IEEE80211W */
+#else /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA256
+ if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#else /* CONFIG_SHA256 */
+ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#endif /* CONFIG_SHA256 */
+#endif /* CONFIG_SHA384 */
return ret;
}
@@ -1383,13 +1376,9 @@
if (force_version)
version = force_version;
- else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
- wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+ else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
- else if (wpa_use_aes_cmac(sm))
+ else if (wpa_use_cmac(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise != WPA_CIPHER_TKIP)
version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
@@ -1411,10 +1400,7 @@
key_data_len = kde_len;
if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
pad_len = key_data_len % 8;
if (pad_len)
@@ -1474,7 +1460,7 @@
os_memcpy(key_data, kde, kde_len);
WPA_PUT_BE16(key_mic + mic_len, kde_len);
#ifdef CONFIG_FILS
- } else if (!mic_len) {
+ } else if (!mic_len && kde) {
const u8 *aad[1];
size_t aad_len[1];
@@ -1513,10 +1499,7 @@
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_printf(MSG_DEBUG,
"WPA: Encrypt Key Data using AES-WRAP (KEK length %u)",
@@ -1560,9 +1543,12 @@
return;
}
- wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
- sm->wpa_key_mgmt, version,
- (u8 *) hdr, len, key_mic);
+ if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+ sm->wpa_key_mgmt, version,
+ (u8 *) hdr, len, key_mic) < 0) {
+ os_free(hdr);
+ return;
+ }
#ifdef CONFIG_TESTING_OPTIONS
if (!pairwise &&
wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
@@ -1963,8 +1949,13 @@
sm->pmk_len = pmk_len;
#ifdef CONFIG_IEEE80211R_AP
if (len >= 2 * PMK_LEN) {
- os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
- sm->xxkey_len = PMK_LEN;
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN);
+ sm->xxkey_len = SHA384_MAC_LEN;
+ } else {
+ os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ }
}
#endif /* CONFIG_IEEE80211R_AP */
} else {
@@ -2049,11 +2040,31 @@
pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
if (sm->pmksa) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from PMKSA entry",
+ sm->pmksa->pmkid, PMKID_LEN);
os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
sm->pmksa->pmkid, PMKID_LEN);
} else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
/* No KCK available to derive PMKID */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No KCK available to derive PMKID for message 1/4");
pmkid = NULL;
+#ifdef CONFIG_SAE
+ } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+ if (sm->pmkid_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from SAE",
+ sm->pmkid, PMKID_LEN);
+ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+ sm->pmkid, PMKID_LEN);
+ } else {
+ /* No PMKID available */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No SAE PMKID available for message 1/4");
+ pmkid = NULL;
+ }
+#endif /* CONFIG_SAE */
} else {
/*
* Calculate PMKID since no PMKSA cache entry was
@@ -2062,6 +2073,9 @@
rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
sm->wpa_key_mgmt);
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID derived from PMK",
+ &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
}
}
wpa_send_eapol(sm->wpa_auth, sm,
@@ -2112,21 +2126,24 @@
if (fils_ft_len) {
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
struct wpa_auth_config *conf = &wpa_auth->conf;
- u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+ size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
conf->ssid, conf->ssid_len,
conf->mobility_domain,
conf->r0_key_holder,
conf->r0_key_holder_len,
- sm->addr, pmk_r0, pmk_r0_name) < 0)
+ sm->addr, pmk_r0, pmk_r0_name,
+ use_sha384) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", pmk_r0, PMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
+ pmk_r0, pmk_r0_len);
wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_ft_store_pmk_r0(wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
- sm->pairwise);
+ wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
os_memset(fils_ft, 0, sizeof(fils_ft));
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -2507,7 +2524,8 @@
/* GTK KDE */
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -2629,6 +2647,12 @@
if (pmk == NULL)
break;
psk_found = 1;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+ sm->xxkey_len = pmk_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
} else {
pmk = sm->PMK;
pmk_len = sm->pmk_len;
@@ -2654,7 +2678,8 @@
}
#endif /* CONFIG_FILS */
- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
@@ -2816,7 +2841,8 @@
else
os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random IGTK to each STA to prevent use of
* IGTK in the BSS.
@@ -2894,7 +2920,8 @@
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -2987,7 +3014,10 @@
2 + sm->assoc_resp_ftie[1]);
res = 2 + sm->assoc_resp_ftie[1];
} else {
- res = wpa_write_ftie(conf, conf->r0_key_holder,
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
+ res = wpa_write_ftie(conf, use_sha384,
+ conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
kde + kde_len - pos,
@@ -3012,7 +3042,7 @@
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
- WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
pos += 4;
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -3270,7 +3300,8 @@
"sending 1/2 msg of Group Key Handshake");
gtk = gsm->GTK[gsm->GN - 1];
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -4047,6 +4078,13 @@
}
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
+{
+ os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+ sm->pmkid_set = 1;
+}
+
+
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
int session_timeout, int akmp)
@@ -4505,11 +4543,12 @@
#ifdef CONFIG_IEEE80211R_AP
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, u8 *buf, size_t len)
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+ u8 *buf, size_t len)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
- return wpa_write_ftie(conf, conf->r0_key_holder,
+ return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, buf, len, NULL, 0);
}
@@ -4529,7 +4568,7 @@
#endif /* CONFIG_FILS */
-#if CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_TESTING_OPTIONS
int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
void (*cb)(void *ctx1, void *ctx2),
@@ -4557,7 +4596,10 @@
void (*cb)(void *ctx1, void *ctx2),
void *ctx1, void *ctx2)
{
- u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, *opos;
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+#ifdef CONFIG_IEEE80211W
+ u8 *opos;
+#endif /* CONFIG_IEEE80211W */
size_t gtk_len, kde_len;
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
@@ -4654,12 +4696,15 @@
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gtk_len);
}
+#ifdef CONFIG_IEEE80211W
opos = pos;
pos = ieee80211w_kde_add(sm, pos);
- if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) {
- opos += 2; /* skip keyid */
+ if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+ /* skip KDE header and keyid */
+ opos += 2 + RSN_SELECTOR_LEN + 2;
os_memset(opos, 0, 6); /* clear PN */
}
+#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -4673,7 +4718,10 @@
2 + sm->assoc_resp_ftie[1]);
res = 2 + sm->assoc_resp_ftie[1];
} else {
- res = wpa_write_ftie(conf, conf->r0_key_holder,
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
+ res = wpa_write_ftie(conf, use_sha384,
+ conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
kde + kde_len - pos,
@@ -4698,7 +4746,7 @@
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
- WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
pos += 4;
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -4722,7 +4770,10 @@
u8 rsc[WPA_KEY_RSC_LEN];
struct wpa_group *gsm = sm->group;
const u8 *kde;
- u8 *kde_buf = NULL, *pos, *opos, hdr[2];
+ u8 *kde_buf = NULL, *pos, hdr[2];
+#ifdef CONFIG_IEEE80211W
+ u8 *opos;
+#endif /* CONFIG_IEEE80211W */
size_t kde_len;
u8 *gtk;
@@ -4745,12 +4796,16 @@
hdr[1] = 0;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gsm->GTK_len);
+#ifdef CONFIG_IEEE80211W
opos = pos;
pos = ieee80211w_kde_add(sm, pos);
- if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) {
- opos += 2; /* skip keyid */
+ if (pos - opos >=
+ 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+ /* skip KDE header and keyid */
+ opos += 2 + RSN_SELECTOR_LEN + 2;
os_memset(opos, 0, 6); /* clear PN */
}
+#endif /* CONFIG_IEEE80211W */
kde_len = pos - kde;
} else {
kde = gtk;
@@ -4773,4 +4828,13 @@
return 0;
}
+
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
+{
+ if (!wpa_auth)
+ return -1;
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+ return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 22f33dd..fad5536 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -14,6 +14,8 @@
#include "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
+struct vlan_description;
+
#define MAX_OWN_IE_OVERRIDE 256
#ifdef _MSC_VER
@@ -77,6 +79,14 @@
#define FT_RRB_PMK_R1 10 /* PMK_LEN */
#define FT_RRB_PAIRWISE 11 /* le16 */
+#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */
+
+#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */
+#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */
+
+#define FT_RRB_IDENTITY 15
+#define FT_RRB_RADIUS_CUI 16
+#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */
struct ft_rrb_tlv {
le16 type;
@@ -92,6 +102,8 @@
/* session TLVs:
* required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI,
+ * SESSION_TIMEOUT
*
* pull frame TLVs:
* auth:
@@ -177,6 +189,7 @@
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
int group_mgmt_cipher;
+ int sae_require_mfp;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R_AP
u8 ssid[SSID_MAX_LEN];
@@ -185,11 +198,12 @@
u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
size_t r0_key_holder_len;
u8 r1_key_holder[FT_R1KH_ID_LEN];
- u32 r0_key_lifetime;
+ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
int rkh_pos_timeout;
int rkh_neg_timeout;
int rkh_pull_timeout; /* ms */
int rkh_pull_retries;
+ int r1_max_key_lifetime;
u32 reassociation_deadline;
struct ft_remote_r0kh **r0kh_list;
struct ft_remote_r1kh **r1kh_list;
@@ -253,6 +267,20 @@
size_t data_len);
#ifdef CONFIG_IEEE80211R_AP
struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
+ int (*set_vlan)(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan);
+ int (*get_vlan)(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan);
+ int (*set_identity)(void *ctx, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len);
+ size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf);
+ int (*set_radius_cui)(void *ctx, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len);
+ size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf);
+ void (*set_session_timeout)(void *ctx, const u8 *sta_addr,
+ int session_timeout);
+ int (*get_session_timeout)(void *ctx, const u8 *sta_addr);
+
int (*send_ft_action)(void *ctx, const u8 *dst,
const u8 *data, size_t data_len);
int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
@@ -331,6 +359,7 @@
struct eapol_state_machine *eapol);
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, const u8 *pmkid);
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
int session_timeout, int akmp);
@@ -420,7 +449,8 @@
int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
size_t ies_len);
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, u8 *buf, size_t len);
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+ u8 *buf, size_t len);
void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
u8 *fils_anonce, u8 *fils_snonce,
u8 *fils_kek, size_t *fils_kek_len);
@@ -437,5 +467,6 @@
int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
void (*cb)(void *ctx1, void *ctx2),
void *ctx1, void *ctx2);
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 153752d..e8d46ab 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -16,6 +16,7 @@
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "crypto/aes_wrap.h"
+#include "crypto/sha384.h"
#include "crypto/random.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -192,6 +193,98 @@
}
+static int cmp_int(const void *a, const void *b)
+{
+ int x, y;
+
+ x = *((int *) a);
+ y = *((int *) b);
+ return x - y;
+}
+
+
+static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
+ struct vlan_description *vlan)
+{
+ struct ft_rrb_tlv *f;
+ size_t left;
+ size_t len;
+ int taggedidx;
+ int vlan_id;
+ int type;
+
+ left = plain_len;
+ taggedidx = 0;
+ os_memset(vlan, 0, sizeof(*vlan));
+
+ while (left >= sizeof(*f)) {
+ f = (struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+
+ len = le_to_host16(f->len);
+ type = le_to_host16(f->type);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+ return -1;
+ }
+
+ if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
+ goto skip;
+
+ if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN_UNTAGGED invalid length");
+ return -1;
+ }
+
+ if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN_TAGGED invalid length");
+ return -1;
+ }
+
+ while (len >= sizeof(le16)) {
+ vlan_id = WPA_GET_LE16(plain);
+ plain += sizeof(le16);
+ left -= sizeof(le16);
+ len -= sizeof(le16);
+
+ if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN ID invalid %d",
+ vlan_id);
+ continue;
+ }
+
+ if (type == FT_RRB_VLAN_UNTAGGED)
+ vlan->untagged = vlan_id;
+
+ if (type == FT_RRB_VLAN_TAGGED &&
+ taggedidx < MAX_NUM_TAGGED_VLAN) {
+ vlan->tagged[taggedidx] = vlan_id;
+ taggedidx++;
+ } else if (type == FT_RRB_VLAN_TAGGED) {
+ wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
+ }
+ }
+
+ skip:
+ left -= len;
+ plain += len;
+ }
+
+ if (taggedidx)
+ qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
+
+ vlan->notempty = vlan->untagged || vlan->tagged[0];
+
+ return 0;
+}
+
+
static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
{
size_t tlv_len = 0;
@@ -233,6 +326,8 @@
if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
return tlv_len;
+ if (tlvs[i].len == 0)
+ continue;
tlv_len += tlvs[i].len;
os_memcpy(pos, tlvs[i].data, tlvs[i].len);
pos = start + tlv_len;
@@ -242,8 +337,84 @@
}
+static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
+{
+ size_t tlv_len = 0;
+ int i;
+
+ if (!vlan || !vlan->notempty)
+ return 0;
+
+ if (vlan->untagged) {
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ tlv_len += sizeof(le16);
+ }
+ if (vlan->tagged[0])
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
+ tlv_len += sizeof(le16);
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
+ u8 *start, u8 *endpos)
+{
+ size_t tlv_len;
+ int i, len;
+ struct ft_rrb_tlv *hdr;
+ u8 *pos = start;
+
+ if (!vlan || !vlan->notempty)
+ return 0;
+
+ tlv_len = 0;
+ if (vlan->untagged) {
+ tlv_len += sizeof(*hdr);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
+ hdr->len = host_to_le16(sizeof(le16));
+ pos = start + tlv_len;
+
+ tlv_len += sizeof(le16);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ WPA_PUT_LE16(pos, vlan->untagged);
+ pos = start + tlv_len;
+ }
+
+ if (!vlan->tagged[0])
+ return tlv_len;
+
+ tlv_len += sizeof(*hdr);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
+ len = 0; /* len is computed below */
+ pos = start + tlv_len;
+
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
+ tlv_len += sizeof(le16);
+ if (start + tlv_len > endpos)
+ break;
+ len += sizeof(le16);
+ WPA_PUT_LE16(pos, vlan->tagged[i]);
+ pos = start + tlv_len;
+ }
+
+ hdr->len = host_to_le16(len);
+
+ return tlv_len;
+}
+
+
static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
const struct tlv_list *tlvs2,
+ const struct vlan_description *vlan,
u8 **plain, size_t *plain_len)
{
u8 *pos, *endpos;
@@ -251,6 +422,7 @@
tlv_len = wpa_ft_tlv_len(tlvs1);
tlv_len += wpa_ft_tlv_len(tlvs2);
+ tlv_len += wpa_ft_vlan_len(vlan);
*plain_len = tlv_len;
*plain = os_zalloc(tlv_len);
@@ -263,6 +435,7 @@
endpos = *plain + tlv_len;
pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+ pos += wpa_ft_vlan_lin(vlan, pos, endpos);
/* sanity check */
if (pos != endpos) {
@@ -324,6 +497,7 @@
const struct tlv_list *tlvs_enc0,
const struct tlv_list *tlvs_enc1,
const struct tlv_list *tlvs_auth,
+ const struct vlan_description *vlan,
const u8 *src_addr, u8 type,
u8 **packet, size_t *packet_len)
{
@@ -331,10 +505,11 @@
size_t plain_len = 0, auth_len = 0;
int ret = -1;
- if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, &plain, &plain_len) < 0)
+ *packet = NULL;
+ if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
goto out;
- if (wpa_ft_rrb_lin(tlvs_auth, NULL, &auth, &auth_len) < 0)
+ if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
goto out;
*packet_len = sizeof(u16) + auth_len + plain_len;
@@ -456,6 +631,89 @@
}
+static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, struct vlan_description *vlan)
+{
+ if (!wpa_auth->cb->set_vlan)
+ return -1;
+ return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, struct vlan_description *vlan)
+{
+ if (!wpa_auth->cb->get_vlan)
+ return -1;
+ return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int
+wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len)
+{
+ if (!wpa_auth->cb->set_identity)
+ return -1;
+ return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
+ identity_len);
+}
+
+
+static size_t
+wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 **buf)
+{
+ *buf = NULL;
+ if (!wpa_auth->cb->get_identity)
+ return 0;
+ return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static int
+wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ if (!wpa_auth->cb->set_radius_cui)
+ return -1;
+ return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
+ radius_cui, radius_cui_len);
+}
+
+
+static size_t
+wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 **buf)
+{
+ *buf = NULL;
+ if (!wpa_auth->cb->get_radius_cui)
+ return 0;
+ return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static void
+wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, int session_timeout)
+{
+ if (!wpa_auth->cb->set_session_timeout)
+ return;
+ wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
+ session_timeout);
+}
+
+
+static int
+wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr)
+{
+ if (!wpa_auth->cb->get_session_timeout)
+ return 0;
+ return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
+}
+
+
static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr,
u8 *tspec_ie, size_t tspec_ielen)
@@ -489,30 +747,44 @@
}
-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
- size_t r0kh_id_len,
+int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+ const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len)
{
u8 *pos = buf, *ielen;
- struct rsn_ftie *hdr;
+ size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
+ sizeof(struct rsn_ftie);
- if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+ if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
subelem_len)
return -1;
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ielen = pos++;
- hdr = (struct rsn_ftie *) pos;
- os_memset(hdr, 0, sizeof(*hdr));
- pos += sizeof(*hdr);
- WPA_PUT_LE16(hdr->mic_control, 0);
- if (anonce)
- os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
- if (snonce)
- os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ WPA_PUT_LE16(hdr->mic_control, 0);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ WPA_PUT_LE16(hdr->mic_control, 0);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ }
/* Optional Parameters */
*pos++ = FTIE_SUBELEM_R1KH_ID;
@@ -656,7 +928,7 @@
seq_req_auth[0].data = item->nonce;
- if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth,
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
&packet, &packet_len) < 0) {
item = NULL; /* some other seq resp might still accept this */
@@ -834,34 +1106,136 @@
struct wpa_ft_pmk_r0_sa {
- struct wpa_ft_pmk_r0_sa *next;
- u8 pmk_r0[PMK_LEN];
+ struct dl_list list;
+ u8 pmk_r0[PMK_LEN_MAX];
+ size_t pmk_r0_len;
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
u8 spa[ETH_ALEN];
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
- /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+ struct vlan_description *vlan;
+ os_time_t expiration; /* 0 for no expiration */
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_cui;
+ size_t radius_cui_len;
+ os_time_t session_timeout; /* 0 for no expiration */
+ /* TODO: radius_class, EAP type */
int pmk_r1_pushed;
};
struct wpa_ft_pmk_r1_sa {
- struct wpa_ft_pmk_r1_sa *next;
- u8 pmk_r1[PMK_LEN];
+ struct dl_list list;
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 spa[ETH_ALEN];
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
- /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+ struct vlan_description *vlan;
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_cui;
+ size_t radius_cui_len;
+ os_time_t session_timeout; /* 0 for no expiration */
+ /* TODO: radius_class, EAP type */
};
struct wpa_ft_pmk_cache {
- struct wpa_ft_pmk_r0_sa *pmk_r0;
- struct wpa_ft_pmk_r1_sa *pmk_r1;
+ struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
+ struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
};
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
+{
+ if (!r0)
+ return;
+
+ dl_list_del(&r0->list);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+
+ os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
+ os_free(r0->vlan);
+ os_free(r0->identity);
+ os_free(r0->radius_cui);
+ os_free(r0);
+}
+
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
+ struct os_reltime now;
+ int expires_in;
+ int session_timeout;
+
+ os_get_reltime(&now);
+
+ if (!r0)
+ return;
+
+ expires_in = r0->expiration - now.sec;
+ session_timeout = r0->session_timeout - now.sec;
+ /* conditions to remove from cache:
+ * a) r0->expiration is set and hit
+ * -or-
+ * b) r0->session_timeout is set and hit
+ */
+ if ((!r0->expiration || expires_in > 0) &&
+ (!r0->session_timeout || session_timeout > 0)) {
+ wpa_printf(MSG_ERROR,
+ "FT: %s() called for non-expired entry %p",
+ __func__, r0);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+ if (r0->expiration && expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+ if (r0->session_timeout && session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+ return;
+ }
+
+ wpa_ft_free_pmk_r0(r0);
+}
+
+
+static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
+{
+ if (!r1)
+ return;
+
+ dl_list_del(&r1->list);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
+
+ os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
+ os_free(r1->vlan);
+ os_free(r1->identity);
+ os_free(r1->radius_cui);
+ os_free(r1);
+}
+
+
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
+
+ wpa_ft_free_pmk_r1(r1);
+}
+
+
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
{
struct wpa_ft_pmk_cache *cache;
cache = os_zalloc(sizeof(*cache));
+ if (cache) {
+ dl_list_init(&cache->pmk_r0);
+ dl_list_init(&cache->pmk_r1);
+ }
return cache;
}
@@ -872,46 +1246,77 @@
struct wpa_ft_pmk_r0_sa *r0, *r0prev;
struct wpa_ft_pmk_r1_sa *r1, *r1prev;
- r0 = cache->pmk_r0;
- while (r0) {
- r0prev = r0;
- r0 = r0->next;
- os_memset(r0prev->pmk_r0, 0, PMK_LEN);
- os_free(r0prev);
- }
+ dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
+ struct wpa_ft_pmk_r0_sa, list)
+ wpa_ft_free_pmk_r0(r0);
- r1 = cache->pmk_r1;
- while (r1) {
- r1prev = r1;
- r1 = r1->next;
- os_memset(r1prev->pmk_r1, 0, PMK_LEN);
- os_free(r1prev);
- }
+ dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
+ struct wpa_ft_pmk_r1_sa, list)
+ wpa_ft_free_pmk_r1(r1);
os_free(cache);
}
-int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
- const u8 *spa, const u8 *pmk_r0,
- const u8 *pmk_r0_name, int pairwise)
+static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r0,
+ size_t pmk_r0_len,
+ const u8 *pmk_r0_name, int pairwise,
+ const struct vlan_description *vlan,
+ int expires_in, int session_timeout,
+ const u8 *identity, size_t identity_len,
+ const u8 *radius_cui, size_t radius_cui_len)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0;
+ struct os_reltime now;
- /* TODO: add expiration and limit on number of entries in cache */
+ /* TODO: add limit on number of entries in cache */
+ os_get_reltime(&now);
r0 = os_zalloc(sizeof(*r0));
if (r0 == NULL)
return -1;
- os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN);
+ os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
+ r0->pmk_r0_len = pmk_r0_len;
os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
os_memcpy(r0->spa, spa, ETH_ALEN);
r0->pairwise = pairwise;
+ if (expires_in > 0)
+ r0->expiration = now.sec + expires_in;
+ if (vlan && vlan->notempty) {
+ r0->vlan = os_zalloc(sizeof(*vlan));
+ if (!r0->vlan) {
+ bin_clear_free(r0, sizeof(*r0));
+ return -1;
+ }
+ *r0->vlan = *vlan;
+ }
+ if (identity) {
+ r0->identity = os_malloc(identity_len);
+ if (r0->identity) {
+ os_memcpy(r0->identity, identity, identity_len);
+ r0->identity_len = identity_len;
+ }
+ }
+ if (radius_cui) {
+ r0->radius_cui = os_malloc(radius_cui_len);
+ if (r0->radius_cui) {
+ os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
+ r0->radius_cui_len = radius_cui_len;
+ }
+ }
+ if (session_timeout > 0)
+ r0->session_timeout = now.sec + session_timeout;
- r0->next = cache->pmk_r0;
- cache->pmk_r0 = r0;
+ dl_list_add(&cache->pmk_r0, &r0->list);
+ if (expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
+ r0, NULL);
+ if (session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
return 0;
}
@@ -923,17 +1328,16 @@
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0;
+ struct os_reltime now;
- r0 = cache->pmk_r0;
- while (r0) {
+ os_get_reltime(&now);
+ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
WPA_PMK_NAME_LEN) == 0) {
*r0_out = r0;
return 0;
}
-
- r0 = r0->next;
}
*r0_out = NULL;
@@ -943,24 +1347,66 @@
static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r1,
- const u8 *pmk_r1_name, int pairwise)
+ size_t pmk_r1_len,
+ const u8 *pmk_r1_name, int pairwise,
+ const struct vlan_description *vlan,
+ int expires_in, int session_timeout,
+ const u8 *identity, size_t identity_len,
+ const u8 *radius_cui, size_t radius_cui_len)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
struct wpa_ft_pmk_r1_sa *r1;
+ struct os_reltime now;
- /* TODO: add expiration and limit on number of entries in cache */
+ /* TODO: limit on number of entries in cache */
+ os_get_reltime(&now);
+
+ if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
+ expires_in = max_expires_in;
r1 = os_zalloc(sizeof(*r1));
if (r1 == NULL)
return -1;
- os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN);
+ os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
+ r1->pmk_r1_len = pmk_r1_len;
os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
os_memcpy(r1->spa, spa, ETH_ALEN);
r1->pairwise = pairwise;
+ if (vlan && vlan->notempty) {
+ r1->vlan = os_zalloc(sizeof(*vlan));
+ if (!r1->vlan) {
+ bin_clear_free(r1, sizeof(*r1));
+ return -1;
+ }
+ *r1->vlan = *vlan;
+ }
+ if (identity) {
+ r1->identity = os_malloc(identity_len);
+ if (r1->identity) {
+ os_memcpy(r1->identity, identity, identity_len);
+ r1->identity_len = identity_len;
+ }
+ }
+ if (radius_cui) {
+ r1->radius_cui = os_malloc(radius_cui_len);
+ if (r1->radius_cui) {
+ os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
+ r1->radius_cui_len = radius_cui_len;
+ }
+ }
+ if (session_timeout > 0)
+ r1->session_timeout = now.sec + session_timeout;
- r1->next = cache->pmk_r1;
- cache->pmk_r1 = r1;
+ dl_list_add(&cache->pmk_r1, &r1->list);
+
+ if (expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
+ r1, NULL);
+ if (session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r1, r1, NULL);
return 0;
}
@@ -968,23 +1414,47 @@
static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r1_name,
- u8 *pmk_r1, int *pairwise)
+ u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui, size_t *radius_cui_len,
+ int *session_timeout)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r1_sa *r1;
+ struct os_reltime now;
- r1 = cache->pmk_r1;
- while (r1) {
+ os_get_reltime(&now);
+
+ dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
WPA_PMK_NAME_LEN) == 0) {
- os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
+ os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
+ *pmk_r1_len = r1->pmk_r1_len;
if (pairwise)
*pairwise = r1->pairwise;
+ if (vlan && r1->vlan)
+ *vlan = *r1->vlan;
+ if (vlan && !r1->vlan)
+ os_memset(vlan, 0, sizeof(*vlan));
+ if (identity && identity_len) {
+ *identity = r1->identity;
+ *identity_len = r1->identity_len;
+ }
+ if (radius_cui && radius_cui_len) {
+ *radius_cui = r1->radius_cui;
+ *radius_cui_len = r1->radius_cui_len;
+ }
+ if (session_timeout && r1->session_timeout > now.sec)
+ *session_timeout = r1->session_timeout -
+ now.sec;
+ else if (session_timeout && r1->session_timeout)
+ *session_timeout = 1;
+ else
+ *session_timeout = 0;
return 0;
}
-
- r1 = r1->next;
}
return -1;
@@ -1459,6 +1929,11 @@
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
+ if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: R0KH-ID points to self - no matching key available");
+ return -1;
+ }
key = r0kh->key;
key_len = sizeof(r0kh->key);
@@ -1486,7 +1961,7 @@
return -1;
}
- if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth,
+ if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
&packet, &packet_len) < 0)
return -1;
@@ -1512,11 +1987,43 @@
}
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
+ const u8 *pmk_r0, const u8 *pmk_r0_name)
+{
+ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len, radius_cui_len;
+ int session_timeout;
+ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
+ SHA384_MAC_LEN : PMK_LEN;
+
+ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+ MAC2STR(sm->addr));
+ return -1;
+ }
+
+ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+ &radius_cui);
+ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+ return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+ pmk_r0_name, sm->pairwise, &vlan, expires_in,
+ session_timeout, identity, identity_len,
+ radius_cui, radius_cui_len);
+}
+
+
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
struct wpa_ptk *ptk)
{
- u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 pmk_r1[PMK_LEN];
+ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
+ SHA384_MAC_LEN : PMK_LEN;
+ size_t pmk_r1_len = pmk_r0_len;
+ u8 pmk_r1[PMK_LEN_MAX];
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
@@ -1525,6 +2032,11 @@
const u8 *ssid = sm->wpa_auth->conf.ssid;
size_t ssid_len = sm->wpa_auth->conf.ssid_len;
int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
+ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len, radius_cui_len;
+ int session_timeout;
if (sm->xxkey_len == 0) {
wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -1532,28 +2044,45 @@
return -1;
}
+ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+ MAC2STR(sm->addr));
+ return -1;
+ }
+
+ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+ &radius_cui);
+ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
r0kh, r0kh_len, sm->addr,
- pmk_r0, pmk_r0_name) < 0)
+ pmk_r0, pmk_r0_name,
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
- wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
- sm->pairwise);
+ wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+ pmk_r0_name,
+ sm->pairwise, &vlan, expires_in,
+ session_timeout, identity, identity_len,
+ radius_cui, radius_cui_len);
- if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+ if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
pmk_r1, sm->pmk_r1_name) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
WPA_PMK_NAME_LEN);
if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
- wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1,
- sm->pmk_r1_name, sm->pairwise);
+ wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
+ sm->pmk_r1_name, sm->pairwise, &vlan,
+ expires_in, session_timeout, identity,
+ identity_len, radius_cui, radius_cui_len);
- return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
- sm->wpa_auth->addr, sm->pmk_r1_name,
+ return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
}
@@ -1575,6 +2104,16 @@
const u8 *key;
size_t key_len;
u8 keybuf[32];
+ const u8 *kek;
+ size_t kek_len;
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
key_len = gsm->GTK_len;
if (key_len > sizeof(keybuf))
@@ -1613,8 +2152,10 @@
WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
subelem[4] = gsm->GTK_len;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
- subelem + 13)) {
+ if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: GTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
os_free(subelem);
return NULL;
}
@@ -1630,10 +2171,23 @@
u8 *subelem, *pos;
struct wpa_group *gsm = sm->group;
size_t subelem_len;
+ const u8 *kek;
+ size_t kek_len;
+ size_t igtk_len;
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
+
+ igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
* Key[16+8] */
- subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
+ subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
subelem = os_zalloc(subelem_len);
if (subelem == NULL)
return NULL;
@@ -1645,9 +2199,12 @@
pos += 2;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
pos += 6;
- *pos++ = WPA_IGTK_LEN;
- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
+ *pos++ = igtk_len;
+ if (aes_wrap(kek, kek_len, igtk_len / 8,
gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: IGTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
os_free(subelem);
return NULL;
}
@@ -1794,17 +2351,21 @@
const u8 *req_ies, size_t req_ies_len)
{
u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+ u8 *fte_mic, *elem_count;
size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
int res;
struct wpa_auth_config *conf;
- struct rsn_ftie *_ftie;
struct wpa_ft_ies parse;
u8 *ric_start;
u8 *anonce, *snonce;
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384;
if (sm == NULL)
return pos;
+ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
conf = &sm->wpa_auth->conf;
if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
@@ -1819,7 +2380,7 @@
*/
res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
if (res < 0)
- return pos;
+ return NULL;
rsnie = pos;
rsnie_len = res;
pos += res;
@@ -1828,7 +2389,7 @@
/* Mobility Domain Information */
res = wpa_write_mdie(conf, pos, end - pos);
if (res < 0)
- return pos;
+ return NULL;
mdie = pos;
mdie_len = res;
pos += res;
@@ -1836,6 +2397,11 @@
/* Fast BSS Transition Information */
if (auth_alg == WLAN_AUTH_FT) {
subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+ if (!subelem) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add GTK subelement");
+ return NULL;
+ }
r0kh_id = sm->r0kh_id;
r0kh_id_len = sm->r0kh_id_len;
anonce = sm->ANonce;
@@ -1847,14 +2413,16 @@
u8 *nbuf;
igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
if (igtk == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add IGTK subelement");
os_free(subelem);
- return pos;
+ return NULL;
}
nbuf = os_realloc(subelem, subelem_len + igtk_len);
if (nbuf == NULL) {
os_free(subelem);
os_free(igtk);
- return pos;
+ return NULL;
}
subelem = nbuf;
os_memcpy(subelem + subelem_len, igtk, igtk_len);
@@ -1868,44 +2436,66 @@
anonce = NULL;
snonce = NULL;
}
- res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
- end - pos, subelem, subelem_len);
+ res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
+ anonce, snonce, pos, end - pos,
+ subelem, subelem_len);
os_free(subelem);
if (res < 0)
- return pos;
+ return NULL;
ftie = pos;
ftie_len = res;
pos += res;
- _ftie = (struct rsn_ftie *) (ftie + 2);
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *_ftie =
+ (struct rsn_ftie_sha384 *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ } else {
+ struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ }
if (auth_alg == WLAN_AUTH_FT)
- _ftie->mic_control[1] = 3; /* Information element count */
+ *elem_count = 3; /* Information element count */
ric_start = pos;
- if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
+ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
+ && parse.ric) {
pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
parse.ric_len);
if (auth_alg == WLAN_AUTH_FT)
- _ftie->mic_control[1] +=
+ *elem_count +=
ieee802_11_ie_count(ric_start,
pos - ric_start);
}
if (ric_start == pos)
ric_start = NULL;
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kck = sm->PTK.kck2;
+ kck_len = sm->PTK.kck2_len;
+ } else {
+ kck = sm->PTK.kck;
+ kck_len = sm->PTK.kck_len;
+ }
if (auth_alg == WLAN_AUTH_FT &&
- wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
- sm->wpa_auth->addr, 6,
+ wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
mdie, mdie_len, ftie, ftie_len,
rsnie, rsnie_len,
ric_start, ric_start ? pos - ric_start : 0,
- _ftie->mic) < 0)
+ fte_mic) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return NULL;
+ }
os_free(sm->assoc_resp_ftie);
sm->assoc_resp_ftie = os_malloc(ftie_len);
- if (sm->assoc_resp_ftie)
- os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+ if (!sm->assoc_resp_ftie)
+ return NULL;
+ os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
return pos;
}
@@ -1964,7 +2554,12 @@
/* Derive PMK-R1 from PSK, check all available PSK */
static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
const u8 *req_pmk_r1_name,
- u8 *out_pmk_r1, int *out_pairwise)
+ u8 *out_pmk_r1, int *out_pairwise,
+ struct vlan_description *out_vlan,
+ const u8 **out_identity, size_t *out_identity_len,
+ const u8 **out_radius_cui,
+ size_t *out_radius_cui_len,
+ int *out_session_timeout)
{
const u8 *pmk = NULL;
u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
@@ -1988,9 +2583,9 @@
if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
r0kh_len, sm->addr,
- pmk_r0, pmk_r0_name) < 0 ||
- wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
- pmk_r1, pmk_r1_name) < 0 ||
+ pmk_r0, pmk_r0_name, 0) < 0 ||
+ wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
+ sm->addr, pmk_r1, pmk_r1_name) < 0 ||
os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
WPA_PMK_NAME_LEN) != 0)
continue;
@@ -2001,6 +2596,28 @@
os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
if (out_pairwise)
*out_pairwise = pairwise;
+ if (out_vlan &&
+ wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
+ MACSTR, MAC2STR(sm->addr));
+ return -1;
+ }
+
+ if (out_identity && out_identity_len) {
+ *out_identity_len = wpa_ft_get_identity(
+ sm->wpa_auth, sm->addr, out_identity);
+ }
+
+ if (out_radius_cui && out_radius_cui_len) {
+ *out_radius_cui_len = wpa_ft_get_radius_cui(
+ sm->wpa_auth, sm->addr, out_radius_cui);
+ }
+
+ if (out_session_timeout) {
+ *out_session_timeout = wpa_ft_get_session_timeout(
+ sm->wpa_auth, sm->addr);
+ }
+
return 0;
}
@@ -2029,6 +2646,10 @@
}
if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
#ifdef CONFIG_FILS
@@ -2050,20 +2671,99 @@
}
+static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *req_pmk_r0_name,
+ const u8 *req_pmk_r1_name,
+ u8 *out_pmk_r1, int *out_pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui,
+ size_t *radius_cui_len,
+ int *out_session_timeout)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ const struct wpa_ft_pmk_r0_sa *r0;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ int expires_in = 0;
+ int session_timeout = 0;
+ struct os_reltime now;
+
+ if (conf->r0_key_holder_len != r0kh_id_len ||
+ os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
+ 0)
+ return -1; /* not our R0KH-ID */
+
+ wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
+ if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
+ 0)
+ return -1; /* no matching PMKR0Name in local cache */
+
+ wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
+
+ if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
+ conf->r1_key_holder,
+ sm->addr, out_pmk_r1, pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ os_get_reltime(&now);
+ if (r0->expiration)
+ expires_in = r0->expiration - now.sec;
+
+ if (r0->session_timeout)
+ session_timeout = r0->session_timeout - now.sec;
+
+ wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
+ pmk_r1_name,
+ sm->pairwise, r0->vlan, expires_in, session_timeout,
+ r0->identity, r0->identity_len,
+ r0->radius_cui, r0->radius_cui_len);
+
+ *out_pairwise = sm->pairwise;
+ if (vlan) {
+ if (r0->vlan)
+ *vlan = *r0->vlan;
+ else
+ os_memset(vlan, 0, sizeof(*vlan));
+ }
+
+ if (identity && identity_len) {
+ *identity = r0->identity;
+ *identity_len = r0->identity_len;
+ }
+
+ if (radius_cui && radius_cui_len) {
+ *radius_cui = r0->radius_cui;
+ *radius_cui_len = r0->radius_cui_len;
+ }
+
+ *out_session_timeout = session_timeout;
+
+ return 0;
+}
+
+
static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
u8 **resp_ies, size_t *resp_ies_len)
{
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
- u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
u8 ptk_name[WPA_PMK_NAME_LEN];
struct wpa_auth_config *conf;
struct wpa_ft_ies parse;
size_t buflen;
int ret;
u8 *pos, *end;
- int pairwise;
+ int pairwise, session_timeout = 0;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len = 0, radius_cui_len = 0;
+ int use_sha384;
+ size_t pmk_r1_len;
*resp_ies = NULL;
*resp_ies_len = 0;
@@ -2074,10 +2774,12 @@
wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
+ use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
+ pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
mdie = (struct rsn_mdie *) parse.mdie;
if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
@@ -2088,13 +2790,27 @@
return WLAN_STATUS_INVALID_MDIE;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
- }
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
- os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ }
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
@@ -2118,17 +2834,38 @@
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) < 0)
+ pmk_r1_name, use_sha384) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
pmk_r1_name, WPA_PMK_NAME_LEN);
if (conf->ft_psk_generate_local &&
wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
- if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise) < 0)
+ if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout) < 0)
return WLAN_STATUS_INVALID_PMKID;
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 for FT-PSK locally");
} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
- pmk_r1, &pairwise) < 0) {
+ pmk_r1, &pmk_r1_len, &pairwise, &vlan,
+ &identity, &identity_len, &radius_cui,
+ &radius_cui_len, &session_timeout) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
+ if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
+ parse.r0kh_id, parse.r0kh_id_len,
+ parse.rsn_pmkid,
+ pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 based on local PMK-R0");
+ goto pmk_r1_derived;
+ }
+
if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
wpa_printf(MSG_DEBUG,
"FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
@@ -2136,9 +2873,12 @@
}
return -1; /* Status pending */
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
}
- wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
+pmk_r1_derived:
+ wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
sm->pmk_r1_name_valid = 1;
os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
@@ -2153,8 +2893,8 @@
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
sm->ANonce, WPA_NONCE_LEN);
- if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
- sm->wpa_auth->addr, pmk_r1_name,
+ if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr, pmk_r1_name,
&sm->PTK, ptk_name, sm->wpa_key_mgmt,
pairwise) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2164,6 +2904,19 @@
sm->tk_already_set = FALSE;
wpa_ft_install_ptk(sm);
+ if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
+ identity, identity_len) < 0 ||
+ wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
+ radius_cui, radius_cui_len) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
+
buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
2 + FT_R1KH_ID_LEN + 200;
*resp_ies = os_zalloc(buflen);
@@ -2183,7 +2936,7 @@
goto fail;
pos += ret;
- ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
+ ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
if (ret < 0)
goto fail;
@@ -2247,17 +3000,23 @@
{
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
size_t mic_len = 16;
unsigned int count;
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384;
+ const u8 *anonce, *snonce, *fte_mic;
+ u8 fte_elem_count;
if (sm == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -2288,25 +3047,47 @@
return WLAN_STATUS_INVALID_MDIE;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
}
- if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- ftie->snonce, WPA_NONCE_LEN);
+ snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->SNonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
- if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- ftie->anonce, WPA_NONCE_LEN);
+ anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->ANonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
@@ -2357,15 +3138,21 @@
count = 3;
if (parse.ric)
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
- if (ftie->mic_control[1] != count) {
+ if (fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- ftie->mic_control[1], count);
+ fte_elem_count, count);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
- sm->wpa_auth->addr, 5,
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kck = sm->PTK.kck2;
+ kck_len = sm->PTK.kck2_len;
+ } else {
+ kck = sm->PTK.kck;
+ kck_len = sm->PTK.kck_len;
+ }
+ if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
@@ -2375,12 +3162,12 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
+ if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
- ftie->mic, mic_len);
+ fte_mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
parse.mdie - 2, parse.mdie_len + 2);
@@ -2579,33 +3366,65 @@
const u8 *src_addr, u8 type,
u8 **packet, size_t *packet_len)
{
- u8 pmk_r1[PMK_LEN];
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len = pmk_r0->pmk_r0_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 f_pairwise[sizeof(le16)];
+ u8 f_expires_in[sizeof(le16)];
+ u8 f_session_timeout[sizeof(le32)];
+ int expires_in;
+ int session_timeout;
+ struct os_reltime now;
int ret;
struct tlv_list sess_tlv[] = {
- { .type = FT_RRB_PMK_R1, .len = sizeof(pmk_r1),
+ { .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
.data = pmk_r1 },
{ .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
.data = pmk_r1_name },
{ .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
.data = f_pairwise },
+ { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
+ .data = f_expires_in },
+ { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
+ .data = pmk_r0->identity },
+ { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
+ .data = pmk_r0->radius_cui },
+ { .type = FT_RRB_SESSION_TIMEOUT,
+ .len = sizeof(f_session_timeout),
+ .data = f_session_timeout },
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
- if (!pmk_r0)
- return wpa_ft_rrb_build(key, key_len, tlvs, NULL, tlv_auth,
- src_addr, type, packet, packet_len);
-
- if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id,
+ if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
+ pmk_r0->pmk_r0_name, r1kh_id,
s1kh_id, pmk_r1, pmk_r1_name) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)",
+ pmk_r1, pmk_r1_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)",
+ pmk_r1_name, WPA_PMK_NAME_LEN);
WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
+ os_get_reltime(&now);
+ if (pmk_r0->expiration > now.sec)
+ expires_in = pmk_r0->expiration - now.sec;
+ else if (pmk_r0->expiration)
+ expires_in = 1;
+ else
+ expires_in = 0;
+ WPA_PUT_LE16(f_expires_in, expires_in);
+
+ if (pmk_r0->session_timeout > now.sec)
+ session_timeout = pmk_r0->session_timeout - now.sec;
+ else if (pmk_r0->session_timeout)
+ session_timeout = 1;
+ else
+ session_timeout = 0;
+ WPA_PUT_LE32(f_session_timeout, session_timeout);
+
ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
- src_addr, type, packet, packet_len);
+ pmk_r0->vlan, src_addr, type,
+ packet, packet_len);
os_memset(pmk_r1, 0, sizeof(pmk_r1));
@@ -2736,13 +3555,18 @@
resp_auth[4].len = 0;
resp_auth[4].data = NULL;
- if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0)
+ if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
-
- ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id,
- resp_auth, wpa_auth->addr,
- FT_PACKET_R0KH_R1KH_RESP,
- &packet, &packet_len);
+ ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
+ NULL, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_RESP,
+ &packet, &packet_len);
+ } else {
+ ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
+ f_s1kh_id, resp_auth, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_RESP,
+ &packet, &packet_len);
+ }
if (!ret)
wpa_ft_rrb_oui_send(wpa_auth, src_addr,
@@ -2780,10 +3604,20 @@
int seq_ret;
const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+ const u8 *f_expires_in;
size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+ const u8 *f_identity, *f_radius_cui;
+ const u8 *f_session_timeout;
size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+ size_t f_expires_in_len;
+ size_t f_identity_len, f_radius_cui_len;
+ size_t f_session_timeout_len;
int pairwise;
int ret = -1;
+ int expires_in;
+ int session_timeout;
+ struct vlan_description vlan;
+ size_t pmk_r1_len;
RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
@@ -2862,13 +3696,58 @@
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
f_pmk_r1_name, WPA_PMK_NAME_LEN);
- RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, PMK_LEN);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, PMK_LEN);
+ pmk_r1_len = PMK_LEN;
+ if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
+ &f_pmk_r1) == 0 &&
+ (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN))
+ pmk_r1_len = f_pmk_r1_len;
+ RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
pairwise = WPA_GET_LE16(f_pairwise);
- if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, f_pmk_r1_name,
- pairwise) < 0)
+ RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
+ sizeof(le16));
+ if (f_expires_in)
+ expires_in = WPA_GET_LE16(f_expires_in);
+ else
+ expires_in = 0;
+
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
+ expires_in);
+
+ if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
+ wpa_ft_rrb_dump(plain, plain_len);
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
+ le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
+
+ RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
+ if (f_identity)
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
+ f_identity_len);
+
+ RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
+ if (f_radius_cui)
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
+ f_radius_cui_len);
+
+ RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
+ sizeof(le32));
+ if (f_session_timeout)
+ session_timeout = WPA_GET_LE32(f_session_timeout);
+ else
+ session_timeout = 0;
+ wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
+
+ if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
+ f_pmk_r1_name,
+ pairwise, &vlan, expires_in, session_timeout,
+ f_identity, f_identity_len, f_radius_cui,
+ f_radius_cui_len) < 0)
goto out;
ret = 0;
@@ -3178,7 +4057,7 @@
seq_resp_auth[4].len = 0;
seq_resp_auth[4].data = NULL;
- if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth,
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
&packet, &packet_len) < 0)
goto out;
@@ -3527,7 +4406,8 @@
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
{
- struct wpa_ft_pmk_r0_sa *r0;
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
struct ft_remote_r1kh *r1kh;
if (!wpa_auth->conf.pmk_r1_push)
@@ -3535,13 +4415,14 @@
if (!wpa_auth->conf.r1kh_list)
return;
- r0 = wpa_auth->ft_pmk_cache->pmk_r0;
- while (r0) {
- if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
+ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+ if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+ r0found = r0;
break;
- r0 = r0->next;
+ }
}
+ r0 = r0found;
if (r0 == NULL || r0->pmk_r1_pushed)
return;
r0->pmk_r1_pushed = 1;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 98133a0..8127403 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -59,6 +59,7 @@
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
+ wconf->sae_require_mfp = conf->sae_require_mfp;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R_AP
wconf->ssid_len = conf->ssid.ssid_len;
@@ -75,6 +76,7 @@
}
os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
wconf->r0_key_lifetime = conf->r0_key_lifetime;
+ wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime;
wconf->reassociation_deadline = conf->reassociation_deadline;
wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
@@ -364,10 +366,10 @@
sta->last_tk_len = key_len;
}
#ifdef CONFIG_IEEE80211W
- } else if (alg == WPA_CIPHER_AES_128_CMAC ||
- alg == WPA_CIPHER_BIP_GMAC_128 ||
- alg == WPA_CIPHER_BIP_GMAC_256 ||
- alg == WPA_CIPHER_BIP_CMAC_256) {
+ } else if (alg == WPA_ALG_IGTK ||
+ alg == WPA_ALG_BIP_GMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_256 ||
+ alg == WPA_ALG_BIP_CMAC_256) {
hapd->last_igtk_alg = alg;
hapd->last_igtk_key_idx = idx;
if (key)
@@ -834,6 +836,244 @@
}
+static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta || !sta->wpa_sm)
+ return -1;
+
+ if (vlan->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from FT",
+ vlan->untagged, vlan->tagged[0] ? "+" : "");
+ return -1;
+ }
+
+ if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+ return -1;
+ /* Configure wpa_group for GTK but ignore error due to driver not
+ * knowing this STA. */
+ ap_sta_bind_vlan(hapd, sta);
+
+ if (sta->vlan_id)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ if (sta->vlan_desc)
+ *vlan = *sta->vlan_desc;
+ else
+ os_memset(vlan, 0, sizeof(*vlan));
+
+ return 0;
+}
+
+
+static int
+hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ os_free(sta->identity);
+ sta->identity = NULL;
+
+ if (sta->eapol_sm) {
+ os_free(sta->eapol_sm->identity);
+ sta->eapol_sm->identity = NULL;
+ sta->eapol_sm->identity_len = 0;
+ }
+
+ if (!identity_len)
+ return 0;
+
+ /* sta->identity is NULL terminated */
+ sta->identity = os_zalloc(identity_len + 1);
+ if (!sta->identity)
+ return -1;
+ os_memcpy(sta->identity, identity, identity_len);
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->identity = os_zalloc(identity_len);
+ if (!sta->eapol_sm->identity)
+ return -1;
+ os_memcpy(sta->eapol_sm->identity, identity, identity_len);
+ sta->eapol_sm->identity_len = identity_len;
+ }
+
+ return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ size_t len;
+ char *identity;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return 0;
+
+ *buf = ieee802_1x_get_identity(sta->eapol_sm, &len);
+ if (*buf && len)
+ return len;
+
+ if (!sta->identity) {
+ *buf = NULL;
+ return 0;
+ }
+
+ identity = sta->identity;
+ len = os_strlen(identity);
+ *buf = (u8 *) identity;
+
+ return len;
+}
+
+
+static int
+hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ os_free(sta->radius_cui);
+ sta->radius_cui = NULL;
+
+ if (sta->eapol_sm) {
+ wpabuf_free(sta->eapol_sm->radius_cui);
+ sta->eapol_sm->radius_cui = NULL;
+ }
+
+ if (!radius_cui)
+ return 0;
+
+ /* sta->radius_cui is NULL terminated */
+ sta->radius_cui = os_zalloc(radius_cui_len + 1);
+ if (!sta->radius_cui)
+ return -1;
+ os_memcpy(sta->radius_cui, radius_cui, radius_cui_len);
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+ radius_cui_len);
+ if (!sta->eapol_sm->radius_cui)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct wpabuf *b;
+ size_t len;
+ char *radius_cui;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return 0;
+
+ b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+ if (b) {
+ len = wpabuf_len(b);
+ *buf = wpabuf_head(b);
+ return len;
+ }
+
+ if (!sta->radius_cui) {
+ *buf = NULL;
+ return 0;
+ }
+
+ radius_cui = sta->radius_cui;
+ len = os_strlen(radius_cui);
+ *buf = (u8 *) radius_cui;
+
+ return len;
+}
+
+
+static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr,
+ int session_timeout)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return;
+
+ if (session_timeout) {
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
+ sta->session_timeout_set = 1;
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ } else {
+ sta->session_timeout_set = 0;
+ ap_sta_no_session_timeout(hapd, sta);
+ }
+}
+
+
+static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct os_reltime now, remaining;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta || !sta->session_timeout_set)
+ return 0;
+
+ os_get_reltime(&now);
+ if (os_reltime_before(&sta->session_timeout, &now)) {
+ /* already expired, return >0 as timeout was set */
+ return 1;
+ }
+
+ os_reltime_sub(&sta->session_timeout, &now, &remaining);
+
+ return (remaining.sec > 0) ? remaining.sec : 1;
+}
+
+
static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
@@ -953,6 +1193,14 @@
.send_ft_action = hostapd_wpa_auth_send_ft_action,
.add_sta = hostapd_wpa_auth_add_sta,
.add_tspec = hostapd_wpa_auth_add_tspec,
+ .set_vlan = hostapd_wpa_auth_set_vlan,
+ .get_vlan = hostapd_wpa_auth_get_vlan,
+ .set_identity = hostapd_wpa_auth_set_identity,
+ .get_identity = hostapd_wpa_auth_get_identity,
+ .set_radius_cui = hostapd_wpa_auth_set_radius_cui,
+ .get_radius_cui = hostapd_wpa_auth_get_radius_cui,
+ .set_session_timeout = hostapd_wpa_auth_set_session_timeout,
+ .get_session_timeout = hostapd_wpa_auth_get_session_timeout,
#endif /* CONFIG_IEEE80211R_AP */
};
const u8 *wpa_ie;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index befa800..b1cea1b 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -58,6 +58,7 @@
u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
u8 PMK[PMK_LEN_MAX];
unsigned int pmk_len;
+ u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
struct wpa_ptk PTK;
Boolean PTK_valid;
Boolean pairwise_set;
@@ -90,6 +91,7 @@
unsigned int pmk_r1_name_valid:1;
#endif /* CONFIG_IEEE80211R_AP */
unsigned int is_wnmsleep:1;
+ unsigned int pmkid_set:1;
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
int req_replay_counter_used;
@@ -110,7 +112,8 @@
u32 dot11RSNAStatsTKIPRemoteMICFailures;
#ifdef CONFIG_IEEE80211R_AP
- u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+ u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
+ * first 384 bits of MSK */
size_t xxkey_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
* Request */
@@ -274,8 +277,8 @@
#ifdef CONFIG_IEEE80211R_AP
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
- size_t r0kh_id_len,
+int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+ const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len);
@@ -284,9 +287,8 @@
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
void wpa_ft_install_ptk(struct wpa_state_machine *sm);
-int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
- const u8 *spa, const u8 *pmk_r0,
- const u8 *pmk_r0_name, int pairwise);
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0,
+ const u8 *pmk_r0_name);
#endif /* CONFIG_IEEE80211R_AP */
#endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 0196d00..421dd5a 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1,6 +1,6 @@
/*
* hostapd - WPA/RSN IE and KDE definitions
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -170,6 +170,13 @@
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+#ifdef CONFIG_SHA384
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_SHA384 */
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
pos += RSN_SELECTOR_LEN;
@@ -248,6 +255,13 @@
num_suites++;
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_HS20 */
#ifdef CONFIG_RSN_TESTING
if (rsn_testing) {
@@ -559,6 +573,10 @@
selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
@@ -588,6 +606,10 @@
else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
selector = RSN_AUTH_KEY_MGMT_DPP;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
+ selector = RSN_AUTH_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
@@ -661,6 +683,10 @@
sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
@@ -688,6 +714,10 @@
else if (key_mgmt & WPA_KEY_MGMT_DPP)
sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (key_mgmt & WPA_KEY_MGMT_OSEN)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
else
sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
@@ -711,12 +741,6 @@
return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
}
- if (ciphers & WPA_CIPHER_TKIP) {
- wpa_printf(MSG_DEBUG, "Management frame protection "
- "cannot use TKIP");
- return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
- }
-
if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
{
wpa_printf(MSG_DEBUG, "Unsupported management group "
@@ -725,11 +749,27 @@
}
}
+#ifdef CONFIG_SAE
+ if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection required with SAE, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+#endif /* CONFIG_SAE */
+
if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
!(data.capabilities & WPA_CAPABILITY_MFPC))
sm->mgmt_frame_prot = 0;
else
sm->mgmt_frame_prot = 1;
+
+ if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection cannot use TKIP");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R_AP
@@ -817,6 +857,15 @@
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
}
+#ifdef CONFIG_SAE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
+ !sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKSA cache entry found for SAE");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_SAE */
+
#ifdef CONFIG_DPP
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
@@ -1018,6 +1067,19 @@
const u8 *req_ies, size_t req_ies_len)
{
int res;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->own_ie_override_len) {
+ if (max_len < conf->own_ie_override_len)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+ conf->own_ie_override, conf->own_ie_override_len);
+ os_memcpy(pos, conf->own_ie_override,
+ conf->own_ie_override_len);
+ return pos + conf->own_ie_override_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len,
sm->pmksa ? sm->pmksa->pmkid : NULL);
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 95b40da..5ec0199 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1064,7 +1064,9 @@
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
wps->auth_types |= WPS_AUTH_WPA2;
- if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) {
+ if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)) {
wps->encr_types |= WPS_ENCR_AES;
wps->encr_types_rsn |= WPS_ENCR_AES;
}
diff --git a/src/common/ctrl_iface_common.c b/src/common/ctrl_iface_common.c
index ebbe6ff..e26407d 100644
--- a/src/common/ctrl_iface_common.c
+++ b/src/common/ctrl_iface_common.c
@@ -113,17 +113,53 @@
}
+static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input)
+{
+ const char *value;
+ int val;
+
+ if (!input)
+ return 0;
+
+ value = os_strchr(input, '=');
+ if (!value)
+ return -1;
+ value++;
+ val = atoi(value);
+ if (val < 0 || val > 1)
+ return -1;
+
+ if (str_starts(input, "probe_rx_events=")) {
+ if (val)
+ dst->events |= WPA_EVENT_RX_PROBE_REQUEST;
+ else
+ dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST;
+ }
+
+ return 0;
+}
+
+
int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
- socklen_t fromlen)
+ socklen_t fromlen, const char *input)
{
struct wpa_ctrl_dst *dst;
+ /* Update event registration if already attached */
+ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
+ if (!sockaddr_compare(from, fromlen,
+ &dst->addr, dst->addrlen))
+ return ctrl_set_events(dst, input);
+ }
+
+ /* New attachment */
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
return -1;
os_memcpy(&dst->addr, from, fromlen);
dst->addrlen = fromlen;
dst->debug_level = MSG_INFO;
+ ctrl_set_events(dst, input);
dl_list_add(ctrl_dst, &dst->list);
sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen);
diff --git a/src/common/ctrl_iface_common.h b/src/common/ctrl_iface_common.h
index 0b6e3e7..85e258e 100644
--- a/src/common/ctrl_iface_common.h
+++ b/src/common/ctrl_iface_common.h
@@ -11,6 +11,9 @@
#include "utils/list.h"
+/* Events enable bits (wpa_ctrl_dst::events) */
+#define WPA_EVENT_RX_PROBE_REQUEST BIT(0)
+
/**
* struct wpa_ctrl_dst - Data structure of control interface monitors
*
@@ -23,13 +26,14 @@
socklen_t addrlen;
int debug_level;
int errors;
+ u32 events; /* WPA_EVENT_* bitmap */
};
void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
socklen_t socklen);
int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
- socklen_t fromlen);
+ socklen_t fromlen, const char *input);
int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
socklen_t fromlen);
int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
diff --git a/src/common/defs.h b/src/common/defs.h
index 1de099f..c968cd6 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - Common definitions
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -57,11 +57,13 @@
#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21)
#define WPA_KEY_MGMT_OWE BIT(22)
#define WPA_KEY_MGMT_DPP BIT(23)
+#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
{
return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
WPA_KEY_MGMT_CCKM |
WPA_KEY_MGMT_OSEN |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
@@ -86,6 +88,7 @@
{
return !!(akm & (WPA_KEY_MGMT_FT_PSK |
WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
WPA_KEY_MGMT_FT_SAE |
WPA_KEY_MGMT_FT_FILS_SHA256 |
WPA_KEY_MGMT_FT_FILS_SHA384));
@@ -114,6 +117,8 @@
{
return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_FT_SAE |
WPA_KEY_MGMT_OSEN |
WPA_KEY_MGMT_IEEE8021X_SUITE_B |
WPA_KEY_MGMT_FILS_SHA256 |
@@ -123,6 +128,7 @@
static inline int wpa_key_mgmt_sha384(int akm)
{
return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
WPA_KEY_MGMT_FILS_SHA384 |
WPA_KEY_MGMT_FT_FILS_SHA384));
}
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 5107950..ea00c93 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -9,6 +9,8 @@
#include "utils/includes.h"
#include <openssl/opensslv.h>
#include <openssl/err.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
#include "utils/common.h"
#include "utils/base64.h"
@@ -22,10 +24,28 @@
#include "crypto/aes_siv.h"
#include "crypto/sha384.h"
#include "crypto/sha512.h"
+#include "drivers/driver.h"
#include "dpp.h"
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#ifdef CONFIG_TESTING_OPTIONS
+enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
+u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_ephemeral_key_override[600];
+size_t dpp_pkex_ephemeral_key_override_len = 0;
+u8 dpp_protocol_key_override[600];
+size_t dpp_protocol_key_override_len = 0;
+u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+size_t dpp_nonce_override_len = 0;
+
+static int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
/* Compatibility wrappers for older versions. */
static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
@@ -83,10 +103,10 @@
0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
};
static const u8 pkex_resp_y_p256[32] = {
- 0x26, 0x04, 0x09, 0x45, 0x0a, 0x05, 0x20, 0xe7,
- 0xa7, 0x27, 0xc1, 0x36, 0x76, 0x85, 0xca, 0x3e,
- 0x42, 0x16, 0xf4, 0x89, 0x85, 0x34, 0x6e, 0xd5,
- 0x17, 0xde, 0xc0, 0xb8, 0xad, 0xfd, 0xb2, 0x98
+ 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
+ 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
+ 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
+ 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
};
/* NIST P-384 */
@@ -99,12 +119,12 @@
0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
};
static const u8 pkex_init_y_p384[48] = {
- 0x89, 0xd0, 0x97, 0x7b, 0x59, 0x4f, 0xa6, 0xd6,
- 0x7c, 0x5d, 0x93, 0x5b, 0x93, 0xc4, 0x07, 0xa9,
- 0x89, 0xee, 0xd5, 0xcd, 0x6f, 0x42, 0xf8, 0x38,
- 0xc8, 0xc6, 0x62, 0x24, 0x69, 0x0c, 0xd4, 0x48,
- 0xd8, 0x44, 0xd6, 0xc2, 0xe8, 0xcc, 0x62, 0x6b,
- 0x3c, 0x25, 0x53, 0xba, 0x4f, 0x71, 0xf8, 0xe7
+ 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
+ 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
+ 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
+ 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
+ 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
+ 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
};
static const u8 pkex_resp_x_p384[48] = {
0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
@@ -115,12 +135,12 @@
0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
};
static const u8 pkex_resp_y_p384[48] = {
- 0x54, 0x58, 0x20, 0xad, 0x55, 0x1d, 0xca, 0xf3,
- 0x1c, 0x8a, 0xcd, 0x19, 0x40, 0xf9, 0x37, 0x83,
- 0xc7, 0xd6, 0xb3, 0x13, 0x7d, 0x53, 0x28, 0x5c,
- 0xf6, 0x2d, 0xf1, 0xdd, 0xa5, 0x8b, 0xad, 0x5d,
- 0x81, 0xab, 0xb1, 0x00, 0x39, 0xd6, 0xcc, 0x9c,
- 0xea, 0x1e, 0x84, 0x1d, 0xbf, 0xe3, 0x35, 0xf9
+ 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
+ 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
+ 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
+ 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
+ 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
+ 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
};
/* NIST P-521 */
@@ -136,15 +156,15 @@
0x97, 0x76
};
static const u8 pkex_init_y_p521[66] = {
- 0x01, 0x4c, 0x71, 0xfd, 0x1b, 0xd5, 0x9c, 0xa6,
- 0xed, 0x39, 0xef, 0x45, 0xc5, 0x06, 0xfd, 0x66,
- 0xc0, 0xeb, 0x0f, 0xbf, 0x21, 0xa3, 0x36, 0x74,
- 0xfd, 0xaa, 0x05, 0x6e, 0x4e, 0x33, 0x95, 0x42,
- 0x1a, 0x9d, 0x3f, 0x3a, 0x1c, 0x5e, 0xa8, 0x60,
- 0xf7, 0xe5, 0x59, 0x1d, 0x07, 0xaa, 0x6f, 0x40,
- 0x0a, 0x59, 0x3c, 0x27, 0xad, 0xe0, 0x48, 0xfd,
- 0xd1, 0x83, 0x37, 0x4c, 0xdf, 0xe1, 0x86, 0x72,
- 0xfc, 0x57
+ 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
+ 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
+ 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
+ 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
+ 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
+ 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
+ 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
+ 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
+ 0x03, 0xa8
};
static const u8 pkex_resp_x_p521[66] = {
0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
@@ -158,15 +178,15 @@
0x84, 0xb4
};
static const u8 pkex_resp_y_p521[66] = {
- 0x01, 0xb9, 0x9c, 0xc6, 0x41, 0x32, 0x5b, 0xd2,
- 0x35, 0xd8, 0x8b, 0x2b, 0xe4, 0x6e, 0xcc, 0xdf,
- 0x7c, 0x38, 0xc4, 0x5b, 0xf6, 0x74, 0x71, 0x5c,
- 0x77, 0x16, 0x8a, 0x80, 0xa9, 0x84, 0xc7, 0x7b,
- 0x9d, 0xfd, 0x83, 0x6f, 0xae, 0xf8, 0x24, 0x16,
- 0x2f, 0x21, 0x25, 0x65, 0xa2, 0x1a, 0x6b, 0x2d,
- 0x30, 0x62, 0xb3, 0xcc, 0x6e, 0x59, 0x3c, 0x7f,
- 0x58, 0x91, 0x81, 0x72, 0x07, 0x8c, 0x91, 0xac,
- 0x31, 0x1e
+ 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
+ 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
+ 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
+ 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
+ 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
+ 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
+ 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
+ 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
+ 0xce, 0xe1
};
/* Brainpool P-256r1 */
@@ -177,10 +197,10 @@
0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
};
static const u8 pkex_init_y_bp_p256r1[32] = {
- 0x16, 0x30, 0x68, 0x32, 0x3b, 0xb0, 0x21, 0xee,
- 0xeb, 0xf7, 0xb6, 0x7c, 0xae, 0x52, 0x26, 0x42,
- 0x59, 0x28, 0x58, 0xb6, 0x14, 0x90, 0xed, 0x69,
- 0xd0, 0x67, 0xea, 0x25, 0x60, 0x0f, 0xa9, 0x6c
+ 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
+ 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
+ 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
+ 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
};
static const u8 pkex_resp_x_bp_p256r1[32] = {
0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
@@ -241,14 +261,14 @@
0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
};
static const u8 pkex_init_y_bp_p512r1[64] = {
- 0x5a, 0x28, 0x01, 0xbe, 0x96, 0x82, 0x4e, 0xf6,
- 0xfa, 0xed, 0x7d, 0xfd, 0x48, 0x8b, 0x48, 0x4e,
- 0xd1, 0x97, 0x87, 0xc4, 0x05, 0x5d, 0x15, 0x2a,
- 0xf4, 0x91, 0x4b, 0x75, 0x90, 0xd9, 0x34, 0x2c,
- 0x3c, 0x12, 0xf2, 0xf5, 0x25, 0x94, 0x24, 0x34,
- 0xa7, 0x6d, 0x66, 0xbc, 0x27, 0xa4, 0xa0, 0x8d,
- 0xd5, 0xe1, 0x54, 0xa3, 0x55, 0x26, 0xd4, 0x14,
- 0x17, 0x0f, 0xc1, 0xc7, 0x3d, 0x68, 0x7f, 0x5a
+ 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
+ 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
+ 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
+ 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
+ 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
+ 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
+ 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
+ 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
};
static const u8 pkex_resp_x_bp_p512r1[64] = {
0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
@@ -261,17 +281,50 @@
0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
};
static const u8 pkex_resp_y_bp_p512r1[64] = {
- 0x2a, 0xbe, 0x59, 0xe6, 0xc4, 0xb3, 0xd8, 0x09,
- 0x66, 0x89, 0x0a, 0x2d, 0x19, 0xf0, 0x9c, 0x9f,
- 0xb4, 0xab, 0x8f, 0x50, 0x68, 0x3c, 0x74, 0x64,
- 0x4e, 0x19, 0x55, 0x81, 0x9b, 0x48, 0x5c, 0xf4,
- 0x12, 0x8d, 0xb9, 0xd8, 0x02, 0x5b, 0xe1, 0x26,
- 0x7e, 0x19, 0x5c, 0xfd, 0x70, 0xf7, 0x4b, 0xdc,
- 0xb5, 0x5d, 0xc1, 0x7a, 0xe9, 0xd1, 0x05, 0x2e,
- 0xd1, 0xfd, 0x2f, 0xce, 0x63, 0x77, 0x48, 0x2c
+ 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
+ 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
+ 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
+ 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
+ 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
+ 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
+ 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
+ 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
};
+static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point)
+{
+ BIGNUM *x, *y;
+ BN_CTX *ctx;
+ char *x_str = NULL, *y_str = NULL;
+
+ if (!wpa_debug_show_keys)
+ return;
+
+ ctx = BN_CTX_new();
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !x || !y ||
+ EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
+ goto fail;
+
+ x_str = BN_bn2hex(x);
+ y_str = BN_bn2hex(y);
+ if (!x_str || !y_str)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+ OPENSSL_free(x_str);
+ OPENSSL_free(y_str);
+ BN_free(x);
+ BN_free(y);
+ BN_CTX_free(ctx);
+}
+
+
static int dpp_hash_vector(const struct dpp_curve_params *curve,
size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
@@ -335,6 +388,20 @@
}
+static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+ int num_bytes, offset;
+
+ num_bytes = BN_num_bytes(bn);
+ if ((size_t) num_bytes > len)
+ return -1;
+ offset = len - num_bytes;
+ os_memset(pos, 0, offset);
+ BN_bn2bin(bn, pos + offset);
+ return 0;
+}
+
+
static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
{
int len, res;
@@ -418,6 +485,7 @@
wpa_printf(MSG_ERROR, "DPP: Invalid point");
goto fail;
}
+ dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
eckey = EC_KEY_new();
if (!eckey ||
@@ -479,6 +547,12 @@
}
+static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
+{
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
size_t len)
{
@@ -523,6 +597,7 @@
int dpp_check_attrs(const u8 *buf, size_t len)
{
const u8 *pos, *end;
+ int wrapped_data = 0;
pos = buf;
end = buf + len;
@@ -540,6 +615,13 @@
"DPP: Truncated message - not enough room for the attribute - dropped");
return -1;
}
+ if (wrapped_data) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: An unexpected attribute included after the Wrapped Data attribute");
+ return -1;
+ }
+ if (id == DPP_ATTR_WRAPPED_DATA)
+ wrapped_data = 1;
pos += alen;
}
@@ -731,7 +813,7 @@
const unsigned char *pk;
int ppklen;
X509_ALGOR *pa;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
ASN1_OBJECT *pa_oid;
#else
const ASN1_OBJECT *pa_oid;
@@ -939,6 +1021,8 @@
int res;
unsigned char *der = NULL;
int der_len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
out = BIO_new(BIO_s_mem());
if (!out)
@@ -961,6 +1045,11 @@
if (!eckey)
return;
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (group && point)
+ dpp_debug_print_point(title, group, point);
+
der_len = i2d_ECPrivateKey(eckey, &der);
if (der_len > 0)
wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
@@ -979,13 +1068,8 @@
static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
{
-#ifdef OPENSSL_IS_BORINGSSL
EVP_PKEY_CTX *kctx = NULL;
- const EC_GROUP *group;
EC_KEY *ec_params;
-#else
- EVP_PKEY_CTX *pctx, *kctx = NULL;
-#endif
EVP_PKEY *params = NULL, *key = NULL;
int nid;
@@ -996,10 +1080,9 @@
wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
return NULL;
}
-#ifdef OPENSSL_IS_BORINGSSL
- group = EC_GROUP_new_by_curve_name(nid);
- ec_params = EC_KEY_new();
- if (!ec_params || EC_KEY_set_group(ec_params, group) != 1) {
+
+ ec_params = EC_KEY_new_by_curve_name(nid);
+ if (!ec_params) {
wpa_printf(MSG_ERROR,
"DPP: Failed to generate EC_KEY parameters");
goto fail;
@@ -1011,20 +1094,6 @@
"DPP: Failed to generate EVP_PKEY parameters");
goto fail;
}
-#else
- pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
- if (!pctx ||
- EVP_PKEY_paramgen_init(pctx) != 1 ||
- EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) != 1 ||
- EVP_PKEY_CTX_set_ec_param_enc(pctx, OPENSSL_EC_NAMED_CURVE) != 1 ||
- EVP_PKEY_paramgen(pctx, ¶ms) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate EVP_PKEY parameters");
- EVP_PKEY_CTX_free(pctx);
- goto fail;
- }
- EVP_PKEY_CTX_free(pctx);
-#endif
kctx = EVP_PKEY_CTX_new(params, NULL);
if (!kctx ||
@@ -1121,34 +1190,111 @@
}
-int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+typedef struct {
+ /* AlgorithmIdentifier ecPublicKey with optional parameters present
+ * as an OID identifying the curve */
+ X509_ALGOR *alg;
+ /* Compressed format public key per ANSI X9.63 */
+ ASN1_BIT_STRING *pub_key;
+} DPP_BOOTSTRAPPING_KEY;
+
+ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
+
+
+static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
{
unsigned char *der = NULL;
int der_len;
EC_KEY *eckey;
- int res;
+ struct wpabuf *ret = NULL;
size_t len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+ BN_CTX *ctx;
+ DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
+ int nid;
- /* Need to get the compressed form of the public key through EC_KEY, so
- * cannot use the simpler i2d_PUBKEY() here. */
- eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
- if (!eckey)
- return -1;
- EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
- der_len = i2d_EC_PUBKEY(eckey, &der);
- EC_KEY_free(eckey);
+ ctx = BN_CTX_new();
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!ctx || !eckey)
+ goto fail;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (!group || !point)
+ goto fail;
+ dpp_debug_print_point("DPP: bootstrap public key", group, point);
+ nid = EC_GROUP_get_curve_name(group);
+
+ bootstrap = DPP_BOOTSTRAPPING_KEY_new();
+ if (!bootstrap ||
+ X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
+ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+ goto fail;
+
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ NULL, 0, ctx);
+ if (len == 0)
+ goto fail;
+
+ der = OPENSSL_malloc(len);
+ if (!der)
+ goto fail;
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ der, len, ctx);
+
+ OPENSSL_free(bootstrap->pub_key->data);
+ bootstrap->pub_key->data = der;
+ der = NULL;
+ bootstrap->pub_key->length = len;
+ /* No unused bits */
+ bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+ der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
if (der_len <= 0) {
wpa_printf(MSG_ERROR,
"DDP: Failed to build DER encoded public key");
- OPENSSL_free(der);
- return -1;
+ goto fail;
}
- len = der_len;
- res = sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash);
+ ret = wpabuf_alloc_copy(der, der_len);
+fail:
+ DPP_BOOTSTRAPPING_KEY_free(bootstrap);
OPENSSL_free(der);
+ EC_KEY_free(eckey);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+ struct wpabuf *der;
+ int res;
+ const u8 *addr[1];
+ size_t len[1];
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ return -1;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+
+ addr[0] = wpabuf_head(der);
+ len[0] = wpabuf_len(der);
+ res = sha256_vector(1, addr, len, bi->pubkey_hash);
if (res < 0)
wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ else
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
+ SHA256_MAC_LEN);
+ wpabuf_free(der);
return res;
}
@@ -1159,9 +1305,9 @@
unsigned char *base64 = NULL;
char *pos, *end;
size_t len;
- unsigned char *der = NULL;
- int der_len;
- EC_KEY *eckey;
+ struct wpabuf *der = NULL;
+ const u8 *addr[1];
+ int res;
if (!curve) {
bi->curve = &dpp_curves[0];
@@ -1181,28 +1327,24 @@
goto fail;
bi->own = 1;
- /* Need to get the compressed form of the public key through EC_KEY, so
- * cannot use the simpler i2d_PUBKEY() here. */
- eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
- if (!eckey)
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
goto fail;
- EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
- der_len = i2d_EC_PUBKEY(eckey, &der);
- EC_KEY_free(eckey);
- if (der_len <= 0) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to build DER encoded public key");
- goto fail;
- }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
- len = der_len;
- if (sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash) < 0) {
+ addr[0] = wpabuf_head(der);
+ len = wpabuf_len(der);
+ res = sha256_vector(1, addr, &len, bi->pubkey_hash);
+ if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
goto fail;
}
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
+ SHA256_MAC_LEN);
- base64 = base64_encode(der, der_len, &len);
- OPENSSL_free(der);
+ base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
+ wpabuf_free(der);
der = NULL;
if (!base64)
goto fail;
@@ -1217,7 +1359,7 @@
return (char *) base64;
fail:
os_free(base64);
- OPENSSL_free(der);
+ wpabuf_free(der);
return NULL;
}
@@ -1291,6 +1433,12 @@
size_t len[3];
size_t num_elem = 0;
+ if (!auth->Mx_len || !auth->Nx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mx/Nx not available - cannot derive ke");
+ return -1;
+ }
+
/* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
/* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
@@ -1298,12 +1446,17 @@
os_memcpy(nonces, auth->i_nonce, nonce_len);
os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
addr[num_elem] = auth->Mx;
- len[num_elem] = auth->secret_len;
+ len[num_elem] = auth->Mx_len;
num_elem++;
addr[num_elem] = auth->Nx;
- len[num_elem] = auth->secret_len;
+ len[num_elem] = auth->Nx_len;
num_elem++;
if (auth->peer_bi && auth->own_bi) {
+ if (!auth->Lx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Lx not available - cannot derive ke");
+ return -1;
+ }
addr[num_elem] = auth->Lx;
len[num_elem] = auth->secret_len;
num_elem++;
@@ -1327,16 +1480,48 @@
}
-struct dpp_authentication * dpp_auth_init(void *msg_ctx,
- struct dpp_bootstrap_info *peer_bi,
- struct dpp_bootstrap_info *own_bi,
- int configurator)
+static void dpp_build_attr_status(struct wpabuf *msg,
+ enum dpp_status_error status)
{
- struct dpp_authentication *auth;
- size_t nonce_len;
- EVP_PKEY_CTX *ctx = NULL;
- size_t secret_len;
- struct wpabuf *msg, *pi = NULL;
+ wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
+ wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, status);
+}
+
+
+static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
+ const struct wpabuf *pi,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ unsigned int neg_freq)
+{
+ struct wpabuf *msg;
u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
u8 *pos;
@@ -1344,24 +1529,593 @@
size_t len[2], siv_len, attr_len;
u8 *attr_start, *attr_end;
+ /* Build DPP Authentication Request frame attributes */
+ attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
+ 4 + sizeof(wrapped_data);
+ if (neg_freq > 0)
+ attr_len += 4 + 2;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Initiator Protocol Key */
+ if (pi) {
+ wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pi));
+ wpabuf_put_buf(msg, pi);
+ }
+
+ /* Channel */
+ if (neg_freq > 0) {
+ u8 op_class, channel;
+
+ if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
+ &channel) ==
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported negotiation frequency request: %d",
+ neg_freq);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_u8(msg, op_class);
+ wpabuf_put_u8(msg, channel);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({I-nonce, I-capabilities}k1) */
+ pos = clear;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ goto skip_i_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len - 1);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len - 1);
+ pos += nonce_len - 1;
+ goto skip_i_nonce;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len);
+ pos += nonce_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_nonce:
+ if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
+ goto skip_i_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->i_capab = auth->allowed_roles;
+ *pos++ = auth->i_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
+ pos[-1] = 0;
+ }
+skip_i_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Request frame attributes", msg);
+
+ return msg;
+}
+
+
+static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
+ enum dpp_status_error status,
+ const struct wpabuf *pr,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ const u8 *r_nonce, const u8 *i_nonce,
+ const u8 *wrapped_r_auth,
+ size_t wrapped_r_auth_len,
+ const u8 *siv_key)
+{
+ struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+ 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
+ u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+ u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end, *pos;
+
+ auth->waiting_auth_conf = 1;
+ auth->auth_resp_tries = 0;
+
+ /* Build DPP Authentication Response frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* DPP Status */
+ if (status != 255)
+ dpp_build_attr_status(msg, status);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Responder Protocol Key */
+ if (pr) {
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+ }
+
+ attr_end = wpabuf_put(msg, 0);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+ pos = clear;
+
+ if (r_nonce) {
+ /* R-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, r_nonce, nonce_len);
+ pos += nonce_len;
+ }
+
+ if (i_nonce) {
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
+ pos[nonce_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ pos += nonce_len;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
+ goto skip_r_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* R-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+ DPP_CAPAB_ENROLLEE;
+ *pos++ = auth->r_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
+ pos[-1] = 0;
+ } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - incompatible R-capabilities");
+ if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
+ pos[-1] = 0;
+ else
+ pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
+ DPP_CAPAB_CONFIGURATOR;
+ }
+skip_r_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wrapped_r_auth) {
+ /* {R-auth}ke */
+ WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+ pos += 2;
+ WPA_PUT_LE16(pos, wrapped_r_auth_len);
+ pos += 2;
+ os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+ pos += wrapped_r_auth_len;
+ }
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Response frame attributes", msg);
+ return msg;
+}
+
+
+static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
+ u16 num_modes, unsigned int freq)
+{
+ u16 m;
+ int c, flag;
+
+ if (!own_modes || !num_modes)
+ return 1;
+
+ for (m = 0; m < num_modes; m++) {
+ for (c = 0; c < own_modes[m].num_channels; c++) {
+ if ((unsigned int) own_modes[m].channels[c].freq !=
+ freq)
+ continue;
+ flag = own_modes[m].channels[c].flag;
+ if (!(flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR)))
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
+ return 0;
+}
+
+
+static int freq_included(const unsigned int freqs[], unsigned int num,
+ unsigned int freq)
+{
+ while (num > 0) {
+ if (freqs[--num] == freq)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void freq_to_start(unsigned int freqs[], unsigned int num,
+ unsigned int freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ if (freqs[i] == freq)
+ break;
+ }
+ if (i == 0 || i >= num)
+ return;
+ os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
+ freqs[0] = freq;
+}
+
+
+static int dpp_channel_intersect(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
+ unsigned int i, freq;
+
+ for (i = 0; i < peer_bi->num_freq; i++) {
+ freq = peer_bi->freq[i];
+ if (freq_included(auth->freq, auth->num_freq, freq))
+ continue;
+ if (dpp_channel_ok_init(own_modes, num_modes, freq))
+ auth->freq[auth->num_freq++] = freq;
+ }
+ if (!auth->num_freq) {
+ wpa_printf(MSG_INFO,
+ "DPP: No available channels for initiating DPP Authentication");
+ return -1;
+ }
+ auth->curr_freq = auth->freq[0];
+ return 0;
+}
+
+
+static int dpp_channel_local_list(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ u16 m;
+ int c, flag;
+ unsigned int freq;
+
+ auth->num_freq = 0;
+
+ if (!own_modes || !num_modes) {
+ auth->freq[0] = 2412;
+ auth->freq[1] = 2437;
+ auth->freq[2] = 2462;
+ auth->num_freq = 3;
+ return 0;
+ }
+
+ for (m = 0; m < num_modes; m++) {
+ for (c = 0; c < own_modes[m].num_channels; c++) {
+ freq = own_modes[m].channels[c].freq;
+ flag = own_modes[m].channels[c].flag;
+ if (flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+ if (freq_included(auth->freq, auth->num_freq, freq))
+ continue;
+ auth->freq[auth->num_freq++] = freq;
+ if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+ m = num_modes;
+ break;
+ }
+ }
+ }
+
+ return auth->num_freq == 0 ? -1 : 0;
+}
+
+
+static int dpp_prepare_channel_list(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ int res;
+ char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
+ unsigned int i;
+
+ if (auth->peer_bi->num_freq > 0)
+ res = dpp_channel_intersect(auth, own_modes, num_modes);
+ else
+ res = dpp_channel_local_list(auth, own_modes, num_modes);
+ if (res < 0)
+ return res;
+
+ /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
+ * likely channels first. */
+ freq_to_start(auth->freq, auth->num_freq, 2462);
+ freq_to_start(auth->freq, auth->num_freq, 2412);
+ freq_to_start(auth->freq, auth->num_freq, 2437);
+
+ auth->freq_idx = 0;
+ auth->curr_freq = auth->freq[0];
+
+ pos = freqs;
+ end = pos + sizeof(freqs);
+ for (i = 0; i < auth->num_freq; i++) {
+ res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
+ freqs);
+
+ return 0;
+}
+
+
+static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
+{
+ struct dpp_bootstrap_info *bi;
+ char *pk = NULL;
+ size_t len;
+
+ if (auth->own_bi)
+ return 0; /* already generated */
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return -1;
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0);
+ if (!pk)
+ goto fail;
+
+ len = 4; /* "DPP:" */
+ len += 4 + os_strlen(pk);
+ bi->uri = os_malloc(len + 1);
+ if (!bi->uri)
+ goto fail;
+ os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Auto-generated own bootstrapping key info: URI %s",
+ bi->uri);
+
+ auth->tmp_own_bi = auth->own_bi = bi;
+
+ os_free(pk);
+
+ return 0;
+fail:
+ os_free(pk);
+ dpp_bootstrap_info_free(bi);
+ return -1;
+}
+
+
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ u8 dpp_allowed_roles,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_authentication *auth;
+ size_t nonce_len;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ struct wpabuf *pi = NULL;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
auth = os_zalloc(sizeof(*auth));
if (!auth)
return NULL;
auth->msg_ctx = msg_ctx;
auth->initiator = 1;
- auth->configurator = configurator;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = dpp_allowed_roles;
+ auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
auth->peer_bi = peer_bi;
auth->own_bi = own_bi;
auth->curve = peer_bi->curve;
+ if (dpp_autogen_bootstrap_key(auth) < 0 ||
+ dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
nonce_len = auth->curve->nonce_len;
if (random_get_bytes(auth->i_nonce, nonce_len)) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
goto fail;
}
+#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
if (!auth->own_protocol_key)
goto fail;
@@ -1388,93 +2142,60 @@
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
auth->curve->hash_len) < 0)
goto fail;
- /* Build DPP Authentication Request frame attributes */
- attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + wpabuf_len(pi) +
- 4 + sizeof(wrapped_data);
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
- if (!msg)
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
+ wpabuf_free(pi);
+ pi = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
+ wpabuf_free(pi);
+ pi = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
+ i_pubkey_hash, neg_freq);
+ if (!auth->req_msg)
goto fail;
- auth->req_msg = msg;
- attr_start = wpabuf_put(msg, 0);
-
- /* Responder Bootstrapping Key Hash */
- wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
-
- /* Initiator Bootstrapping Key Hash */
- wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- if (auth->own_bi)
- wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
- else
- os_memset(wpabuf_put(msg, SHA256_MAC_LEN), 0, SHA256_MAC_LEN);
-
- /* Initiator Protocol Key */
- wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
- wpabuf_put_le16(msg, wpabuf_len(pi));
- wpabuf_put_buf(msg, pi);
- wpabuf_free(pi);
- pi = NULL;
-
- /* Wrapped data ({I-nonce, I-capabilities}k1) */
- pos = clear;
- /* I-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, auth->i_nonce, nonce_len);
- pos += nonce_len;
- /* I-capabilities */
- WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
- pos += 2;
- WPA_PUT_LE16(pos, 1);
- pos += 2;
- auth->i_capab = configurator ? DPP_CAPAB_CONFIGURATOR :
- DPP_CAPAB_ENROLLEE;
- *pos++ = auth->i_capab;
-
- attr_end = wpabuf_put(msg, 0);
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- siv_len = pos - clear;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
- if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
- 2, addr, len, wrapped_data) < 0)
- goto fail;
- siv_len += AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, siv_len);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, siv_len);
- wpabuf_put_data(msg, wrapped_data, siv_len);
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Request frame attributes", msg);
-
- return auth;
-fail:
+out:
wpabuf_free(pi);
EVP_PKEY_CTX_free(ctx);
+ return auth;
+fail:
dpp_auth_deinit(auth);
- return NULL;
+ auth = NULL;
+ goto out;
}
@@ -1485,6 +2206,7 @@
size_t json_len, clear_len;
struct wpabuf *clear = NULL, *msg = NULL;
u8 *wrapped;
+ size_t attr_len;
wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
@@ -1500,20 +2222,55 @@
/* { E-nonce, configAttrib }ke */
clear_len = 4 + nonce_len + 4 + json_len;
clear = wpabuf_alloc(clear_len);
- msg = wpabuf_alloc(4 + clear_len + AES_BLOCK_SIZE);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = wpabuf_alloc(attr_len);
if (!clear || !msg)
goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce");
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, nonce_len - 1);
+ wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1);
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* E-nonce */
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, nonce_len);
wpabuf_put_data(clear, auth->e_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_e_nonce:
+ if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib");
+ goto skip_conf_attr_obj;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* configAttrib */
wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
wpabuf_put_le16(clear, json_len);
wpabuf_put_data(clear, json, json_len);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_conf_attr_obj:
+#endif /* CONFIG_TESTING_OPTIONS */
+
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
@@ -1527,6 +2284,14 @@
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Configuration Request frame attributes", msg);
wpabuf_free(clear);
@@ -1544,8 +2309,11 @@
wpa_printf(MSG_DEBUG,
"DPP: Authentication success - clear temporary keys");
os_memset(auth->Mx, 0, sizeof(auth->Mx));
+ auth->Mx_len = 0;
os_memset(auth->Nx, 0, sizeof(auth->Nx));
+ auth->Nx_len = 0;
os_memset(auth->Lx, 0, sizeof(auth->Lx));
+ auth->Lx_len = 0;
os_memset(auth->k1, 0, sizeof(auth->k1));
os_memset(auth->k2, 0, sizeof(auth->k2));
@@ -1725,7 +2493,6 @@
BIGNUM *lx, *sum, *q;
const BIGNUM *bR_bn, *pR_bn;
int ret = -1;
- int num_bytes, offset;
/* L = ((bR + pR) modulo q) * BI */
@@ -1765,17 +2532,10 @@
goto fail;
}
- num_bytes = BN_num_bytes(lx);
- if ((size_t) num_bytes > auth->secret_len)
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
goto fail;
- if (auth->secret_len > (size_t) num_bytes)
- offset = auth->secret_len - num_bytes;
- else
- offset = 0;
-
- os_memset(auth->Lx, 0, offset);
- BN_bn2bin(lx, auth->Lx + offset);
wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
ret = 0;
fail:
EC_POINT_clear_free(l);
@@ -1800,7 +2560,6 @@
BIGNUM *lx;
const BIGNUM *bI_bn;
int ret = -1;
- int num_bytes, offset;
/* L = bI * (BR + PR) */
@@ -1835,17 +2594,10 @@
goto fail;
}
- num_bytes = BN_num_bytes(lx);
- if ((size_t) num_bytes > auth->secret_len)
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
goto fail;
- if (auth->secret_len > (size_t) num_bytes)
- offset = auth->secret_len - num_bytes;
- else
- offset = 0;
-
- os_memset(auth->Lx, 0, offset);
- BN_bn2bin(lx, auth->Lx + offset);
wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
ret = 0;
fail:
EC_POINT_clear_free(l);
@@ -1858,34 +2610,63 @@
}
-static int dpp_auth_build_resp(struct dpp_authentication *auth)
+static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
{
size_t nonce_len;
EVP_PKEY_CTX *ctx = NULL;
size_t secret_len;
struct wpabuf *msg, *pr = NULL;
u8 r_auth[4 + DPP_MAX_HASH_LEN];
- u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE];
-#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
- 4 + sizeof(wrapped_r_auth)
+ u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
size_t wrapped_r_auth_len;
- u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
- u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
- u8 *pos;
- const u8 *addr[2];
- size_t len[2], siv_len, attr_len;
- u8 *attr_start, *attr_end;
+ int ret = -1;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
+ enum dpp_status_error status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+ if (!auth->own_bi)
+ return -1;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
nonce_len = auth->curve->nonce_len;
if (random_get_bytes(auth->r_nonce, nonce_len)) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
goto fail;
}
+#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
if (!auth->own_protocol_key)
goto fail;
@@ -1911,6 +2692,7 @@
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
auth->curve->hash_len) < 0)
@@ -1928,215 +2710,163 @@
/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
- if (dpp_gen_r_auth(auth, r_auth + 4) < 0 ||
- aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
+ goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
+ r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
r_auth, 4 + auth->curve->hash_len,
0, NULL, NULL, wrapped_r_auth) < 0)
goto fail;
wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
wrapped_r_auth, wrapped_r_auth_len);
+ w_r_auth = wrapped_r_auth;
- /* Build DPP Authentication Response frame attributes */
- attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
- 4 + wpabuf_len(pr) + 4 + sizeof(wrapped_data);
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+ r_nonce = auth->r_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
+ wpabuf_free(pr);
+ pr = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
+ wpabuf_free(pr);
+ pr = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
+ goto fail;
+ } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
+ w_r_auth = NULL;
+ wrapped_r_auth_len = 0;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
+ r_nonce = NULL;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ r_nonce, i_nonce,
+ w_r_auth, wrapped_r_auth_len,
+ auth->k2);
if (!msg)
goto fail;
wpabuf_free(auth->resp_msg);
auth->resp_msg = msg;
-
- attr_start = wpabuf_put(msg, 0);
-
- /* DPP Status */
- wpabuf_put_le16(msg, DPP_ATTR_STATUS);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, DPP_STATUS_OK);
-
- /* Responder Bootstrapping Key Hash */
- wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
-
- if (auth->peer_bi) {
- /* Mutual authentication */
- /* Initiator Bootstrapping Key Hash */
- wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
- SHA256_MAC_LEN);
- }
-
- /* Responder Protocol Key */
- wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
- wpabuf_put_le16(msg, wpabuf_len(pr));
- wpabuf_put_buf(msg, pr);
- wpabuf_free(pr);
- pr = NULL;
-
- attr_end = wpabuf_put(msg, 0);
-
- /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
- pos = clear;
- /* R-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, auth->r_nonce, nonce_len);
- pos += nonce_len;
- /* I-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, auth->i_nonce, nonce_len);
- pos += nonce_len;
- /* R-capabilities */
- WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
- pos += 2;
- WPA_PUT_LE16(pos, 1);
- pos += 2;
- auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
- DPP_CAPAB_ENROLLEE;
- *pos++ = auth->r_capab;
- /* {R-auth}ke */
- WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
- pos += 2;
- WPA_PUT_LE16(pos, wrapped_r_auth_len);
- pos += 2;
- os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
- pos += wrapped_r_auth_len;
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- siv_len = pos - clear;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
- if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, clear, siv_len,
- 2, addr, len, wrapped_data) < 0)
- goto fail;
- siv_len += AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, siv_len);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, siv_len);
- wpabuf_put_data(msg, wrapped_data, siv_len);
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Response frame attributes", msg);
-
- return 0;
-
+ ret = 0;
fail:
wpabuf_free(pr);
- return -1;
+ return ret;
}
static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
enum dpp_status_error status)
{
- size_t nonce_len;
struct wpabuf *msg;
-#define DPP_AUTH_RESP_CLEAR_LEN2 4 + DPP_MAX_NONCE_LEN + 4 + 1
- u8 clear[DPP_AUTH_RESP_CLEAR_LEN2];
- u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN2 + AES_BLOCK_SIZE];
- u8 *pos;
- const u8 *addr[2];
- size_t len[2], siv_len, attr_len;
- u8 *attr_start, *attr_end;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_bi)
+ return -1;
wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
- /* Build DPP Authentication Response frame attributes */
- attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + 4 + sizeof(wrapped_data);
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = -1;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ NULL, i_nonce, NULL, 0, auth->k1);
if (!msg)
- goto fail;
+ return -1;
wpabuf_free(auth->resp_msg);
auth->resp_msg = msg;
-
- attr_start = wpabuf_put(msg, 0);
-
- /* DPP Status */
- wpabuf_put_le16(msg, DPP_ATTR_STATUS);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, status);
-
- /* Responder Bootstrapping Key Hash */
- wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
-
- if (auth->peer_bi) {
- /* Mutual authentication */
- /* Initiator Bootstrapping Key Hash */
- wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
- SHA256_MAC_LEN);
- }
-
- attr_end = wpabuf_put(msg, 0);
-
- /* Wrapped data ({I-nonce, R-capabilities}k1) */
- pos = clear;
- /* I-nonce */
- nonce_len = auth->curve->nonce_len;
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, auth->i_nonce, nonce_len);
- pos += nonce_len;
- /* R-capabilities */
- WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
- pos += 2;
- WPA_PUT_LE16(pos, 1);
- pos += 2;
- auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
- DPP_CAPAB_ENROLLEE;
- *pos++ = auth->r_capab;
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- siv_len = pos - clear;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
- if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
- 2, addr, len, wrapped_data) < 0)
- goto fail;
- siv_len += AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, siv_len);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, siv_len);
- wpabuf_put_data(msg, wrapped_data, siv_len);
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Response frame attributes", msg);
-
return 0;
-
-fail:
- return -1;
}
@@ -2145,7 +2875,7 @@
struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
unsigned int freq, const u8 *hdr, const u8 *attr_start,
- const u8 *wrapped_data, u16 wrapped_data_len)
+ size_t attr_len)
{
EVP_PKEY *pi = NULL;
EVP_PKEY_CTX *ctx = NULL;
@@ -2154,14 +2884,29 @@
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
- const u8 *i_proto, *i_nonce, *i_capab, *i_bootstrap;
- u16 i_proto_len, i_nonce_len, i_capab_len, i_bootstrap_len;
+ const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
+ *channel;
+ u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
+ i_bootstrap_len, channel_len;
struct dpp_authentication *auth = NULL;
- size_t attr_len;
- if (wrapped_data_len < AES_BLOCK_SIZE)
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Request");
return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
attr_len = wrapped_data - 4 - attr_start;
auth = os_zalloc(sizeof(*auth));
@@ -2173,11 +2918,39 @@
auth->curve = own_bi->curve;
auth->curr_freq = freq;
+ channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
+ &channel_len);
+ if (channel) {
+ int neg_freq;
+
+ if (channel_len < 2) {
+ dpp_auth_fail(auth, "Too short Channel attribute");
+ goto fail;
+ }
+
+ neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
+ channel[0], channel[1], neg_freq);
+ if (neg_freq < 0) {
+ dpp_auth_fail(auth,
+ "Unsupported Channel attribute value");
+ goto fail;
+ }
+
+ if (auth->curr_freq != (unsigned int) neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Changing negotiation channel from %u MHz to %u MHz",
+ freq, neg_freq);
+ auth->curr_freq = neg_freq;
+ }
+ }
+
i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
&i_proto_len);
if (!i_proto) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing required Initiator Protocol Key attribute");
+ dpp_auth_fail(auth,
+ "Missing required Initiator Protocol Key attribute");
goto fail;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
@@ -2186,7 +2959,7 @@
/* M = bR * PI */
pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
if (!pi) {
- wpa_printf(MSG_DEBUG, "DPP: Invalid Initiator Protocol Key");
+ dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
goto fail;
}
dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
@@ -2201,6 +2974,7 @@
wpa_printf(MSG_ERROR,
"DPP: Failed to derive ECDH shared secret: %s",
ERR_error_string(ERR_get_error(), NULL));
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
goto fail;
}
auth->secret_len = secret_len;
@@ -2209,6 +2983,7 @@
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
auth->curve->hash_len) < 0)
@@ -2229,22 +3004,21 @@
if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in unwrapped data");
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
&i_nonce_len);
if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
@@ -2254,7 +3028,7 @@
DPP_ATTR_I_CAPABILITIES,
&i_capab_len);
if (!i_capab || i_capab_len < 1) {
- wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-capabilities");
+ dpp_auth_fail(auth, "Missing or invalid I-capabilities");
goto fail;
}
auth->i_capab = i_capab[0];
@@ -2282,9 +3056,25 @@
wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
auth->configurator = 0;
break;
+ case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
+ if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator/Enrollee role");
+ goto not_compatible;
+ }
+ break;
default:
wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
- goto not_compatible;
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
+ auth->i_capab & DPP_CAPAB_ROLE_MASK);
+ goto fail;
}
auth->peer_protocol_key = pi;
@@ -2314,7 +3104,7 @@
"%s", hex);
return auth;
}
- if (dpp_auth_build_resp(auth) < 0)
+ if (dpp_auth_build_resp_ok(auth) < 0)
goto fail;
return auth;
@@ -2355,52 +3145,107 @@
MACSTR, MAC2STR(auth->peer_mac_addr));
auth->peer_bi = peer_bi;
- if (dpp_auth_build_resp(auth) < 0)
+ if (dpp_auth_build_resp_ok(auth) < 0)
return -1;
return 1;
}
-static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth)
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
+ enum dpp_status_error status)
{
struct wpabuf *msg;
u8 i_auth[4 + DPP_MAX_HASH_LEN];
size_t i_auth_len;
+ u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
+ size_t r_nonce_len;
const u8 *addr[2];
size_t len[2], attr_len;
u8 *wrapped_i_auth;
+ u8 *wrapped_r_nonce;
u8 *attr_start, *attr_end;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
i_auth_len = 4 + auth->curve->hash_len;
+ r_nonce_len = 4 + auth->curve->nonce_len;
/* Build DPP Authentication Confirmation frame attributes */
attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
- 4 + i_auth_len + AES_BLOCK_SIZE;
+ 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
if (!msg)
goto fail;
attr_start = wpabuf_put(msg, 0);
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ if (auth->own_bi)
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* DPP Status */
- wpabuf_put_le16(msg, DPP_ATTR_STATUS);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, DPP_STATUS_OK);
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
/* Responder Bootstrapping Key Hash */
- wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
- if (auth->own_bi) {
- /* Mutual authentication */
- /* Initiator Bootstrapping Key Hash */
- wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
- }
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
+ goto skip_wrapped_data;
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ i_auth_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
attr_end = wpabuf_put(msg, 0);
@@ -2414,28 +3259,73 @@
len[1] = attr_end - attr_start;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
- wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
- /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
- WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
- WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
- if (dpp_gen_i_auth(auth, i_auth + 4) < 0 ||
- aes_siv_encrypt(auth->ke, auth->curve->hash_len,
- i_auth, i_auth_len,
- 2, addr, len, wrapped_i_auth) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
- wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+ if (status == DPP_STATUS_OK) {
+ /* I-auth wrapped with ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+ wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ goto skip_i_auth;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
+ * 1) */
+ WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+ WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+ if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
+ i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+skip_i_auth:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ i_auth, i_auth_len,
+ 2, addr, len, wrapped_i_auth) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+ wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+ } else {
+ /* R-nonce wrapped with k2 */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
+ wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
+
+ WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
+ WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
+ os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
+
+ if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
+ r_nonce, r_nonce_len,
+ 2, addr, len, wrapped_r_nonce) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
+ wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Authentication Confirmation frame attributes",
msg);
- dpp_auth_success(auth);
+ if (status == DPP_STATUS_OK)
+ dpp_auth_success(auth);
return msg;
fail:
+ wpabuf_free(msg);
return NULL;
}
@@ -2463,6 +3353,7 @@
wpa_printf(MSG_DEBUG,
"DPP: Responder reported failure (status %d)",
status);
+ dpp_auth_fail(auth, "Responder reported failure");
return;
}
@@ -2481,27 +3372,26 @@
if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in unwrapped data");
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
&i_nonce_len);
if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+ dpp_auth_fail(auth, "I-nonce mismatch");
goto fail;
}
@@ -2509,7 +3399,7 @@
DPP_ATTR_R_CAPABILITIES,
&r_capab_len);
if (!r_capab || r_capab_len < 1) {
- wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
goto fail;
}
auth->r_capab = r_capab[0];
@@ -2518,9 +3408,20 @@
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
"r-capab=0x%02x", auth->r_capab);
} else if (status == DPP_STATUS_RESPONSE_PENDING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Continue waiting for full DPP Authentication Response");
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_RESPONSE_PENDING);
+ u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+
+ if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
+ role);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue waiting for full DPP Authentication Response");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_RESPONSE_PENDING "%s",
+ auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
+ }
}
fail:
bin_clear_free(unwrapped, unwrapped_len);
@@ -2544,34 +3445,49 @@
r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
wrapped2_len, r_auth_len;
u8 r_auth2[DPP_MAX_HASH_LEN];
+ u8 role;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Response");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->initiator) {
+ dpp_auth_fail(auth, "Unexpected Authentication Response");
+ return NULL;
+ }
+
+ auth->waiting_auth_resp = 0;
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
- if (!wrapped_data) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing required Wrapped data attribute");
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
return NULL;
}
wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
wrapped_data, wrapped_data_len);
- if (wrapped_data_len < AES_BLOCK_SIZE)
- return NULL;
-
attr_len = wrapped_data - 4 - attr_start;
r_bootstrap = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
return NULL;
}
wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
r_bootstrap, r_bootstrap_len);
if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Unexpected Responder Bootstrapping Key Hash value");
wpa_hexdump(MSG_DEBUG,
"DPP: Expected Responder Bootstrapping Key Hash",
auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
@@ -2583,8 +3499,8 @@
&i_bootstrap_len);
if (i_bootstrap) {
if (i_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
return NULL;
}
wpa_hexdump(MSG_MSGDUMP,
@@ -2593,17 +3509,22 @@
if (!auth->own_bi ||
os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
SHA256_MAC_LEN) != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash attribute did not match");
return NULL;
}
+ } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
+ /* PKEX bootstrapping mandates use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
}
status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
&status_len);
if (!status || status_len < 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required DPP Status attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
return NULL;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
@@ -2615,11 +3536,20 @@
return NULL;
}
+ if (!i_bootstrap && auth->own_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder decided not to use mutual authentication");
+ auth->own_bi = NULL;
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
+ auth->own_bi != NULL);
+
r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
&r_proto_len);
if (!r_proto) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing required Responder Protocol Key attribute");
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
@@ -2628,7 +3558,7 @@
/* N = pI * PR */
pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
if (!pr) {
- wpa_printf(MSG_DEBUG, "DPP: Invalid Responder Protocol Key");
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
return NULL;
}
dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
@@ -2643,6 +3573,7 @@
wpa_printf(MSG_ERROR,
"DPP: Failed to derive ECDH shared secret: %s",
ERR_error_string(ERR_get_error(), NULL));
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
goto fail;
}
EVP_PKEY_CTX_free(ctx);
@@ -2652,6 +3583,7 @@
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
auth->curve->hash_len) < 0)
@@ -2672,22 +3604,21 @@
if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in unwrapped data");
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
&r_nonce_len);
if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
- wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-nonce");
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
@@ -2696,12 +3627,12 @@
i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
&i_nonce_len);
if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+ dpp_auth_fail(auth, "I-nonce mismatch");
goto fail;
}
@@ -2711,34 +3642,52 @@
goto fail;
}
- if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
- goto fail;
-
r_capab = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_R_CAPABILITIES,
&r_capab_len);
if (!r_capab || r_capab_len < 1) {
- wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
goto fail;
}
auth->r_capab = r_capab[0];
wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
- if ((auth->configurator && (auth->r_capab & DPP_CAPAB_CONFIGURATOR)) ||
- (!auth->configurator && (auth->r_capab & DPP_CAPAB_ENROLLEE))) {
+ role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+ if ((auth->allowed_roles ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
+ (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
+ /* Peer selected its role, so move from "either role" to the
+ * role that is compatible with peer's selection. */
+ auth->configurator = role == DPP_CAPAB_ENROLLEE;
+ wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
+ auth->configurator ? "Configurator" : "Enrollee");
+ } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
- goto fail;
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Unexpected role in R-capabilities 0x%02x",
+ role);
+ if (role != DPP_CAPAB_ENROLLEE &&
+ role != DPP_CAPAB_CONFIGURATOR)
+ goto fail;
+ bin_clear_free(unwrapped, unwrapped_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
}
wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid Secondary Wrapped Data");
+ dpp_auth_fail(auth,
+ "Missing or invalid Secondary Wrapped Data");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped2, wrapped2_len);
+
+ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+ goto fail;
+
unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
unwrapped2 = os_malloc(unwrapped2_len);
if (!unwrapped2)
@@ -2746,23 +3695,23 @@
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped2, wrapped2_len,
0, NULL, NULL, unwrapped2) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped2, unwrapped2_len);
if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in secondary unwrapped data");
+ dpp_auth_fail(auth,
+ "Invalid attribute in secondary unwrapped data");
goto fail;
}
r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
&r_auth_len);
if (!r_auth || r_auth_len != auth->curve->hash_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid Responder Authenticating Tag");
+ dpp_auth_fail(auth,
+ "Missing or invalid Responder Authenticating Tag");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
@@ -2773,15 +3722,27 @@
wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
r_auth2, r_auth_len);
if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Mismatching Responder Authenticating Tag");
- goto fail;
+ dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
}
bin_clear_free(unwrapped, unwrapped_len);
bin_clear_free(unwrapped2, unwrapped2_len);
- return dpp_auth_build_conf(auth);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - Authentication Response in place of Confirm");
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return NULL;
+ return wpabuf_dup(auth->resp_msg);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return dpp_auth_build_conf(auth, DPP_STATUS_OK);
fail:
bin_clear_free(unwrapped, unwrapped_len);
@@ -2792,6 +3753,77 @@
}
+static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
+ const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data,
+ u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *r_nonce;
+ u16 r_nonce_len;
+
+ /* Authentication Confirm failure cases are expected to include
+ * {R-nonce}k2 in the Wrapped Data attribute. */
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped) {
+ dpp_auth_fail(auth, "Authentication failed");
+ goto fail;
+ }
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+ wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
+ r_nonce, r_nonce_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
+ auth->r_nonce, r_nonce_len);
+ dpp_auth_fail(auth, "R-nonce mismatch");
+ goto fail;
+ }
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE)
+ dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
+ else if (status == DPP_STATUS_AUTH_FAILURE)
+ dpp_auth_fail(auth, "Peer reported authentication failure)");
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
+
+
int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len)
{
@@ -2804,28 +3836,39 @@
size_t unwrapped_len = 0;
u8 i_auth2[DPP_MAX_HASH_LEN];
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return -1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (auth->initiator) {
+ dpp_auth_fail(auth, "Unexpected Authentication Confirm");
+ return -1;
+ }
+
+ auth->waiting_auth_conf = 0;
+
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
- if (!wrapped_data) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing required Wrapped data attribute");
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
return -1;
}
wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
wrapped_data, wrapped_data_len);
- if (wrapped_data_len < AES_BLOCK_SIZE)
- return -1;
-
attr_len = wrapped_data - 4 - attr_start;
r_bootstrap = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap > wrapped_data ||
- r_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
return -1;
}
wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
@@ -2835,6 +3878,8 @@
wpa_hexdump(MSG_DEBUG,
"DPP: Expected Responder Bootstrapping Key Hash",
auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ dpp_auth_fail(auth,
+ "Responder Bootstrapping Key Hash mismatch");
return -1;
}
@@ -2842,10 +3887,9 @@
DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
if (i_bootstrap) {
- if (i_bootstrap > wrapped_data ||
- i_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
return -1;
}
wpa_hexdump(MSG_MSGDUMP,
@@ -2854,22 +3898,34 @@
if (!auth->peer_bi ||
os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
SHA256_MAC_LEN) != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash mismatch");
return -1;
}
+ } else if (auth->own_bi && auth->peer_bi) {
+ /* Mutual authentication and peer did not include its
+ * Bootstrapping Key Hash attribute. */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return -1;
}
status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
&status_len);
if (!status || status_len < 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required DPP Status attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
return -1;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
+ status[0] == DPP_STATUS_AUTH_FAILURE)
+ return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+
if (status[0] != DPP_STATUS_OK) {
- wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+ dpp_auth_fail(auth, "Authentication failed");
return -1;
}
@@ -2888,23 +3944,22 @@
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in unwrapped data");
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
&i_auth_len);
if (!i_auth || i_auth_len != auth->curve->hash_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid Initiator Authenticating Tag");
+ dpp_auth_fail(auth,
+ "Missing or invalid Initiator Authenticating Tag");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
@@ -2915,8 +3970,7 @@
wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
i_auth2, i_auth_len);
if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Mismatching Initiator Authenticating Tag");
+ dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
goto fail;
}
@@ -2952,6 +4006,7 @@
os_free(auth->connector);
wpabuf_free(auth->net_access_key);
wpabuf_free(auth->c_sign_key);
+ dpp_bootstrap_info_free(auth->tmp_own_bi);
#ifdef CONFIG_TESTING_OPTIONS
os_free(auth->config_obj_override);
os_free(auth->discovery_override);
@@ -2990,28 +4045,12 @@
json_escape_string(ssid, sizeof(ssid),
(const char *) conf->ssid, conf->ssid_len);
wpabuf_put_str(buf, ssid);
- wpabuf_put_str(buf, "\"");
- /* TODO: optional channel information */
- wpabuf_put_str(buf, "},");
+ wpabuf_put_str(buf, "\"},");
return buf;
}
-static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
-{
- int num_bytes, offset;
-
- num_bytes = BN_num_bytes(bn);
- if ((size_t) num_bytes > len)
- return -1;
- offset = len - num_bytes;
- os_memset(pos, 0, offset);
- BN_bn2bin(bn, pos + offset);
- return 0;
-}
-
-
static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
const char *kid, const struct dpp_curve_params *curve)
{
@@ -3208,7 +4247,7 @@
tailroom += signed1_len + signed2_len + signed3_len;
buf = dpp_build_conf_start(auth, conf, tailroom);
if (!buf)
- return NULL;
+ goto fail;
wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
wpabuf_put_str(buf, signed1);
@@ -3255,7 +4294,7 @@
if (!buf)
return NULL;
- wpabuf_put_str(buf, "\"cred\":{\"akm\":\"psk\",");
+ wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
if (conf->passphrase) {
char pass[63 * 6 + 1];
@@ -3308,7 +4347,7 @@
return NULL;
}
- if (conf->dpp)
+ if (conf->akm == DPP_AKM_DPP)
return dpp_build_conf_obj_dpp(auth, ap, conf);
return dpp_build_conf_obj_legacy(auth, ap, conf);
}
@@ -3319,7 +4358,7 @@
u16 e_nonce_len, int ap)
{
struct wpabuf *conf;
- size_t clear_len;
+ size_t clear_len, attr_len;
struct wpabuf *clear = NULL, *msg = NULL;
u8 *wrapped;
const u8 *addr[1];
@@ -3338,27 +4377,71 @@
if (conf)
clear_len += 4 + wpabuf_len(conf);
clear = wpabuf_alloc(clear_len);
- msg = wpabuf_alloc(4 + 1 + 4 + clear_len + AES_BLOCK_SIZE);
+ attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = wpabuf_alloc(attr_len);
if (!clear || !msg)
goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, e_nonce_len);
+ wpabuf_put_data(clear, e_nonce, e_nonce_len - 1);
+ wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01);
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* E-nonce */
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, e_nonce_len);
wpabuf_put_data(clear, e_nonce, e_nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_e_nonce:
+ if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - Config Object");
+ goto skip_config_obj;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
if (conf) {
wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
wpabuf_put_le16(clear, wpabuf_len(conf));
wpabuf_put_buf(clear, conf);
- wpabuf_free(conf);
- conf = NULL;
}
+#ifdef CONFIG_TESTING_OPTIONS
+skip_config_obj:
+ if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - Status");
+ goto skip_status;
+ }
+ if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 255;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* DPP Status */
- wpabuf_put_le16(msg, DPP_ATTR_STATUS);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, status);
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
addr[0] = wpabuf_head(msg);
len[0] = wpabuf_len(msg);
@@ -3375,17 +4458,26 @@
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
- wpabuf_free(clear);
- clear = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Configuration Response attributes", msg);
- return msg;
-fail:
+out:
wpabuf_free(conf);
wpabuf_free(clear);
+
+ return msg;
+fail:
wpabuf_free(msg);
- return NULL;
+ msg = NULL;
+ goto out;
}
@@ -3401,17 +4493,24 @@
struct json_token *root = NULL, *token;
int ap;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Config Request");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
if (dpp_check_attrs(attr_start, attr_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in config request");
+ dpp_auth_fail(auth, "Invalid attribute in config request");
return NULL;
}
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required Wrapped data attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
return NULL;
}
@@ -3424,15 +4523,14 @@
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
0, NULL, NULL, unwrapped) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in unwrapped data");
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
@@ -3440,8 +4538,8 @@
DPP_ATTR_ENROLLEE_NONCE,
&e_nonce_len);
if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid Enrollee Nonce attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid Enrollee Nonce attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
@@ -3450,8 +4548,8 @@
DPP_ATTR_CONFIG_ATTR_OBJ,
&config_attr_len);
if (!config_attr) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid Config Attributes attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid Config Attributes attribute");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
@@ -3459,32 +4557,33 @@
root = json_parse((const char *) config_attr, config_attr_len);
if (!root) {
- wpa_printf(MSG_DEBUG, "DPP: Could not parse Config Attributes");
+ dpp_auth_fail(auth, "Could not parse Config Attributes");
goto fail;
}
token = json_get_member(root, "name");
if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - name");
+ dpp_auth_fail(auth, "No Config Attributes - name");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
token = json_get_member(root, "wi-fi_tech");
if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - wi-fi_tech");
+ dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
if (os_strcmp(token->string, "infra") != 0) {
wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
token->string);
+ dpp_auth_fail(auth, "Unsupported wi-fi_tech");
goto fail;
}
token = json_get_member(root, "netRole");
if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - netRole");
+ dpp_auth_fail(auth, "No Config Attributes - netRole");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
@@ -3495,6 +4594,7 @@
} else {
wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
token->string);
+ dpp_auth_fail(auth, "Unsupported netRole");
goto fail;
}
@@ -3607,6 +4707,11 @@
os_strlcpy(auth->passphrase, pass->string,
sizeof(auth->passphrase));
} else if (psk_hex && psk_hex->type == JSON_STRING) {
+ if (auth->akm == DPP_AKM_SAE) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected psk_hex with akm=sae");
+ return -1;
+ }
if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
@@ -3620,6 +4725,12 @@
return -1;
}
+ if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
+ !auth->passphrase[0]) {
+ wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
+ return -1;
+ }
+
return 0;
}
@@ -3950,11 +5061,11 @@
size_t payload_len;
};
-static int
+static enum dpp_status_error
dpp_process_signed_connector(struct dpp_signed_connector_info *info,
EVP_PKEY *csign_pub, const char *connector)
{
- int ret = -1;
+ enum dpp_status_error ret = 255;
const char *pos, *end, *signed_start, *signed_end;
struct wpabuf *kid = NULL;
unsigned char *prot_hdr = NULL, *signature = NULL;
@@ -3988,6 +5099,7 @@
end = os_strchr(pos, '.');
if (!end) {
wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
prot_hdr = base64_url_decode((const unsigned char *) pos,
@@ -3995,18 +5107,22 @@
if (!prot_hdr) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to base64url decode signedConnector JWS Protected Header");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG,
"DPP: signedConnector - JWS Protected Header",
prot_hdr, prot_hdr_len);
kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
- if (!kid)
+ if (!kid) {
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
+ }
if (wpabuf_len(kid) != SHA256_MAC_LEN) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
(unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
@@ -4015,6 +5131,7 @@
if (!end) {
wpa_printf(MSG_DEBUG,
"DPP: Missing dot(2) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
signed_end = end - 1;
@@ -4023,6 +5140,7 @@
if (!info->payload) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to base64url decode signedConnector JWS Payload");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG,
@@ -4034,18 +5152,22 @@
if (!signature) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to base64url decode signedConnector signature");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
signature, signature_len);
- if (dpp_check_pubkey_match(csign_pub, kid) < 0)
+ if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
+ ret = DPP_STATUS_NO_MATCH;
goto fail;
+ }
if (signature_len & 0x01) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected signedConnector signature length (%d)",
(int) signature_len);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
@@ -4086,10 +5208,11 @@
wpa_printf(MSG_DEBUG,
"DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
res, ERR_error_string(ERR_get_error(), NULL));
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
- ret = 0;
+ ret = DPP_STATUS_OK;
fail:
EC_KEY_free(eckey);
EVP_MD_CTX_destroy(md_ctx);
@@ -4148,7 +5271,7 @@
}
if (dpp_process_signed_connector(&info, csign_pub,
- signed_connector) < 0)
+ signed_connector) != DPP_STATUS_OK)
goto fail;
if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
@@ -4170,6 +5293,37 @@
}
+const char * dpp_akm_str(enum dpp_akm akm)
+{
+ switch (akm) {
+ case DPP_AKM_DPP:
+ return "dpp";
+ case DPP_AKM_PSK:
+ return "psk";
+ case DPP_AKM_SAE:
+ return "sae";
+ case DPP_AKM_PSK_SAE:
+ return "psk+sae";
+ default:
+ return "??";
+ }
+}
+
+
+static enum dpp_akm dpp_akm_from_str(const char *akm)
+{
+ if (os_strcmp(akm, "psk") == 0)
+ return DPP_AKM_PSK;
+ if (os_strcmp(akm, "sae") == 0)
+ return DPP_AKM_SAE;
+ if (os_strcmp(akm, "psk+sae") == 0)
+ return DPP_AKM_PSK_SAE;
+ if (os_strcmp(akm, "dpp") == 0)
+ return DPP_AKM_DPP;
+ return DPP_AKM_UNKNOWN;
+}
+
+
static int dpp_parse_conf_obj(struct dpp_authentication *auth,
const u8 *conf_obj, u16 conf_obj_len)
{
@@ -4180,38 +5334,37 @@
if (!root)
return -1;
if (root->type != JSON_OBJECT) {
- wpa_printf(MSG_DEBUG, "DPP: JSON root is not an object");
+ dpp_auth_fail(auth, "JSON root is not an object");
goto fail;
}
token = json_get_member(root, "wi-fi_tech");
if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG, "DPP: No wi-fi_tech string value found");
+ dpp_auth_fail(auth, "No wi-fi_tech string value found");
goto fail;
}
if (os_strcmp(token->string, "infra") != 0) {
wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
token->string);
+ dpp_auth_fail(auth, "Unsupported wi-fi_tech value");
goto fail;
}
discovery = json_get_member(root, "discovery");
if (!discovery || discovery->type != JSON_OBJECT) {
- wpa_printf(MSG_DEBUG, "DPP: No discovery object in JSON");
+ dpp_auth_fail(auth, "No discovery object in JSON");
goto fail;
}
token = json_get_member(discovery, "ssid");
if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: No discovery::ssid string value found");
+ dpp_auth_fail(auth, "No discovery::ssid string value found");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
token->string, os_strlen(token->string));
if (os_strlen(token->string) > SSID_MAX_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Too long discovery::ssid string value");
+ dpp_auth_fail(auth, "Too long discovery::ssid string value");
goto fail;
}
auth->ssid_len = os_strlen(token->string);
@@ -4219,25 +5372,28 @@
cred = json_get_member(root, "cred");
if (!cred || cred->type != JSON_OBJECT) {
- wpa_printf(MSG_DEBUG, "DPP: No cred object in JSON");
+ dpp_auth_fail(auth, "No cred object in JSON");
goto fail;
}
token = json_get_member(cred, "akm");
if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: No cred::akm string value found");
+ dpp_auth_fail(auth, "No cred::akm string value found");
goto fail;
}
- if (os_strcmp(token->string, "psk") == 0) {
+ auth->akm = dpp_akm_from_str(token->string);
+
+ if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE ||
+ auth->akm == DPP_AKM_PSK_SAE) {
if (dpp_parse_cred_legacy(auth, cred) < 0)
goto fail;
- } else if (os_strcmp(token->string, "dpp") == 0) {
+ } else if (auth->akm == DPP_AKM_DPP) {
if (dpp_parse_cred_dpp(auth, cred) < 0)
goto fail;
} else {
wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
token->string);
+ dpp_auth_fail(auth, "Unsupported akm");
goto fail;
}
@@ -4261,8 +5417,7 @@
int ret = -1;
if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in config response");
+ dpp_auth_fail(auth, "Invalid attribute in config response");
return -1;
}
@@ -4270,8 +5425,8 @@
DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required Wrapped data attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
return -1;
}
@@ -4289,15 +5444,14 @@
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
1, addr, len, unwrapped) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in unwrapped data");
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
@@ -4305,34 +5459,34 @@
DPP_ATTR_ENROLLEE_NONCE,
&e_nonce_len);
if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid Enrollee Nonce attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid Enrollee Nonce attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
- wpa_printf(MSG_DEBUG, "Enrollee Nonce mismatch");
+ dpp_auth_fail(auth, "Enrollee Nonce mismatch");
goto fail;
}
status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
DPP_ATTR_STATUS, &status_len);
if (!status || status_len < 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required DPP Status attribute");
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
if (status[0] != DPP_STATUS_OK) {
- wpa_printf(MSG_DEBUG, "DPP: Configuration failed");
+ dpp_auth_fail(auth, "Configurator rejected configuration");
goto fail;
}
conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
if (!conf_obj) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing required Configuration Object attribute");
+ dpp_auth_fail(auth,
+ "Missing required Configuration Object attribute");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
@@ -4358,6 +5512,30 @@
}
+int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
+ size_t buflen)
+{
+ EC_KEY *eckey;
+ int key_len, ret = -1;
+ unsigned char *key = NULL;
+
+ if (!conf->csign)
+ return -1;
+
+ eckey = EVP_PKEY_get1_EC_KEY(conf->csign);
+ if (!eckey)
+ return -1;
+
+ key_len = i2d_ECPrivateKey(eckey, &key);
+ if (key_len > 0)
+ ret = wpa_snprintf_hex(buf, buflen, key, key_len);
+
+ EC_KEY_free(eckey);
+ OPENSSL_free(key);
+ return ret;
+}
+
+
struct dpp_configurator *
dpp_keygen_configurator(const char *curve, const u8 *privkey,
size_t privkey_len)
@@ -4421,7 +5599,7 @@
int dpp_configurator_own_config(struct dpp_authentication *auth,
- const char *curve)
+ const char *curve, int ap)
{
struct wpabuf *conf_obj;
int ret = -1;
@@ -4452,7 +5630,7 @@
auth->peer_protocol_key = auth->own_protocol_key;
dpp_copy_csign(auth, auth->conf->csign);
- conf_obj = dpp_build_conf_obj(auth, 0);
+ conf_obj = dpp_build_conf_obj(auth, ap);
if (!conf_obj)
goto fail;
ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
@@ -4615,15 +5793,16 @@
}
-int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
- const u8 *net_access_key, size_t net_access_key_len,
- const u8 *csign_key, size_t csign_key_len,
- const u8 *peer_connector, size_t peer_connector_len,
- os_time_t *expiry)
+enum dpp_status_error
+dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len,
+ os_time_t *expiry)
{
struct json_token *root = NULL, *netkey, *token;
struct json_token *own_root = NULL;
- int ret = -1;
+ enum dpp_status_error ret = 255, res;
EVP_PKEY *own_key = NULL, *peer_key = NULL;
struct wpabuf *own_key_pub = NULL;
const struct dpp_curve_params *curve, *own_curve;
@@ -4691,18 +5870,23 @@
os_memcpy(signed_connector, peer_connector, peer_connector_len);
signed_connector[peer_connector_len] = '\0';
- if (dpp_process_signed_connector(&info, csign, signed_connector) < 0)
+ res = dpp_process_signed_connector(&info, csign, signed_connector);
+ if (res != DPP_STATUS_OK) {
+ ret = res;
goto fail;
+ }
root = json_parse((const char *) info.payload, info.payload_len);
if (!root) {
wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
if (!dpp_connector_match_groups(own_root, root)) {
wpa_printf(MSG_DEBUG,
"DPP: Peer connector does not include compatible group netrole with own connector");
+ ret = DPP_STATUS_NO_MATCH;
goto fail;
}
@@ -4715,6 +5899,7 @@
if (dpp_key_expired(token->string, expiry)) {
wpa_printf(MSG_DEBUG,
"DPP: Connector (netAccessKey) has expired");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
}
@@ -4722,18 +5907,22 @@
netkey = json_get_member(root, "netAccessKey");
if (!netkey || netkey->type != JSON_OBJECT) {
wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
peer_key = dpp_parse_jwk(netkey, &curve);
- if (!peer_key)
+ if (!peer_key) {
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
+ }
dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
if (own_curve != curve) {
wpa_printf(MSG_DEBUG,
"DPP: Mismatching netAccessKey curves (%s != %s)",
own_curve->name, curve->name);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
@@ -4767,9 +5956,9 @@
goto fail;
}
- ret = 0;
+ ret = DPP_STATUS_OK;
fail:
- if (ret < 0)
+ if (ret != DPP_STATUS_OK)
os_memset(intro, 0, sizeof(*intro));
os_memset(Nx, 0, sizeof(Nx));
EVP_PKEY_CTX_free(ctx);
@@ -4893,9 +6082,10 @@
EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
goto fail;
if (EC_POINT_is_at_infinity(group, Qi)) {
- wpa_printf(MSG_INFO, "PDP: Qi is the point-at-infinity");
+ wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
goto fail;
}
+ dpp_debug_print_point("DPP: Qi", group, Qi);
out:
EC_KEY_free(Pi_ec);
EVP_PKEY_free(Pi);
@@ -4973,6 +6163,11 @@
if (!hash_bn ||
EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
goto fail;
+ if (EC_POINT_is_at_infinity(group, Qr)) {
+ wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qr", group, Qr);
out:
EC_KEY_free(Pr_ec);
EVP_PKEY_free(Pr);
@@ -4987,6 +6182,72 @@
}
+#ifdef CONFIG_TESTING_OPTIONS
+static int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve)
+{
+ BN_CTX *ctx;
+ BIGNUM *x, *y;
+ int ret = -1;
+ EC_GROUP *group;
+ EC_POINT *point;
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return -1;
+
+ ctx = BN_CTX_new();
+ point = EC_POINT_new(group);
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !point || !x || !y)
+ goto fail;
+
+ if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ /* Generate a random y coordinate that results in a point that is not
+ * on the curve. */
+ for (;;) {
+ if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
+ ctx) != 1) {
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L || defined(OPENSSL_IS_BORINGSSL)
+ /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
+ * return an error from EC_POINT_set_affine_coordinates_GFp()
+ * when the point is not on the curve. */
+ break;
+#else /* >=1.1.1 or OPENSSL_IS_BORINGSSL */
+ goto fail;
+#endif /* >= 1.1.1 or OPENSSL_IS_BORINGSSL */
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx))
+ break;
+ }
+
+ if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ if (ret < 0)
+ wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
+ BN_free(x);
+ BN_free(y);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+
+ return ret;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
{
EC_KEY *X_ec = NULL;
@@ -4999,7 +6260,6 @@
struct wpabuf *msg = NULL;
size_t attr_len;
const struct dpp_curve_params *curve = pkex->own_bi->curve;
- int num_bytes, offset;
wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
@@ -5013,7 +6273,21 @@
goto fail;
/* Generate a random ephemeral keypair x/X */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key x/X");
+ pkex->x = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->x = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
pkex->x = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
if (!pkex->x)
goto fail;
@@ -5024,6 +6298,7 @@
X_point = EC_KEY_get0_public_key(X_ec);
if (!X_point)
goto fail;
+ dpp_debug_print_point("DPP: X", group, X_point);
M = EC_POINT_new(group);
Mx = BN_new();
My = BN_new();
@@ -5031,6 +6306,7 @@
EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
goto fail;
+ dpp_debug_print_point("DPP: M", group, M);
/* Initiator -> Responder: group, [identifier,] M */
attr_len = 4 + 2;
@@ -5041,11 +6317,22 @@
if (!msg)
goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
+ goto skip_finite_cyclic_group;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* Finite Cyclic Group attribute */
wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
wpabuf_put_le16(msg, 2);
wpabuf_put_le16(msg, curve->ike_group);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_finite_cyclic_group:
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* Code Identifier attribute */
if (pkex->identifier) {
wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
@@ -5053,31 +6340,32 @@
wpabuf_put_str(msg, pkex->identifier);
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* M in Encrypted Key attribute */
wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
wpabuf_put_le16(msg, 2 * curve->prime_len);
- num_bytes = BN_num_bytes(Mx);
- if ((size_t) num_bytes > curve->prime_len)
- goto fail;
- if (curve->prime_len > (size_t) num_bytes)
- offset = curve->prime_len - num_bytes;
- else
- offset = 0;
- os_memset(wpabuf_put(msg, offset), 0, offset);
- BN_bn2bin(Mx, wpabuf_put(msg, num_bytes));
- os_memset(pkex->Mx, 0, offset);
- BN_bn2bin(Mx, pkex->Mx + offset);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
- num_bytes = BN_num_bytes(My);
- if ((size_t) num_bytes > curve->prime_len)
+ if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
goto fail;
- if (curve->prime_len > (size_t) num_bytes)
- offset = curve->prime_len - num_bytes;
- else
- offset = 0;
- os_memset(wpabuf_put(msg, offset), 0, offset);
- BN_bn2bin(My, wpabuf_put(msg, num_bytes));
out:
wpabuf_free(M_buf);
@@ -5096,16 +6384,31 @@
}
-struct dpp_pkex * dpp_pkex_init(struct dpp_bootstrap_info *bi,
+static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
+{
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const char *identifier,
const char *code)
{
struct dpp_pkex *pkex;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
pkex = os_zalloc(sizeof(*pkex));
if (!pkex)
return NULL;
+ pkex->msg_ctx = msg_ctx;
pkex->initiator = 1;
pkex->own_bi = bi;
os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
@@ -5127,158 +6430,42 @@
}
-struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
- const u8 *own_mac,
- const u8 *peer_mac,
- const char *identifier,
- const char *code,
- const u8 *buf, size_t len)
+static struct wpabuf *
+dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
+ enum dpp_status_error status,
+ const BIGNUM *Nx, const BIGNUM *Ny)
{
- const u8 *attr_group, *attr_id, *attr_key;
- u16 attr_group_len, attr_id_len, attr_key_len;
- const struct dpp_curve_params *curve = bi->curve;
- u16 ike_group;
- struct dpp_pkex *pkex = NULL;
- EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
- BN_CTX *bnctx = NULL;
- const EC_GROUP *group;
- BIGNUM *Mx = NULL, *My = NULL;
- EC_KEY *Y_ec = NULL, *X_ec = NULL;;
- const EC_POINT *Y_point;
- BIGNUM *Nx = NULL, *Ny = NULL;
struct wpabuf *msg = NULL;
size_t attr_len;
- int num_bytes, offset;
-
- attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
- &attr_id_len);
- if (!attr_id && identifier) {
- wpa_printf(MSG_DEBUG,
- "DPP: No PKEX code identifier received, but expected one");
- return NULL;
- }
- if (attr_id && identifier &&
- (os_strlen(identifier) != attr_id_len ||
- os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
- wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
- return NULL;
- }
-
- attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
- &attr_group_len);
- if (!attr_group || attr_group_len != 2) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid Finite Cyclic Group attribute");
- return NULL;
- }
- ike_group = WPA_GET_LE16(attr_group);
- if (ike_group != curve->ike_group) {
- wpa_printf(MSG_DEBUG,
- "DPP: Mismatching PKEX curve: peer=%u own=%u",
- ike_group, curve->ike_group);
- /* TODO: error response with suggested curve:
- * DPP Status, group */
- return NULL;
- }
-
- /* M in Encrypted Key attribute */
- attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
- &attr_key_len);
- if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
- attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
- wpa_printf(MSG_DEBUG, "DPP: Missing Encrypted Key attribute");
- return NULL;
- }
-
- /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
- Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
- &group);
- if (!Qi)
- goto fail;
-
- /* X' = M - Qi */
- X = EC_POINT_new(group);
- M = EC_POINT_new(group);
- Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
- My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
- if (!X || !M || !Mx || !My ||
- EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, M) ||
- !EC_POINT_is_on_curve(group, M, bnctx) ||
- EC_POINT_invert(group, Qi, bnctx) != 1 ||
- EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, X) ||
- !EC_POINT_is_on_curve(group, X, bnctx))
- goto fail;
-
- pkex = os_zalloc(sizeof(*pkex));
- if (!pkex)
- goto fail;
- pkex->own_bi = bi;
- os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
- os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
- if (identifier) {
- pkex->identifier = os_strdup(identifier);
- if (!pkex->identifier)
- goto fail;
- }
- pkex->code = os_strdup(code);
- if (!pkex->code)
- goto fail;
-
- os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
-
- X_ec = EC_KEY_new();
- if (!X_ec ||
- EC_KEY_set_group(X_ec, group) != 1 ||
- EC_KEY_set_public_key(X_ec, X) != 1)
- goto fail;
- pkex->x = EVP_PKEY_new();
- if (!pkex->x ||
- EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
- goto fail;
-
- /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
- Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
- if (!Qr)
- goto fail;
-
- /* Generate a random ephemeral keypair y/Y */
- pkex->y = dpp_gen_keypair(curve);
- if (!pkex->y)
- goto fail;
-
- /* N = Y + Qr */
- Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
- if (!Y_ec)
- goto fail;
- Y_point = EC_KEY_get0_public_key(Y_ec);
- if (!Y_point)
- goto fail;
- N = EC_POINT_new(group);
- Nx = BN_new();
- Ny = BN_new();
- if (!N || !Nx || !Ny ||
- EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
- goto fail;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
/* Initiator -> Responder: DPP Status, [identifier,] N */
attr_len = 4 + 1;
- if (identifier)
- attr_len += 4 + os_strlen(identifier);
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
attr_len += 4 + 2 * curve->prime_len;
msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
if (!msg)
goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ }
+
+ if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 255;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* DPP Status */
- wpabuf_put_le16(msg, DPP_ATTR_STATUS);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, DPP_STATUS_OK);
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
/* Code Identifier attribute */
if (pkex->identifier) {
@@ -5287,56 +6474,48 @@
wpabuf_put_str(msg, pkex->identifier);
}
+ if (status != DPP_STATUS_OK)
+ goto skip_encrypted_key;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
/* N in Encrypted Key attribute */
wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
wpabuf_put_le16(msg, 2 * curve->prime_len);
- num_bytes = BN_num_bytes(Nx);
- if ((size_t) num_bytes > curve->prime_len)
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
goto fail;
- if (curve->prime_len > (size_t) num_bytes)
- offset = curve->prime_len - num_bytes;
- else
- offset = 0;
- os_memset(wpabuf_put(msg, offset), 0, offset);
- BN_bn2bin(Nx, wpabuf_put(msg, num_bytes));
- os_memset(pkex->Nx, 0, offset);
- BN_bn2bin(Nx, pkex->Nx + offset);
- num_bytes = BN_num_bytes(Ny);
- if ((size_t) num_bytes > curve->prime_len)
- goto fail;
- if (curve->prime_len > (size_t) num_bytes)
- offset = curve->prime_len - num_bytes;
- else
- offset = 0;
- os_memset(wpabuf_put(msg, offset), 0, offset);
- BN_bn2bin(Ny, wpabuf_put(msg, num_bytes));
+skip_encrypted_key:
+ if (status == DPP_STATUS_BAD_GROUP) {
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+ }
- pkex->exchange_resp = msg;
- msg = NULL;
- pkex->exchange_done = 1;
-
-out:
- wpabuf_free(msg);
- BN_CTX_free(bnctx);
- EC_POINT_free(Qi);
- EC_POINT_free(Qr);
- BN_free(Mx);
- BN_free(My);
- BN_free(Nx);
- BN_free(Ny);
- EC_POINT_free(M);
- EC_POINT_free(N);
- EC_POINT_free(X);
- EC_KEY_free(X_ec);
- EC_KEY_free(Y_ec);
- return pkex;
+ return msg;
fail:
- wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing faileed");
- dpp_pkex_free(pkex);
- pkex = NULL;
- goto out;
+ wpabuf_free(msg);
+ return NULL;
}
@@ -5399,16 +6578,376 @@
}
-struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
- const u8 *buf, size_t buflen)
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+ struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const u8 *peer_mac,
+ const char *identifier,
+ const char *code,
+ const u8 *buf, size_t len)
{
- const u8 *attr_status, *attr_id, *attr_key;
- u16 attr_status_len, attr_id_len, attr_key_len;
- const EC_GROUP *group;
+ const u8 *attr_group, *attr_id, *attr_key;
+ u16 attr_group_len, attr_id_len, attr_key_len;
+ const struct dpp_curve_params *curve = bi->curve;
+ u16 ike_group;
+ struct dpp_pkex *pkex = NULL;
+ EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
BN_CTX *bnctx = NULL;
- size_t clear_len;
+ const EC_GROUP *group;
+ BIGNUM *Mx = NULL, *My = NULL;
+ EC_KEY *Y_ec = NULL, *X_ec = NULL;;
+ const EC_POINT *Y_point;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Kx_len;
+ int res;
+ EVP_PKEY_CTX *ctx = NULL;
+
+ if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "PKEX counter t limit reached - ignore message");
+ return NULL;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!attr_id && identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PKEX code identifier received, but expected one");
+ return NULL;
+ }
+ if (attr_id && identifier &&
+ (os_strlen(identifier) != attr_id_len ||
+ os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+ return NULL;
+ }
+
+ attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (!attr_group || attr_group_len != 2) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid Finite Cyclic Group attribute");
+ return NULL;
+ }
+ ike_group = WPA_GET_LE16(attr_group);
+ if (ike_group != curve->ike_group) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Mismatching PKEX curve: peer=%u own=%u",
+ ike_group, curve->ike_group);
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->own_bi = bi;
+ pkex->failed = 1;
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(
+ pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
+ if (!pkex->exchange_resp)
+ goto fail;
+ return pkex;
+ }
+
+ /* M in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
+ attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
+ &group);
+ if (!Qi)
+ goto fail;
+
+ /* X' = M - Qi */
+ X = EC_POINT_new(group);
+ M = EC_POINT_new(group);
+ Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!X || !M || !Mx || !My ||
+ EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, M) ||
+ !EC_POINT_is_on_curve(group, M, bnctx) ||
+ EC_POINT_invert(group, Qi, bnctx) != 1 ||
+ EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, X) ||
+ !EC_POINT_is_on_curve(group, X, bnctx)) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Invalid Encrypted Key value");
+ bi->pkex_t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: M", group, M);
+ dpp_debug_print_point("DPP: X'", group, X);
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->t = bi->pkex_t;
+ pkex->msg_ctx = msg_ctx;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+
+ os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
+
+ X_ec = EC_KEY_new();
+ if (!X_ec ||
+ EC_KEY_set_group(X_ec, group) != 1 ||
+ EC_KEY_set_public_key(X_ec, X) != 1)
+ goto fail;
+ pkex->x = EVP_PKEY_new();
+ if (!pkex->x ||
+ EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+ goto fail;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+ Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+ if (!Qr)
+ goto fail;
+
+ /* Generate a random ephemeral keypair y/Y */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key y/Y");
+ pkex->y = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->y = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->y = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->y)
+ goto fail;
+
+ /* N = Y + Qr */
+ Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
+ if (!Y_ec)
+ goto fail;
+ Y_point = EC_KEY_get0_public_key(Y_ec);
+ if (!Y_point)
+ goto fail;
+ dpp_debug_print_point("DPP: Y", group, Y_point);
+ N = EC_POINT_new(group);
+ Nx = BN_new();
+ Ny = BN_new();
+ if (!N || !Nx || !Ny ||
+ EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: N", group, N);
+
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
+ Nx, Ny);
+ if (!pkex->exchange_resp)
+ goto fail;
+
+ /* K = y * X' */
+ ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
+ Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+ pkex->Mx, curve->prime_len,
+ pkex->Nx, curve->prime_len, pkex->code,
+ Kx, Kx_len, pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ pkex->exchange_done = 1;
+
+out:
+ EVP_PKEY_CTX_free(ctx);
+ BN_CTX_free(bnctx);
+ EC_POINT_free(Qi);
+ EC_POINT_free(Qr);
+ BN_free(Mx);
+ BN_free(My);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_POINT_free(M);
+ EC_POINT_free(N);
+ EC_POINT_free(X);
+ EC_KEY_free(X_ec);
+ EC_KEY_free(Y_ec);
+ return pkex;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
+ dpp_pkex_free(pkex);
+ pkex = NULL;
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
+ const struct wpabuf *A_pub, const u8 *u)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ size_t clear_len, attr_len;
struct wpabuf *clear = NULL;
u8 *wrapped;
+ u8 octet;
+ const u8 *addr[2];
+ size_t len[2];
+
+ /* {A, u, [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* A in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(A_pub));
+ wpabuf_put_buf(clear, A_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
+ goto skip_i_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len - 1);
+ wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
+ goto skip_i_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* u in I-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+ const u8 *peer_mac,
+ const u8 *buf, size_t buflen)
+{
+ const u8 *attr_status, *attr_id, *attr_key, *attr_group;
+ u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
+ const EC_GROUP *group;
+ BN_CTX *bnctx = NULL;
struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
const struct dpp_curve_params *curve = pkex->own_bi->curve;
EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
@@ -5420,18 +6959,50 @@
const u8 *addr[4];
size_t len[4];
u8 u[DPP_MAX_HASH_LEN];
- u8 octet;
int res;
+ if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX Exchange Response");
+ pkex->failed = 1;
+ return NULL;
+ }
+
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+
attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
&attr_status_len);
if (!attr_status || attr_status_len != 1) {
- wpa_printf(MSG_DEBUG, "DPP: No DPP Status attribute");
+ dpp_pkex_fail(pkex, "No DPP Status attribute");
return NULL;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+
+ if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
+ attr_group = dpp_get_attr(buf, buflen,
+ DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (attr_group && attr_group_len == 2) {
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Peer indicated mismatching PKEX group - proposed %u",
+ WPA_GET_LE16(attr_group));
+ return NULL;
+ }
+ }
+
if (attr_status[0] != DPP_STATUS_OK) {
- wpa_printf(MSG_DEBUG, "DPP: PKEX failed");
+ dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
return NULL;
}
@@ -5445,7 +7016,7 @@
if (attr_id && pkex->identifier &&
(os_strlen(pkex->identifier) != attr_id_len ||
os_memcmp(pkex->identifier, attr_id, attr_id_len) != 0)) {
- wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+ dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
return NULL;
}
@@ -5453,7 +7024,7 @@
attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
&attr_key_len);
if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
- wpa_printf(MSG_DEBUG, "DPP: Missing Encrypted Key attribute");
+ dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
return NULL;
}
@@ -5478,8 +7049,13 @@
EC_POINT_invert(group, Qr, bnctx) != 1 ||
EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
EC_POINT_is_at_infinity(group, Y) ||
- !EC_POINT_is_on_curve(group, Y, bnctx))
+ !EC_POINT_is_on_curve(group, Y, bnctx)) {
+ dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
+ pkex->t++;
goto fail;
+ }
+ dpp_debug_print_point("DPP: N", group, N);
+ dpp_debug_print_point("DPP: Y'", group, Y);
pkex->exchange_done = 1;
@@ -5556,27 +7132,106 @@
if (res < 0)
goto fail;
- /* {A, u, [bootstrapping info]}z */
+ msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
+ if (!msg)
+ goto fail;
+
+out:
+ wpabuf_free(A_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ EC_POINT_free(Qr);
+ EC_POINT_free(Y);
+ EC_POINT_free(N);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_KEY_free(Y_ec);
+ EVP_PKEY_CTX_free(ctx);
+ BN_CTX_free(bnctx);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
+ const struct wpabuf *B_pub, const u8 *v)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 octet;
+ u8 *wrapped;
+ struct wpabuf *clear = NULL;
+ size_t clear_len, attr_len;
+
+ /* {B, v [bootstrapping info]}z */
clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
clear = wpabuf_alloc(clear_len);
- msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ,
- 4 + clear_len + AES_BLOCK_SIZE);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
if (!clear || !msg)
goto fail;
- /* A in Bootstrap Key attribute */
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, wpabuf_len(A_pub));
- wpabuf_put_buf(clear, A_pub);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
- /* u in I-Auth tag attribute */
- wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ /* B in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(B_pub));
+ wpabuf_put_buf(clear, B_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
+ goto skip_r_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len - 1);
+ wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
+ goto skip_r_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* v in R-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, u, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_r_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = DPP_HDR_LEN;
- octet = 0;
+ octet = 1;
addr[1] = &octet;
len[1] = sizeof(octet);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
@@ -5594,22 +7249,19 @@
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
out:
wpabuf_free(clear);
- wpabuf_free(A_pub);
- wpabuf_free(X_pub);
- wpabuf_free(Y_pub);
- EC_POINT_free(Qr);
- EC_POINT_free(Y);
- EC_POINT_free(N);
- BN_free(Nx);
- BN_free(Ny);
- EC_KEY_free(Y_ec);
- EVP_PKEY_CTX_free(ctx);
- BN_CTX_free(bnctx);
return msg;
+
fail:
- wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing faileed");
wpabuf_free(msg);
msg = NULL;
goto out;
@@ -5621,9 +7273,9 @@
const u8 *buf, size_t buflen)
{
const struct dpp_curve_params *curve = pkex->own_bi->curve;
- EVP_PKEY_CTX *ctx;
- size_t Jx_len, Kx_len, Lx_len;
- u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t Jx_len, Lx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
const u8 *wrapped_data, *b_key, *peer_u;
u16 wrapped_data_len, b_key_len, peer_u_len = 0;
@@ -5635,43 +7287,25 @@
struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
struct wpabuf *B_pub = NULL;
u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
- size_t clear_len;
- struct wpabuf *clear = NULL;
- u8 *wrapped;
- int res;
- /* K = y * X' */
- ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
- if (!ctx ||
- EVP_PKEY_derive_init(ctx) != 1 ||
- EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
- EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
- Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
- EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to derive ECDH shared secret: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Request");
+ pkex->failed = 1;
+ return NULL;
}
+#endif /* CONFIG_TESTING_OPTIONS */
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
- Kx, Kx_len);
-
- /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
- */
- res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
- pkex->Mx, curve->prime_len,
- pkex->Nx, curve->prime_len, pkex->code,
- Kx, Kx_len, pkex->z, curve->hash_len);
- os_memset(Kx, 0, Kx_len);
- if (res < 0)
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
goto fail;
wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required Wrapped data attribute");
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
goto fail;
}
@@ -5693,34 +7327,36 @@
if (aes_siv_decrypt(pkex->z, curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->failed = 1;
+ pkex->t++;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in unwrapped data");
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
goto fail;
}
b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
&b_key_len);
if (!b_key || b_key_len != 2 * curve->prime_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: No valid peer bootstrapping key found");
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
goto fail;
}
pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
b_key_len);
- if (!pkex->peer_bootstrap_key)
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
goto fail;
+ }
dpp_debug_print_key("DPP: Peer bootstrap public key",
pkex->peer_bootstrap_key);
/* ECDH: J' = y * A' */
- EVP_PKEY_CTX_free(ctx);
ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
if (!ctx ||
EVP_PKEY_derive_init(ctx) != 1 ||
@@ -5758,10 +7394,11 @@
&peer_u_len);
if (!peer_u || peer_u_len != curve->hash_len ||
os_memcmp(peer_u, u, curve->hash_len) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: No valid u (I-Auth tag) found");
+ dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
u, curve->hash_len);
wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+ pkex->t++;
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
@@ -5800,43 +7437,10 @@
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
- /* {B, v [bootstrapping info]}z */
- clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
- clear = wpabuf_alloc(clear_len);
- msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP,
- 4 + clear_len + AES_BLOCK_SIZE);
- if (!clear || !msg)
+ msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
+ if (!msg)
goto fail;
- /* A in Bootstrap Key attribute */
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, wpabuf_len(B_pub));
- wpabuf_put_buf(clear, B_pub);
-
- /* v in R-Auth tag attribute */
- wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, v, curve->hash_len);
-
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = DPP_HDR_LEN;
- octet = 1;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
- wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
- wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
- if (aes_siv_encrypt(pkex->z, curve->hash_len,
- wpabuf_head(clear), wpabuf_len(clear),
- 2, addr, len, wrapped) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
out:
EVP_PKEY_CTX_free(ctx);
os_free(unwrapped);
@@ -5844,11 +7448,10 @@
wpabuf_free(B_pub);
wpabuf_free(X_pub);
wpabuf_free(Y_pub);
- wpabuf_free(clear);
return msg;
fail:
- wpabuf_free(msg);
- msg = NULL;
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX Commit-Reveal Request processing failed");
goto out;
}
@@ -5871,11 +7474,24 @@
EVP_PKEY_CTX *ctx = NULL;
struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Response");
+ pkex->failed = 1;
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ goto fail;
+
wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing or invalid required Wrapped data attribute");
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
goto fail;
}
@@ -5897,29 +7513,31 @@
if (aes_siv_decrypt(pkex->z, curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->t++;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Invalid attribute in unwrapped data");
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
goto fail;
}
b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
&b_key_len);
if (!b_key || b_key_len != 2 * curve->prime_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: No valid peer bootstrapping key found");
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
goto fail;
}
pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
b_key_len);
- if (!pkex->peer_bootstrap_key)
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
goto fail;
+ }
dpp_debug_print_key("DPP: Peer bootstrap public key",
pkex->peer_bootstrap_key);
@@ -5961,10 +7579,11 @@
&peer_v_len);
if (!peer_v || peer_v_len != curve->hash_len ||
os_memcmp(peer_v, v, curve->hash_len) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: No valid v (R-Auth tag) found");
+ dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
v, curve->hash_len);
wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+ pkex->t++;
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
@@ -5996,3 +7615,56 @@
wpabuf_free(pkex->exchange_resp);
os_free(pkex);
}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+char * dpp_corrupt_connector_signature(const char *connector)
+{
+ char *tmp, *pos, *signed3 = NULL;
+ unsigned char *signature = NULL;
+ size_t signature_len = 0, signed3_len;
+
+ tmp = os_zalloc(os_strlen(connector) + 5);
+ if (!tmp)
+ goto fail;
+ os_memcpy(tmp, connector, os_strlen(connector));
+
+ pos = os_strchr(tmp, '.');
+ if (!pos)
+ goto fail;
+
+ pos = os_strchr(pos + 1, '.');
+ if (!pos)
+ goto fail;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
+ pos);
+ signature = base64_url_decode((const unsigned char *) pos,
+ os_strlen(pos), &signature_len);
+ if (!signature || signature_len == 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
+ signature, signature_len);
+ signature[signature_len - 1] ^= 0x01;
+ wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
+ signature, signature_len);
+ signed3 = (char *) base64_url_encode(signature, signature_len,
+ &signed3_len, 0);
+ if (!signed3)
+ goto fail;
+ os_memcpy(pos, signed3, signed3_len);
+ pos[signed3_len] = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
+ pos);
+
+out:
+ os_free(signature);
+ os_free(signed3);
+ return tmp;
+fail:
+ os_free(tmp);
+ tmp = NULL;
+ goto out;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index dfee499..185a31c 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -52,6 +52,8 @@
DPP_ATTR_ENROLLEE_NONCE = 0x1014,
DPP_ATTR_CODE_IDENTIFIER = 0x1015,
DPP_ATTR_TRANSACTION_ID = 0x1016,
+ DPP_ATTR_BOOTSTRAP_INFO = 0x1017,
+ DPP_ATTR_CHANNEL = 0x1018,
};
enum dpp_status_error {
@@ -62,6 +64,8 @@
DPP_STATUS_BAD_GROUP = 4,
DPP_STATUS_CONFIGURE_FAILURE = 5,
DPP_STATUS_RESPONSE_PENDING = 6,
+ DPP_STATUS_INVALID_CONNECTOR = 7,
+ DPP_STATUS_NO_MATCH = 8,
};
#define DPP_CAPAB_ENROLLEE BIT(0)
@@ -102,11 +106,17 @@
EVP_PKEY *pubkey;
u8 pubkey_hash[SHA256_MAC_LEN];
const struct dpp_curve_params *curve;
+ unsigned int pkex_t; /* number of failures before dpp_pkex
+ * instantiation */
};
+#define PKEX_COUNTER_T_LIMIT 5
+
struct dpp_pkex {
+ void *msg_ctx;
unsigned int initiator:1;
unsigned int exchange_done:1;
+ unsigned int failed:1;
struct dpp_bootstrap_info *own_bi;
u8 own_mac[ETH_ALEN];
u8 peer_mac[ETH_ALEN];
@@ -120,12 +130,24 @@
EVP_PKEY *peer_bootstrap_key;
struct wpabuf *exchange_req;
struct wpabuf *exchange_resp;
+ unsigned int t; /* number of failures on code use */
+ unsigned int exch_req_wait_time;
+ unsigned int exch_req_tries;
+ unsigned int freq;
+};
+
+enum dpp_akm {
+ DPP_AKM_UNKNOWN,
+ DPP_AKM_DPP,
+ DPP_AKM_PSK,
+ DPP_AKM_SAE,
+ DPP_AKM_PSK_SAE
};
struct dpp_configuration {
u8 ssid[32];
size_t ssid_len;
- int dpp; /* whether to use DPP or legacy configuration */
+ enum dpp_akm akm;
/* For DPP configuration (connector) */
os_time_t netaccesskey_expiry;
@@ -142,6 +164,7 @@
const struct dpp_curve_params *curve;
struct dpp_bootstrap_info *peer_bi;
struct dpp_bootstrap_info *own_bi;
+ struct dpp_bootstrap_info *tmp_own_bi;
u8 waiting_pubkey_hash[SHA256_MAC_LEN];
int response_pending;
enum dpp_status_error auth_resp_status;
@@ -155,19 +178,34 @@
EVP_PKEY *peer_protocol_key;
struct wpabuf *req_msg;
struct wpabuf *resp_msg;
+ /* Intersection of possible frequencies for initiating DPP
+ * Authentication exchange */
+ unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
+ unsigned int num_freq, freq_idx;
unsigned int curr_freq;
+ unsigned int neg_freq;
+ unsigned int num_freq_iters;
size_t secret_len;
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Mx_len;
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Nx_len;
u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Lx_len;
u8 k1[DPP_MAX_HASH_LEN];
u8 k2[DPP_MAX_HASH_LEN];
u8 ke[DPP_MAX_HASH_LEN];
int initiator;
+ int waiting_auth_resp;
+ int waiting_auth_conf;
+ int auth_req_ack;
+ unsigned int auth_resp_tries;
+ u8 allowed_roles;
int configurator;
int remove_on_tx_status;
int auth_success;
struct wpabuf *conf_req;
+ const struct wpabuf *conf_resp; /* owned by GAS server */
struct dpp_configuration *conf_ap;
struct dpp_configuration *conf_sta;
struct dpp_configurator *conf;
@@ -177,6 +215,7 @@
char passphrase[64];
u8 psk[PMK_LEN];
int psk_set;
+ enum dpp_akm akm;
struct wpabuf *net_access_key;
os_time_t net_access_key_expiry;
struct wpabuf *c_sign_key;
@@ -203,6 +242,112 @@
size_t pmk_len;
};
+#ifdef CONFIG_TESTING_OPTIONS
+enum dpp_test_behavior {
+ DPP_TEST_DISABLED = 0,
+ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ = 1,
+ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP = 2,
+ DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF = 3,
+ DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ = 4,
+ DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP = 5,
+ DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ = 6,
+ DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP = 7,
+ DPP_TEST_ZERO_I_CAPAB = 8,
+ DPP_TEST_ZERO_R_CAPAB = 9,
+ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 10,
+ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 11,
+ DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ = 12,
+ DPP_TEST_NO_I_NONCE_AUTH_REQ = 13,
+ DPP_TEST_NO_I_CAPAB_AUTH_REQ = 14,
+ DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ = 15,
+ DPP_TEST_NO_STATUS_AUTH_RESP = 16,
+ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 17,
+ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 18,
+ DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP = 19,
+ DPP_TEST_NO_R_NONCE_AUTH_RESP = 20,
+ DPP_TEST_NO_I_NONCE_AUTH_RESP = 21,
+ DPP_TEST_NO_R_CAPAB_AUTH_RESP = 22,
+ DPP_TEST_NO_R_AUTH_AUTH_RESP = 23,
+ DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP = 24,
+ DPP_TEST_NO_STATUS_AUTH_CONF = 25,
+ DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 26,
+ DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 27,
+ DPP_TEST_NO_I_AUTH_AUTH_CONF = 28,
+ DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF = 29,
+ DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP = 30,
+ DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP = 31,
+ DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP = 32,
+ DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF = 33,
+ DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ = 34,
+ DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 35,
+ DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP = 36,
+ DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 37,
+ DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ = 38,
+ DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ = 39,
+ DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ = 40,
+ DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP = 41,
+ DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP = 42,
+ DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP = 43,
+ DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 44,
+ DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 45,
+ DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP = 46,
+ DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ = 47,
+ DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP = 48,
+ DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ = 49,
+ DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP = 50,
+ DPP_TEST_NO_E_NONCE_CONF_REQ = 51,
+ DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ = 52,
+ DPP_TEST_NO_WRAPPED_DATA_CONF_REQ = 53,
+ DPP_TEST_NO_E_NONCE_CONF_RESP = 54,
+ DPP_TEST_NO_CONFIG_OBJ_CONF_RESP = 55,
+ DPP_TEST_NO_STATUS_CONF_RESP = 56,
+ DPP_TEST_NO_WRAPPED_DATA_CONF_RESP = 57,
+ DPP_TEST_INVALID_STATUS_CONF_RESP = 58,
+ DPP_TEST_E_NONCE_MISMATCH_CONF_RESP = 59,
+ DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_REQ = 60,
+ DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ = 61,
+ DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP = 62,
+ DPP_TEST_NO_STATUS_PEER_DISC_RESP = 63,
+ DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP = 64,
+ DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF = 65,
+ DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ = 66,
+ DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP = 67,
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 68,
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 69,
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 70,
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 71,
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 72,
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 73,
+ DPP_TEST_INVALID_STATUS_AUTH_RESP = 74,
+ DPP_TEST_INVALID_STATUS_AUTH_CONF = 75,
+ DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ = 76,
+ DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP = 77,
+ DPP_TEST_INVALID_STATUS_PEER_DISC_RESP = 78,
+ DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP = 79,
+ DPP_TEST_INVALID_CONNECTOR_PEER_DISC_REQ = 80,
+ DPP_TEST_INVALID_I_NONCE_AUTH_REQ = 81,
+ DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_REQ = 82,
+ DPP_TEST_INVALID_E_NONCE_CONF_REQ = 83,
+ DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP = 84,
+ DPP_TEST_STOP_AT_PKEX_CR_REQ = 85,
+ DPP_TEST_STOP_AT_PKEX_CR_RESP = 86,
+ DPP_TEST_STOP_AT_AUTH_REQ = 87,
+ DPP_TEST_STOP_AT_AUTH_RESP = 88,
+ DPP_TEST_STOP_AT_AUTH_CONF = 89,
+ DPP_TEST_STOP_AT_CONF_REQ = 90,
+};
+
+extern enum dpp_test_behavior dpp_test;
+extern u8 dpp_pkex_own_mac_override[ETH_ALEN];
+extern u8 dpp_pkex_peer_mac_override[ETH_ALEN];
+extern u8 dpp_pkex_ephemeral_key_override[600];
+extern size_t dpp_pkex_ephemeral_key_override_len;
+extern u8 dpp_protocol_key_override[600];
+extern size_t dpp_protocol_key_override_len;
+extern u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+extern size_t dpp_nonce_override_len;
+#endif /* CONFIG_TESTING_OPTIONS */
+
void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info);
const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type);
int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
@@ -213,16 +358,20 @@
struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
const u8 *privkey, size_t privkey_len);
+struct hostapd_hw_modes;
struct dpp_authentication * dpp_auth_init(void *msg_ctx,
struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
- int configurator);
+ u8 dpp_allowed_roles,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes);
struct dpp_authentication *
dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
unsigned int freq, const u8 *hdr, const u8 *attr_start,
- const u8 *wrapped_data, u16 wrapped_data_len);
+ size_t attr_len);
struct wpabuf *
dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len);
@@ -244,28 +393,34 @@
const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
int dpp_check_attrs(const u8 *buf, size_t len);
int dpp_key_expired(const char *timestamp, os_time_t *expiry);
+const char * dpp_akm_str(enum dpp_akm akm);
+int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
+ size_t buflen);
void dpp_configurator_free(struct dpp_configurator *conf);
struct dpp_configurator *
dpp_keygen_configurator(const char *curve, const u8 *privkey,
size_t privkey_len);
int dpp_configurator_own_config(struct dpp_authentication *auth,
- const char *curve);
-int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
- const u8 *net_access_key, size_t net_access_key_len,
- const u8 *csign_key, size_t csign_key_len,
- const u8 *peer_connector, size_t peer_connector_len,
- os_time_t *expiry);
-struct dpp_pkex * dpp_pkex_init(struct dpp_bootstrap_info *bi,
+ const char *curve, int ap);
+enum dpp_status_error
+dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len,
+ os_time_t *expiry);
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const char *identifier,
const char *code);
-struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+ struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const u8 *peer_mac,
const char *identifier,
const char *code,
const u8 *buf, size_t len);
struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+ const u8 *peer_mac,
const u8 *buf, size_t len);
struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
const u8 *hdr,
@@ -274,4 +429,6 @@
const u8 *buf, size_t len);
void dpp_pkex_free(struct dpp_pkex *pkex);
+char * dpp_corrupt_connector_signature(const char *connector);
+
#endif /* DPP_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index fdd5e73..db40379 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -89,7 +89,7 @@
{
int ok, j, first;
int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
- 149, 157, 184, 192 };
+ 149, 157, 165, 184, 192 };
size_t k;
if (pri_chan == sec_chan || !sec_chan)
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 120d4e8..e1ef277 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "defs.h"
#include "wpa_common.h"
+#include "drivers/driver.h"
#include "qca-vendor.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
@@ -120,6 +121,11 @@
elems->mbo = pos;
elems->mbo_len = elen;
break;
+ case HS20_ROAMING_CONS_SEL_OUI_TYPE:
+ /* Hotspot 2.0 Roaming Consortium Selection */
+ elems->roaming_cons_sel = pos;
+ elems->roaming_cons_sel_len = elen;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
@@ -256,6 +262,10 @@
elems->owe_dh = pos;
elems->owe_dh_len = elen;
break;
+ case WLAN_EID_EXT_PASSWORD_IDENTIFIER:
+ elems->password_id = pos;
+ elems->password_id_len = elen;
+ break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
@@ -352,6 +362,10 @@
elems->rsn_ie_len = elen;
break;
case WLAN_EID_PWR_CAPABILITY:
+ if (elen < 2)
+ break;
+ elems->power_capab = pos;
+ elems->power_capab_len = elen;
break;
case WLAN_EID_SUPPORTED_CHANNELS:
elems->supp_channels = pos;
@@ -1169,10 +1183,24 @@
}
-int ieee80211_is_dfs(int freq)
+int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
+ u16 num_modes)
{
- /* TODO: this could be more accurate to better cover all domains */
- return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
+ int i, j;
+
+ if (!modes || !num_modes)
+ return (freq >= 5260 && freq <= 5320) ||
+ (freq >= 5500 && freq <= 5700);
+
+ for (i = 0; i < num_modes; i++) {
+ for (j = 0; j < modes[i].num_channels; j++) {
+ if (modes[i].channels[j].freq == freq &&
+ (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR))
+ return 1;
+ }
+ }
+
+ return 0;
}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 9276158..ff7e51d 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -11,6 +11,8 @@
#include "defs.h"
+struct hostapd_hw_modes;
+
#define MAX_NOF_MB_IES_SUPPORTED 5
struct mb_ies_info {
@@ -79,6 +81,9 @@
const u8 *fils_pk;
const u8 *fils_nonce;
const u8 *owe_dh;
+ const u8 *power_capab;
+ const u8 *roaming_cons_sel;
+ const u8 *password_id;
u8 ssid_len;
u8 supp_rates_len;
@@ -122,6 +127,9 @@
u8 fils_wrapped_data_len;
u8 fils_pk_len;
u8 owe_dh_len;
+ u8 power_capab_len;
+ u8 roaming_cons_sel_len;
+ u8 password_id_len;
struct mb_ies_info mb_ies;
};
@@ -152,7 +160,8 @@
enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
int sec_channel, int vht,
u8 *op_class, u8 *channel);
-int ieee80211_is_dfs(int freq);
+int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
+ u16 num_modes);
enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
int supp_rates_11b_only(struct ieee802_11_elems *elems);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index b7fa563..e03a095 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -203,6 +203,7 @@
#define WLAN_STATUS_AUTHORIZATION_DEENABLED 107
#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
+#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
#define WLAN_REASON_UNSPECIFIED 1
@@ -463,6 +464,7 @@
#define WLAN_EID_EXT_FILS_NONCE 13
#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
#define WLAN_EID_EXT_OWE_DH_PARAM 32
+#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33
#define WLAN_EID_EXT_HE_CAPABILITIES 35
#define WLAN_EID_EXT_HE_OPERATION 36
@@ -1317,6 +1319,7 @@
#define HS20_INDICATION_OUI_TYPE 16
#define HS20_ANQP_OUI_TYPE 17
#define HS20_OSEN_OUI_TYPE 18
+#define HS20_ROAMING_CONS_SEL_OUI_TYPE 29
#define HS20_STYPE_QUERY_LIST 1
#define HS20_STYPE_CAPABILITY_LIST 2
#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
@@ -1327,15 +1330,19 @@
#define HS20_STYPE_OSU_PROVIDERS_LIST 8
#define HS20_STYPE_ICON_REQUEST 10
#define HS20_STYPE_ICON_BINARY_FILE 11
+#define HS20_STYPE_OPERATOR_ICON_METADATA 12
#define HS20_DGAF_DISABLED 0x01
#define HS20_PPS_MO_ID_PRESENT 0x02
#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
+#ifndef HS20_VERSION
#define HS20_VERSION 0x10 /* Release 2 */
+#endif /* HS20_VERSION */
/* WNM-Notification WFA vendors specific subtypes */
#define HS20_WNM_SUB_REM_NEEDED 0
#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
+#define HS20_WNM_T_C_ACCEPTANCE 2
#define HS20_DEAUTH_REASON_CODE_BSS 0
#define HS20_DEAUTH_REASON_CODE_ESS 1
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index b4f7d12..7c2be1f 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,6 +1,7 @@
/*
* Qualcomm Atheros OUI and vendor specific assignments
* Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -90,6 +91,49 @@
* which supports DFS offloading, to indicate a radar pattern has been
* detected. The channel is now unusable.
*
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap
+ * based on enum wifi_logger_supported_features. Attributes defined in
+ * enum qca_wlan_vendor_attr_get_logger_features.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA: Get the ring data from a particular
+ * logger ring, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID is passed as the
+ * attribute for this command. Attributes defined in
+ * enum qca_wlan_vendor_attr_wifi_logger_start.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES: Get the supported TDLS
+ * capabilities of the driver, parameters includes the attributes defined
+ * in enum qca_wlan_vendor_attr_tdls_get_capabilities.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS: Vendor command used to offload
+ * sending of certain periodic IP packet to firmware, attributes defined in
+ * enum qca_wlan_vendor_attr_offloaded_packets.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI: Command used to configure RSSI
+ * monitoring, defines min and max RSSI which are configured for RSSI
+ * monitoring. Also used to notify the RSSI breach and provides the BSSID
+ * and RSSI value that was breached. Attributes defined in
+ * enum qca_wlan_vendor_attr_rssi_monitoring.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NDP: Command used for performing various NAN
+ * Data Path (NDP) related operations, attributes defined in
+ * enum qca_wlan_vendor_attr_ndp_params.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD: Command used to enable/disable
+ * Neighbour Discovery offload, attributes defined in
+ * enum qca_wlan_vendor_attr_nd_offload.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: Used to set/get the various
+ * configuration parameter for BPF packet filter, attributes defined in
+ * enum qca_wlan_vendor_attr_packet_filter.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE: Gets the driver-firmware
+ * maximum supported size, attributes defined in
+ * enum qca_wlan_vendor_drv_info.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS: Command to get various
+ * data about wake reasons and datapath IP statistics, attributes defined
+ * in enum qca_wlan_vendor_attr_wake_stats.
+ *
* @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration
* for IEEE 802.11 communicating outside the context of a basic service
* set, called OCB command. Uses the attributes defines in
@@ -338,6 +382,77 @@
* information indicating the reason that triggered this detection. The
* attributes for this command are defined in
* enum qca_wlan_vendor_attr_hang.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG: Get the current values
+ * of spectral parameters used. The spectral scan parameters are specified
+ * by enum qca_wlan_vendor_attr_spectral_scan.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS: Get the debug stats
+ * for spectral scan functionality. The debug stats are specified by
+ * enum qca_wlan_vendor_attr_spectral_diag_stats.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO: Get spectral
+ * scan system capabilities. The capabilities are specified
+ * by enum qca_wlan_vendor_attr_spectral_cap.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS: Get the current
+ * status of spectral scan. The status values are specified
+ * by enum qca_wlan_vendor_attr_spectral_scan_status.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING: Sub-command to flush
+ * peer pending packets. Specify the peer MAC address in
+ * QCA_WLAN_VENDOR_ATTR_PEER_ADDR and the access category of the packets
+ * in QCA_WLAN_VENDOR_ATTR_AC. The attributes are listed
+ * in enum qca_wlan_vendor_attr_flush_pending.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO: Get vendor specific Representative
+ * RF Operating Parameter (RROP) information. The attributes for this
+ * information are defined in enum qca_wlan_vendor_attr_rrop_info. This is
+ * intended for use by external Auto Channel Selection applications.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS: Get the Specific Absorption Rate
+ * (SAR) power limits. This is a companion to the command
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS and is used to retrieve the
+ * settings currently in use. The attributes returned by this command are
+ * defined by enum qca_vendor_attr_sar_limits.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO: Provides the current behavior of
+ * the WLAN hardware MAC. Also, provides the WLAN netdev interface
+ * information attached to the respective MAC.
+ * This works both as a query (user space asks the current mode) or event
+ * interface (driver advertising the current mode to the user space).
+ * Driver does not trigger this event for temporary hardware mode changes.
+ * Mode changes w.r.t Wi-Fi connection update (VIZ creation / deletion,
+ * channel change, etc.) are updated with this event. Attributes for this
+ * interface are defined in enum qca_wlan_vendor_attr_mac.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH: Set MSDU queue depth threshold
+ * per peer per TID. Attributes for this command are define in
+ * enum qca_wlan_set_qdepth_thresh_attr.
+ * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD: Provides the thermal shutdown action
+ * guide for WLAN driver. Request to suspend of driver and FW if the
+ * temperature is higher than the suspend threshold; resume action is
+ * requested to driver if the temperature is lower than the resume
+ * threshold. In user poll mode, request temperature data by user. For test
+ * purpose, getting thermal shutdown configuration parameters is needed.
+ * Attributes for this interface are defined in
+ * enum qca_wlan_vendor_attr_thermal_cmd.
+ * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT: Thermal events reported from
+ * driver. Thermal temperature and indication of resume completion are
+ * reported as thermal events. The attributes for this command are defined
+ * in enum qca_wlan_vendor_attr_thermal_event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION: Sub command to set WiFi
+ * test configuration. Attributes for this command are defined in
+ * enum qca_wlan_vendor_attr_wifi_test_config.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER: This command is used to configure an
+ * RX filter to receive frames from stations that are active on the
+ * operating channel, but not associated with the local device (e.g., STAs
+ * associated with other APs). Filtering is done based on a list of BSSIDs
+ * and STA MAC addresses added by the user. This command is also used to
+ * fetch the statistics of unassociated stations. The attributes used with
+ * this command are defined in enum qca_wlan_vendor_attr_bss_filter.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -405,7 +520,17 @@
/* Wi-Fi configuration subcommands */
QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74,
QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75,
- /* 76-90 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET = 76,
+ QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA = 77,
+ QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES = 78,
+ QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79,
+ QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80,
+ QCA_NL80211_VENDOR_SUBCMD_NDP = 81,
+ QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD = 82,
+ QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER = 83,
+ QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE = 84,
+ QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS = 85,
+ /* 86-90 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
@@ -470,9 +595,25 @@
QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155,
QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156,
QCA_NL80211_VENDOR_SUBCMD_HANG = 157,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG = 158,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS = 159,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO = 160,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS = 161,
+ /* Flush peer pending data */
+ QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING = 162,
+ QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO = 163,
+ QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS = 164,
+ QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO = 165,
+ QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH = 166,
+ /* Thermal shutdown commands to protect wifi chip */
+ QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD = 167,
+ QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT = 168,
+ /* Wi-Fi test configuration subcommand */
+ QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION = 169,
+ /* Frame filter operations for other BSSs/unassociated STAs */
+ QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER = 170,
};
-
enum qca_wlan_vendor_attr {
QCA_WLAN_VENDOR_ATTR_INVALID = 0,
/* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
@@ -484,7 +625,8 @@
/* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
QCA_WLAN_VENDOR_ATTR_IFINDEX = 4,
/* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined
- * by enum qca_roaming_policy. */
+ * by enum qca_roaming_policy.
+ */
QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
@@ -572,10 +714,12 @@
QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
* to specify the chain number (unsigned 32 bit value) to inquire
- * the corresponding antenna RSSI value */
+ * the corresponding antenna RSSI value
+ */
QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
- * to report the specific antenna RSSI value (unsigned 32 bit value) */
+ * to report the specific antenna RSSI value (unsigned 32 bit value)
+ */
QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27,
/* Frequency in MHz, various uses. Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_FREQ = 28,
@@ -642,7 +786,8 @@
*/
QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
- * to report the corresponding antenna index to the chain RSSI value */
+ * to report the corresponding antenna index to the chain RSSI value
+ */
QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40,
/* keep last */
@@ -650,7 +795,6 @@
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
};
-
enum qca_roaming_policy {
QCA_ROAMING_NOT_ALLOWED,
QCA_ROAMING_ALLOWED_WITHIN_ESS,
@@ -778,7 +922,7 @@
* @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
* band selection based on channel selection results.
* @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
- * simultaneous off-channel operations.
+ * simultaneous off-channel operations.
* @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P
* Listen offload; a mechanism where the station's firmware takes care of
* responding to incoming Probe Request frames received from other P2P
@@ -794,6 +938,8 @@
* %QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that
* this Device may not support all OCE AP functionalities but can support
* only OCE STA-CFON functionalities.
+ * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
+ * managed regulatory.
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
@@ -804,6 +950,7 @@
QCA_WLAN_VENDOR_FEATURE_OCE_STA = 4,
QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5,
QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6,
+ QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -947,6 +1094,12 @@
* from kernel space to user space.
*/
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
+ /* An array of nested values as per enum qca_wlan_vendor_attr_pcl
+ * attribute. Each element contains frequency (MHz), weight, and flag
+ * bit mask indicating how the frequency should be used in P2P
+ * negotiation; sent from kernel space to user space.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX =
@@ -1127,16 +1280,16 @@
* @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported
* rates to be included
* @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests
- * at non CCK rate in 2GHz band
+ * at non CCK rate in 2GHz band
* @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags
* @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the
- * driver for the specific scan request
+ * driver for the specific scan request
* @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan
- * request decoded as in enum scan_status
+ * request decoded as in enum scan_status
* @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation
- * scan flag is set
+ * scan flag is set
* @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
- * randomisation
+ * randomisation
* @QCA_WLAN_VENDOR_ATTR_SCAN_BSSID: 6-byte MAC address representing the
* specific BSSID to scan for.
*/
@@ -1160,10 +1313,10 @@
/**
* enum scan_status - Specifies the valid values the vendor scan attribute
- * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
+ * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
*
* @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with
- * new scan results
+ * new scan results
* @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between
*/
enum scan_status {
@@ -1210,7 +1363,8 @@
enum qca_vendor_attr_txpower_decr_db {
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID,
/* 8-bit unsigned value to indicate the reduction of TX power in dB for
- * a virtual interface. */
+ * a virtual interface.
+ */
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST,
@@ -1260,29 +1414,37 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7,
/* 8-bit unsigned value to configure the maximum TX MPDU for
- * aggregation. */
+ * aggregation.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8,
/* 8-bit unsigned value to configure the maximum RX MPDU for
- * aggregation. */
+ * aggregation.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9,
/* 8-bit unsigned value to configure the Non aggregrate/11g sw
- * retry threshold (0 disable, 31 max). */
+ * retry threshold (0 disable, 31 max).
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10,
/* 8-bit unsigned value to configure the aggregrate sw
- * retry threshold (0 disable, 31 max). */
+ * retry threshold (0 disable, 31 max).
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11,
/* 8-bit unsigned value to configure the MGMT frame
- * retry threshold (0 disable, 31 max). */
+ * retry threshold (0 disable, 31 max).
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12,
/* 8-bit unsigned value to configure the CTRL frame
- * retry threshold (0 disable, 31 max). */
+ * retry threshold (0 disable, 31 max).
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13,
/* 8-bit unsigned value to configure the propagation delay for
- * 2G/5G band (0~63, units in us) */
+ * 2G/5G band (0~63, units in us)
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14,
/* Unsigned 32-bit value to configure the number of unicast TX fail
* packet count. The peer is disconnected once this threshold is
- * reached. */
+ * reached.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15,
/* Attribute used to set scan default IEs to the driver.
*
@@ -1293,7 +1455,8 @@
* merged with the IEs received along with scan request coming to the
* driver. If a particular IE is present in the scan default IEs but not
* present in the scan request, then that IE should be added to the IEs
- * sent in the Probe Request frames for that scan request. */
+ * sent in the Probe Request frames for that scan request.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16,
/* Unsigned 32-bit attribute for generic commands */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17,
@@ -1302,41 +1465,50 @@
/* Unsigned 32-bit data attribute for generic command response */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19,
/* Unsigned 32-bit length attribute for
- * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20,
/* Unsigned 32-bit flags attribute for
- * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21,
/* Unsigned 32-bit, defining the access policy.
* See enum qca_access_policy. Used with
- * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22,
/* Sets the list of full set of IEs for which a specific access policy
* has to be applied. Used along with
* QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access.
- * Zero length payload can be used to clear this access constraint. */
+ * Zero length payload can be used to clear this access constraint.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23,
/* Unsigned 32-bit, specifies the interface index (netdev) for which the
* corresponding configurations are applied. If the interface index is
* not specified, the configurations are attributed to the respective
- * wiphy. */
+ * wiphy.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24,
/* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */
QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25,
/* 8-bit unsigned value to configure the driver and below layers to
* ignore the assoc disallowed set by APs while connecting
- * 1-Ignore, 0-Don't ignore */
+ * 1-Ignore, 0-Don't ignore
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26,
/* 32-bit unsigned value to trigger antenna diversity features:
- * 1-Enable, 0-Disable */
+ * 1-Enable, 0-Disable
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27,
/* 32-bit unsigned value to configure specific chain antenna */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28,
/* 32-bit unsigned value to trigger cycle selftest
- * 1-Enable, 0-Disable */
+ * 1-Enable, 0-Disable
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29,
/* 32-bit unsigned to configure the cycle time of selftest
- * the unit is micro-second */
+ * the unit is micro-second
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30,
/* 32-bit unsigned value to set reorder timeout for AC_VO */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31,
@@ -1360,10 +1532,12 @@
* NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or
* 10 MHz channel width, the station will not connect to a BSS using 20
* MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to
- * clear this constraint. */
+ * clear this constraint.
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39,
/* 32-bit unsigned value to configure the propagation absolute delay
- * for 2G/5G band (units in us) */
+ * for 2G/5G band (units in us)
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40,
/* 32-bit unsigned value to set probe period */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41,
@@ -1431,7 +1605,7 @@
QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51,
/* 8-bit unsigned value to set the total beacon miss count
- * This paramater will set the total beacon miss count.
+ * This parameter will set the total beacon miss count.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52,
@@ -1443,9 +1617,32 @@
/* 8-bit unsigned value to configure the driver and below layers to
* enable/disable all FILS features.
- * 0-enable, 1-disable */
+ * 0-enable, 1-disable
+ */
QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54,
+ /* 16-bit unsigned value to configure the level of WLAN latency
+ * module. See enum qca_wlan_vendor_attr_config_latency_level.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL = 55,
+
+ /* 8-bit unsigned value indicating the driver to use the RSNE as-is from
+ * the connect interface. Exclusively used for the scenarios where the
+ * device is used as a test bed device with special functionality and
+ * not recommended for production. This helps driver to not validate the
+ * RSNE passed from user space and thus allow arbitrary IE data to be
+ * used for testing purposes.
+ * 1-enable, 0-disable.
+ * Applications set/reset this configuration. If not reset, this
+ * parameter remains in use until the driver is unloaded.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE = 56,
+
+ /* 8-bit unsigned value to trigger green Tx power saving.
+ * 1-Enable, 0-Disable
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -1478,7 +1675,8 @@
enum qca_wlan_vendor_attr_sap_conditional_chan_switch {
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0,
/* Priority based frequency list (an array of u32 values in host byte
- * order) */
+ * order)
+ */
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1,
/* Status of the conditional switch (u32).
* 0: Success, Non-zero: Failure
@@ -1513,6 +1711,36 @@
};
/**
+ * qca_wlan_set_qdepth_thresh_attr - Parameters for setting
+ * MSDUQ depth threshold per peer per tid in the target
+ *
+ * Associated Vendor Command:
+ * QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH
+ */
+enum qca_wlan_set_qdepth_thresh_attr {
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_INVALID = 0,
+ /* 6-byte MAC address */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAC_ADDR,
+ /* Unsigned 32-bit attribute for holding the TID */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_TID,
+ /* Unsigned 32-bit attribute for holding the update mask
+ * bit 0 - Update high priority msdu qdepth threshold
+ * bit 1 - Update low priority msdu qdepth threshold
+ * bit 2 - Update UDP msdu qdepth threshold
+ * bit 3 - Update Non UDP msdu qdepth threshold
+ * rest of bits are reserved
+ */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_UPDATE_MASK,
+ /* Unsigned 32-bit attribute for holding the threshold value */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_VALUE,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST,
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAX =
+ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability
*/
enum qca_wlan_vendor_attr_get_hw_capability {
@@ -1684,6 +1912,12 @@
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME: u64
+ * Absolute timestamp from 1970/1/1, unit in ms. After receiving the
+ * message, user layer APP could call gettimeofday to get another
+ * timestamp and calculate transfer delay for the message.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME: u32
+ * Real period for this measurement, unit in us.
*/
enum qca_wlan_vendor_attr_ll_stats_ext {
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0,
@@ -1777,6 +2011,9 @@
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME,
+
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX =
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1
@@ -1830,7 +2067,7 @@
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver
* can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION
* will be supported if set.
-* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder
* supports immediate (ASAP) response.
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone
* AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS.
@@ -2393,13 +2630,13 @@
/* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40,
/* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std
- * in the units of 0.5 Mbps HT/VHT it would be MCS index */
+ * in the units of 0.5 Mbps HT/VHT it would be MCS index
+ */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41,
/* Unsigned 32 bit value. Bit rate in units of 100 kbps */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42,
-
/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be
* nested within the peer info stats.
*/
@@ -2558,14 +2795,18 @@
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82,
+ /* Unsigned int 32 value.
+ * Pending MSDUs corresponding to respective AC.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1,
};
-enum qca_wlan_vendor_attr_ll_stats_type
-{
+enum qca_wlan_vendor_attr_ll_stats_type {
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2,
@@ -2682,10 +2923,14 @@
* limit feature.
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: Select the SAR power
* limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power
+ * limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
*
* This enumerates the valid set of values that may be supplied for
* attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of
- * the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command.
+ * the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command or in
+ * the response to an instance of the
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command.
*/
enum qca_vendor_attr_sar_limits_selections {
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 = 0,
@@ -2695,6 +2940,7 @@
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4 = 4,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7,
};
/**
@@ -2709,7 +2955,8 @@
* attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION in an
* instance of attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC in an
* instance of the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor
- * command.
+ * command or in the response to an instance of the
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command.
*/
enum qca_vendor_attr_sar_limits_spec_modulations {
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK = 0,
@@ -2740,7 +2987,12 @@
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN, and
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION and always
* contains as a payload the attribute
- * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT.
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT,
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX.
+ * Either %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT or
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX is
+ * needed based upon the value of
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE.
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND: Optional (u32) value to
* indicate for which band this specification applies. Valid
@@ -2765,8 +3017,16 @@
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT: Required (u32)
* value to specify the actual power limit value in units of 0.5
* dBm (i.e., a value of 11 represents 5.5 dBm).
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER.
*
- * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS.
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX: Required (u32)
+ * value to indicate SAR V2 indices (0 - 11) to select SAR V2 profiles.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
+ * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0.
+ *
+ * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS
+ * and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS.
*/
enum qca_vendor_attr_sar_limits {
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_INVALID = 0,
@@ -2777,6 +3037,7 @@
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN = 5,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX =
@@ -2824,7 +3085,7 @@
/* Unsigned 32-bit value; used to indicate the size of memory
* dump to be allocated.
- */
+ */
QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE = 2,
/* keep last */
@@ -3528,7 +3789,8 @@
/**
* qca_wlan_vendor_attr_pcl: Represents attributes for
* preferred channel list (PCL). These attributes are sent as part of
- * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL.
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL and
+ * QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST.
*/
enum qca_wlan_vendor_attr_pcl {
QCA_WLAN_VENDOR_ATTR_PCL_INVALID = 0,
@@ -3537,6 +3799,17 @@
QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL = 1,
/* Channel weightage (u8) */
QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT = 2,
+ /* Channel frequency (u32) in MHz */
+ QCA_WLAN_VENDOR_ATTR_PCL_FREQ = 3,
+ /* Channel flags (u32)
+ * bit 0 set: channel to be used for GO role,
+ * bit 1 set: channel to be used on CLI role,
+ * bit 2 set: channel must be considered for operating channel
+ * selection & peer chosen operating channel should be
+ * one of the channels with this flag set,
+ * bit 3 set: channel should be excluded in GO negotiation
+ */
+ QCA_WLAN_VENDOR_ATTR_PCL_FLAG = 4,
};
/**
@@ -3556,12 +3829,14 @@
/* Flag attribute to indicate if 11ac is offloaded to firmware */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3,
/* Flag attribute to indicate if driver provides additional channel
- * capability as part of scan operation */
+ * capability as part of scan operation
+ */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4,
/* Flag attribute to indicate interface status is UP */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AP_UP = 5,
/* Operating mode (u8) of interface. Takes one of enum nl80211_iftype
- * values. */
+ * values.
+ */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 6,
/* Channel width (u8). It takes one of enum nl80211_chan_width values.
* This is the upper bound of channel width. ACS logic should try to get
@@ -3572,7 +3847,8 @@
/* This (u8) will hold values of one of enum nl80211_bands */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 8,
/* PHY/HW mode (u8). Takes one of enum qca_wlan_vendor_acs_hw_mode
- * values */
+ * values
+ */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 9,
/* Array of (u32) supported frequency list among which ACS should choose
* best frequency.
@@ -3591,6 +3867,11 @@
* qca_wlan_vendor_attr_external_acs_policy.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13,
+ /* Reference RF Operating Parameter (RROP) availability information
+ * (u16). It uses values defined in enum
+ * qca_wlan_vendor_attr_rropavail_info.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_RROPAVAIL_INFO = 14,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
@@ -3642,10 +3923,10 @@
/* Indicates if the reason for the failure is due to a protocol
* layer/module.
*/
- QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0,
+ QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0,
/* Indicates if the reason for the failure is due to a hardware issue.
*/
- QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1,
+ QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1,
};
/**
@@ -3654,17 +3935,70 @@
* information leading to the power save failure.
*/
enum qca_attr_chip_power_save_failure {
- QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0,
- /* Reason to cause the power save failure.
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0,
+ /* Reason to cause the power save failure.
* These reasons are represented by
* enum qca_chip_power_save_failure_reason.
*/
- QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1,
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1,
- /* keep last */
- QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST,
- QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX =
- QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1,
+ /* keep last */
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST,
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX =
+ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_nud_stats_data_pkt_flags: Flag representing the various
+ * data types for which the stats have to get collected.
+ */
+enum qca_wlan_vendor_nud_stats_data_pkt_flags {
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_ARP = 1 << 0,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_DNS = 1 << 1,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_HANDSHAKE = 1 << 2,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV4 = 1 << 3,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV6 = 1 << 4,
+ /* Used by QCA_ATTR_NUD_STATS_PKT_TYPE only in nud stats get
+ * to represent the stats of respective data type.
+ */
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN = 1 << 5,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN_ACK = 1 << 6,
+ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_ACK = 1 << 7,
+};
+
+enum qca_wlan_vendor_nud_stats_set_data_pkt_info {
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_INVALID = 0,
+ /* Represents the data packet type to be monitored (u32).
+ * Host driver tracks the stats corresponding to each data frame
+ * represented by these flags.
+ * These data packets are represented by
+ * enum qca_wlan_vendor_nud_stats_data_pkt_flags
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE = 1,
+ /* Name corresponding to the DNS frame for which the respective DNS
+ * stats have to get monitored (string). Max string length 255.
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME = 2,
+ /* source port on which the respective proto stats have to get
+ * collected (u32).
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT = 3,
+ /* destination port on which the respective proto stats have to get
+ * collected (u32).
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT = 4,
+ /* IPv4 address for which the destined data packets have to be
+ * monitored. (in network byte order), u32.
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4 = 5,
+ /* IPv6 address for which the destined data packets have to be
+ * monitored. (in network byte order), 16 bytes array.
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6 = 6,
+
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST,
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_MAX =
+ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST - 1,
};
/**
@@ -3679,8 +4013,15 @@
* Start - If included, Stop - If not included
*/
QCA_ATTR_NUD_STATS_SET_START = 1,
- /* IPv4 address of the default gateway (in network byte order) */
+ /* IPv4 address of the default gateway (in network byte order), u32 */
QCA_ATTR_NUD_STATS_GW_IPV4 = 2,
+ /* Represents the list of data packet types to be monitored.
+ * Host driver tracks the stats corresponding to each data frame
+ * represented by these flags.
+ * These data packets are represented by
+ * enum qca_wlan_vendor_nud_stats_set_data_pkt_info
+ */
+ QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO = 3,
/* keep last */
QCA_ATTR_NUD_STATS_SET_LAST,
@@ -3688,6 +4029,58 @@
QCA_ATTR_NUD_STATS_SET_LAST - 1,
};
+enum qca_attr_nud_data_stats {
+ QCA_ATTR_NUD_DATA_STATS_INVALID = 0,
+ /* Data packet type for which the stats are collected (u32).
+ * Represented by enum qca_wlan_vendor_nud_stats_data_pkt_flags
+ */
+ QCA_ATTR_NUD_STATS_PKT_TYPE = 1,
+ /* Name corresponding to the DNS frame for which the respective DNS
+ * stats are monitored (string). Max string length 255.
+ */
+ QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME = 2,
+ /* source port on which the respective proto stats are collected (u32).
+ */
+ QCA_ATTR_NUD_STATS_PKT_SRC_PORT = 3,
+ /* destination port on which the respective proto stats are collected
+ * (u32).
+ */
+ QCA_ATTR_NUD_STATS_PKT_DEST_PORT = 4,
+ /* IPv4 address for which the destined data packets have to be
+ * monitored. (in network byte order), u32.
+ */
+ QCA_ATTR_NUD_STATS_PKT_DEST_IPV4 = 5,
+ /* IPv6 address for which the destined data packets have to be
+ * monitored. (in network byte order), 16 bytes array.
+ */
+ QCA_ATTR_NUD_STATS_PKT_DEST_IPV6 = 6,
+ /* Data packet Request count received from netdev (u32). */
+ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV = 7,
+ /* Data packet Request count sent to lower MAC from upper MAC (u32). */
+ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC = 8,
+ /* Data packet Request count received by lower MAC from upper MAC
+ * (u32)
+ */
+ QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC = 9,
+ /* Data packet Request count successfully transmitted by the device
+ * (u32)
+ */
+ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS = 10,
+ /* Data packet Response count received by lower MAC (u32) */
+ QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC = 11,
+ /* Data packet Response count received by upper MAC (u32) */
+ QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC = 12,
+ /* Data packet Response count delivered to netdev (u32) */
+ QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV = 13,
+ /* Data Packet Response count that are dropped out of order (u32) */
+ QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP = 14,
+
+ /* keep last */
+ QCA_ATTR_NUD_DATA_STATS_LAST,
+ QCA_ATTR_NUD_DATA_STATS_MAX =
+ QCA_ATTR_NUD_DATA_STATS_LAST - 1,
+};
+
/**
* qca_attr_nud_stats_get: Attributes to vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET. This carries the requisite
@@ -3695,21 +4088,21 @@
*/
enum qca_attr_nud_stats_get {
QCA_ATTR_NUD_STATS_GET_INVALID = 0,
- /* ARP Request count from netdev */
+ /* ARP Request count from netdev (u32) */
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV = 1,
- /* ARP Request count sent to lower MAC from upper MAC */
+ /* ARP Request count sent to lower MAC from upper MAC (u32) */
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC = 2,
- /* ARP Request count received by lower MAC from upper MAC */
+ /* ARP Request count received by lower MAC from upper MAC (u32) */
QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC = 3,
- /* ARP Request count successfully transmitted by the device */
+ /* ARP Request count successfully transmitted by the device (u32) */
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS = 4,
- /* ARP Response count received by lower MAC */
+ /* ARP Response count received by lower MAC (u32) */
QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC = 5,
- /* ARP Response count received by upper MAC */
+ /* ARP Response count received by upper MAC (u32) */
QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC = 6,
- /* ARP Response count delivered to netdev */
+ /* ARP Response count delivered to netdev (u32) */
QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV = 7,
- /* ARP Response count delivered to netdev */
+ /* ARP Response count dropped due to out of order reception (u32) */
QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP = 8,
/* Flag indicating if the station's link to the AP is active.
* Active Link - If included, Inactive link - If not included
@@ -3719,6 +4112,11 @@
* Yes - If detected, No - If not detected.
*/
QCA_ATTR_NUD_STATS_IS_DAD = 10,
+ /* List of Data packet types for which the stats are requested.
+ * This list does not carry ARP stats as they are done by the
+ * above attributes. Represented by enum qca_attr_nud_data_stats.
+ */
+ QCA_ATTR_NUD_STATS_DATA_PKT_STATS = 11,
/* keep last */
QCA_ATTR_NUD_STATS_GET_LAST,
@@ -3784,7 +4182,8 @@
enum qca_wlan_vendor_attr_get_he_capabilities {
QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0,
/* Whether HE capabilities is supported
- * (u8 attribute: 0 = not supported, 1 = supported) */
+ * (u8 attribute: 0 = not supported, 1 = supported)
+ */
QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED = 1,
/* HE PHY capabilities, array of 3 u32 values */
QCA_WLAN_VENDOR_ATTR_PHY_CAPAB = 2,
@@ -3895,12 +4294,138 @@
* specific scan to be stopped.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19,
+ /* Skip interval for FFT reports. u32 attribute */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_PERIOD = 20,
+ /* Set to report only one set of FFT results.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SHORT_REPORT = 21,
+ /* Debug level for spectral module in driver.
+ * 0 : Verbosity level 0
+ * 1 : Verbosity level 1
+ * 2 : Verbosity level 2
+ * 3 : Matched filterID display
+ * 4 : One time dump of FFT report
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DEBUG_LEVEL = 22,
+ /* Type of spectral scan request. u32 attribute.
+ * It uses values defined in enum
+ * qca_wlan_vendor_attr_spectral_scan_request_type.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_spectral_diag_stats - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS.
+ */
+enum qca_wlan_vendor_attr_spectral_diag_stats {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_INVALID = 0,
+ /* Number of spectral TLV signature mismatches.
+ * u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SIG_MISMATCH = 1,
+ /* Number of spectral phyerror events with insufficient length when
+ * parsing for secondary 80 search FFT report. u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SEC80_SFFT_INSUFFLEN = 2,
+ /* Number of spectral phyerror events without secondary 80
+ * search FFT report. u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_NOSEC80_SFFT = 3,
+ /* Number of spectral phyerror events with vht operation segment 1 id
+ * mismatches in search fft report. u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG1ID_MISMATCH = 4,
+ /* Number of spectral phyerror events with vht operation segment 2 id
+ * mismatches in search fft report. u64 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG2ID_MISMATCH = 5,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_spectral_cap - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO.
+ */
+enum qca_wlan_vendor_attr_spectral_cap {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_INVALID = 0,
+ /* Flag attribute to indicate phydiag capability */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_PHYDIAG = 1,
+ /* Flag attribute to indicate radar detection capability */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RADAR = 2,
+ /* Flag attribute to indicate spectral capability */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_SPECTRAL = 3,
+ /* Flag attribute to indicate advanced spectral capability */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_ADVANCED_SPECTRAL = 4,
+ /* Spectral hardware generation. u32 attribute.
+ * It uses values defined in enum
+ * qca_wlan_vendor_spectral_scan_cap_hw_gen.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_spectral_scan_status - used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS.
+ */
+enum qca_wlan_vendor_attr_spectral_scan_status {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_INVALID = 0,
+ /* Flag attribute to indicate whether spectral scan is enabled */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1,
+ /* Flag attribute to indicate whether spectral scan is in progress*/
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_spectral_scan_request_type: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. This represents the
+ * spectral scan request types.
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG: Request to
+ * set the spectral parameters and start scan.
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN: Request to
+ * only set the spectral parameters.
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG: Request to
+ * only start the spectral scan.
+ */
+enum qca_wlan_vendor_attr_spectral_scan_request_type {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG,
+};
+
+/**
+ * qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the
+ * spectral hardware generation.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1: generation 1
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2: generation 2
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3: generation 3
+ */
+enum qca_wlan_vendor_spectral_scan_cap_hw_gen {
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1 = 0,
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2 = 1,
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3 = 2,
+};
+
enum qca_wlan_vendor_tos {
QCA_WLAN_VENDOR_TOS_BK = 0,
QCA_WLAN_VENDOR_TOS_BE = 1,
@@ -3969,4 +4494,1113 @@
QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_flush_pending - Attributes for
+ * flushing pending traffic in firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_ADDR: Configure peer MAC address.
+ * @QCA_WLAN_VENDOR_ATTR_AC: Configure access category of the pending
+ * packets. It is u8 value with bit 0~3 represent AC_BE, AC_BK,
+ * AC_VI, AC_VO respectively. Set the corresponding bit to 1 to
+ * flush packets with access category.
+ */
+enum qca_wlan_vendor_attr_flush_pending {
+ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_PEER_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_AC = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative
+ * RF Operating Parameter (RROP) information is available, and if so, at which
+ * point in the application-driver interaction sequence it can be retrieved by
+ * the application from the driver. This point may vary by architecture and
+ * other factors. This is a u16 value.
+ */
+enum qca_wlan_vendor_attr_rropavail_info {
+ /* RROP information is unavailable. */
+ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_UNAVAILABLE,
+ /* RROP information is available and the application can retrieve the
+ * information after receiving an QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS
+ * event from the driver.
+ */
+ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_EXTERNAL_ACS_START,
+ /* RROP information is available only after a vendor specific scan
+ * (requested using QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) has
+ * successfully completed. The application can retrieve the information
+ * after receiving the QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE event from
+ * the driver.
+ */
+ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_VSCAN_END,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rrop_info - Specifies vendor specific
+ * Representative RF Operating Parameter (RROP) information. It is sent for the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO. This information is
+ * intended for use by external Auto Channel Selection applications. It provides
+ * guidance values for some RF parameters that are used by the system during
+ * operation. These values could vary by channel, band, radio, and so on.
+ */
+enum qca_wlan_vendor_attr_rrop_info {
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_INVALID = 0,
+
+ /* Representative Tx Power List (RTPL) which has an array of nested
+ * values as per attributes in enum qca_wlan_vendor_attr_rtplinst.
+ */
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_RTPL = 1,
+
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rtplinst - Specifies attributes for individual list
+ * entry instances in the Representative Tx Power List (RTPL). It provides
+ * simplified power values intended for helping external Auto channel Selection
+ * applications compare potential Tx power performance between channels, other
+ * operating conditions remaining identical. These values are not necessarily
+ * the actual Tx power values that will be used by the system. They are also not
+ * necessarily the max or average values that will be used. Instead, they are
+ * relative, summarized keys for algorithmic use computed by the driver or
+ * underlying firmware considering a number of vendor specific factors.
+ */
+enum qca_wlan_vendor_attr_rtplinst {
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_INVALID = 0,
+
+ /* Primary channel number (u8) */
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY = 1,
+ /* Representative Tx power in dBm (s32) with emphasis on throughput. */
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_THROUGHPUT = 2,
+ /* Representative Tx power in dBm (s32) with emphasis on range. */
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_RANGE = 3,
+
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_MAX =
+ QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_latency_level - Level for
+ * wlan latency module.
+ *
+ * There will be various of Wi-Fi functionality like scan/roaming/adaptive
+ * power saving which would causing data exchange out of service, this
+ * would be a big impact on latency. For latency sensitive applications over
+ * Wi-Fi are intolerant to such operations and thus would configure them
+ * to meet their respective needs. It is well understood by such applications
+ * that altering the default behavior would degrade the Wi-Fi functionality
+ * w.r.t the above pointed WLAN operations.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL:
+ * Default WLAN operation level which throughput orientated.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE:
+ * Use moderate level to improve latency by limit scan duration.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW:
+ * Use low latency level to benifit application like concurrent
+ * downloading or video streaming via constraint scan/adaptive PS.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW:
+ * Use ultra low latency level to benefit for gaming/voice
+ * application via constraint scan/roaming/adaptive PS.
+ */
+enum qca_wlan_vendor_attr_config_latency_level {
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL = 1,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE = 2,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW = 3,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MAX =
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_wlan_mac - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO.
+ */
+enum qca_wlan_vendor_attr_mac {
+ QCA_WLAN_VENDOR_ATTR_MAC_INVALID = 0,
+
+ /* MAC mode info list which has an array of nested values as
+ * per attributes in enum qca_wlan_vendor_attr_mac_mode_info.
+ */
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAC_MAX =
+ QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mac_iface_info - Information of the connected
+ * Wi-Fi netdev interface on a respective MAC.
+ * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO.
+ */
+enum qca_wlan_vendor_attr_mac_iface_info {
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_INVALID = 0,
+ /* Wi-Fi netdev's interface index (u32) */
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX = 1,
+ /* Associated frequency in MHz of the connected Wi-Fi interface (u32) */
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mac_info - Points to MAC the information.
+ * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_INFO of the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO.
+ */
+enum qca_wlan_vendor_attr_mac_info {
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_INVALID = 0,
+ /* Hardware MAC ID associated for the MAC (u32) */
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID = 1,
+ /* Band supported by the MAC at a given point.
+ * This is a u32 bitmask of BIT(NL80211_BAND_*) as described in %enum
+ * nl80211_band.
+ */
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND = 2,
+ /* Refers to list of WLAN netdev interfaces associated with this MAC.
+ * Represented by enum qca_wlan_vendor_attr_mac_iface_info.
+ */
+ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_logger_features - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET.
+ */
+enum qca_wlan_vendor_attr_get_logger_features {
+ QCA_WLAN_VENDOR_ATTR_LOGGER_INVALID = 0,
+ /* Unsigned 32-bit enum value of wifi_logger_supported_features */
+ QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED = 1,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LOGGER_MAX =
+ QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST - 1,
+};
+
+/**
+ * enum wifi_logger_supported_features - Values for supported logger features
+ */
+enum wifi_logger_supported_features {
+ WIFI_LOGGER_MEMORY_DUMP_FEATURE = (1 << (0)),
+ WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_FEATURE = (1 << (1)),
+ WIFI_LOGGER_CONNECT_EVENT_FEATURE = (1 << (2)),
+ WIFI_LOGGER_POWER_EVENT_FEATURE = (1 << (3)),
+ WIFI_LOGGER_WAKE_LOCK_FEATURE = (1 << (4)),
+ WIFI_LOGGER_VERBOSE_FEATURE = (1 << (5)),
+ WIFI_LOGGER_WATCHDOG_TIMER_FEATURE = (1 << (6)),
+ WIFI_LOGGER_DRIVER_DUMP_FEATURE = (1 << (7)),
+ WIFI_LOGGER_PACKET_FATE_FEATURE = (1 << (8)),
+};
+
+/**
+ * enum qca_wlan_tdls_caps_features_supported - Values for TDLS get
+ * capabilities features
+ */
+enum qca_wlan_tdls_caps_features_supported {
+ WIFI_TDLS_SUPPORT = (1 << (0)),
+ WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT = (1 << (1)),
+ WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2))
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_get_capabilities - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES.
+ */
+enum qca_wlan_vendor_attr_tdls_get_capabilities {
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_INVALID = 0,
+ /* Indicates the max concurrent sessions */
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS,
+ /* Indicates the support for features */
+ /* Unsigned 32-bit bitmap qca_wlan_tdls_caps_features_supported
+ */
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_offloaded_packets_sending_control - Offload packets control
+ * command used as value for the attribute
+ * QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL.
+ */
+enum qca_wlan_offloaded_packets_sending_control {
+ QCA_WLAN_OFFLOADED_PACKETS_SENDING_CONTROL_INVALID = 0,
+ QCA_WLAN_OFFLOADED_PACKETS_SENDING_START,
+ QCA_WLAN_OFFLOADED_PACKETS_SENDING_STOP
+};
+
+/**
+ * enum qca_wlan_vendor_attr_offloaded_packets - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS.
+ */
+enum qca_wlan_vendor_attr_offloaded_packets {
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID = 0,
+ /* Takes valid value from the enum
+ * qca_wlan_offloaded_packets_sending_control
+ * Unsigned 32-bit value
+ */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID,
+ /* array of u8 len: Max packet size */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA,
+ /* 6-byte MAC address used to represent source MAC address */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR,
+ /* 6-byte MAC address used to represent destination MAC address */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR,
+ /* Unsigned 32-bit value, in milli seconds */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX =
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_rssi_monitoring_control - RSSI control commands used as values
+ * by the attribute QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL.
+ */
+enum qca_wlan_rssi_monitoring_control {
+ QCA_WLAN_RSSI_MONITORING_CONTROL_INVALID = 0,
+ QCA_WLAN_RSSI_MONITORING_START,
+ QCA_WLAN_RSSI_MONITORING_STOP,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rssi_monitoring - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI.
+ */
+enum qca_wlan_vendor_attr_rssi_monitoring {
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_INVALID = 0,
+ /* Takes valid value from the enum
+ * qca_wlan_rssi_monitoring_control
+ * Unsigned 32-bit value enum qca_wlan_rssi_monitoring_control
+ */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL,
+ /* Unsigned 32-bit value */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
+ /* Signed 8-bit value in dBm */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI,
+ /* Signed 8-bit value in dBm */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI,
+ /* attributes to be used/received in callback */
+ /* 6-byte MAC address used to represent current BSSID MAC address */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID,
+ /* Signed 8-bit value indicating the current RSSI */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX =
+ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ndp_params - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_NDP.
+ */
+enum qca_wlan_vendor_attr_ndp_params {
+ QCA_WLAN_VENDOR_ATTR_NDP_PARAM_INVALID = 0,
+ /* Unsigned 32-bit value
+ * enum of sub commands values in qca_wlan_ndp_sub_cmd
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD,
+ /* Unsigned 16-bit value */
+ QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
+ /* NL attributes for data used NDP SUB cmds */
+ /* Unsigned 32-bit value indicating a service info */
+ QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID,
+ /* Unsigned 32-bit value; channel frequency in MHz */
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL,
+ /* Interface Discovery MAC address. An array of 6 Unsigned int8 */
+ QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR,
+ /* Interface name on which NDP is being created */
+ QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR,
+ /* Unsigned 32-bit value for security */
+ /* CONFIG_SECURITY is deprecated, use NCS_SK_TYPE/PMK/SCID instead */
+ QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY,
+ /* Unsigned 32-bit value for QoS */
+ QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS,
+ /* Array of u8: len = QCA_WLAN_VENDOR_ATTR_NAN_DP_APP_INFO_LEN */
+ QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO,
+ /* Unsigned 32-bit value for NDP instance Id */
+ QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID,
+ /* Array of instance Ids */
+ QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY,
+ /* Unsigned 32-bit value for initiator/responder NDP response code
+ * accept/reject
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE,
+ /* NDI MAC address. An array of 6 Unsigned int8 */
+ QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR,
+ /* Unsigned 32-bit value errors types returned by driver
+ * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy
+ * NanStatusType includes these values.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
+ /* Unsigned 32-bit value error values returned by driver
+ * The nan_i.h in AOSP project platform/hardware/qcom/wlan
+ * NanInternalStatusType includes these values.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ /* Unsigned 32-bit value for Channel setup configuration
+ * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy
+ * NanDataPathChannelCfg includes these values.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG,
+ /* Unsigned 32-bit value for Cipher Suite Shared Key Type */
+ QCA_WLAN_VENDOR_ATTR_NDP_CSID,
+ /* Array of u8: len = NAN_PMK_INFO_LEN 32 bytes */
+ QCA_WLAN_VENDOR_ATTR_NDP_PMK,
+ /* Security Context Identifier that contains the PMKID
+ * Array of u8: len = NAN_SCID_BUF_LEN 1024 bytes
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_SCID,
+ /* Array of u8: len = NAN_SECURITY_MAX_PASSPHRASE_LEN 63 bytes */
+ QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE,
+ /* Array of u8: len = NAN_MAX_SERVICE_NAME_LEN 255 bytes */
+ QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME,
+ /* Unsigned 32-bit bitmap indicating schedule update
+ * BIT_0: NSS Update
+ * BIT_1: Channel list update
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON,
+ /* Unsigned 32-bit value for NSS */
+ QCA_WLAN_VENDOR_ATTR_NDP_NSS,
+ /* Unsigned 32-bit value for NUMBER NDP CHANNEL */
+ QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS,
+ /* Unsigned 32-bit value for CHANNEL BANDWIDTH
+ * 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH,
+ /* Array of channel/band width */
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO,
+ /* IPv6 address used by NDP (in network byte order), 16 bytes array.
+ * This attribute is used and optional for ndp request, ndp response,
+ * ndp indication, and ndp confirm.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR = 27,
+ /* Unsigned 16-bit value indicating transport port used by NDP.
+ * This attribute is used and optional for ndp response, ndp indication,
+ * and ndp confirm.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT = 28,
+ /* Unsigned 8-bit value indicating protocol used by NDP and assigned by
+ * the Internet Assigned Numbers Authority (IANA) as per:
+ * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+ * This attribute is used and optional for ndp response, ndp indication,
+ * and ndp confirm.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL = 29,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX =
+ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_ndp_sub_cmd {
+ QCA_WLAN_VENDOR_ATTR_NDP_INVALID = 0,
+ /* Command to create a NAN data path interface */
+ QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE = 1,
+ /* Command to delete a NAN data path interface */
+ QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE = 2,
+ /* Command to initiate a NAN data path session */
+ QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST = 3,
+ /* Command to notify if the NAN data path session was sent */
+ QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE = 4,
+ /* Command to respond to NAN data path session */
+ QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST = 5,
+ /* Command to notify on the responder about the response */
+ QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE = 6,
+ /* Command to initiate a NAN data path end */
+ QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST = 7,
+ /* Command to notify the if end request was sent */
+ QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE = 8,
+ /* Command to notify the peer about the end request */
+ QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 9,
+ /* Command to confirm the NAN data path session is complete */
+ QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 10,
+ /* Command to indicate the peer about the end request being received */
+ QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11,
+ /* Command to indicate the peer of schedule update */
+ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12
+};
+
+/**
+ * enum qca_wlan_vendor_attr_nd_offload - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD.
+ */
+enum qca_wlan_vendor_attr_nd_offload {
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_INVALID = 0,
+ /* Flag to set Neighbour Discovery offload */
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG,
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX =
+ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST - 1,
+};
+
+/**
+ * enum packet_filter_sub_cmd - Packet filter sub commands
+ */
+enum packet_filter_sub_cmd {
+ /**
+ * Write packet filter program and/or data. The driver/firmware should
+ * disable APF before writing into local buffer and re-enable APF after
+ * writing is done.
+ */
+ QCA_WLAN_SET_PACKET_FILTER = 1,
+ /* Get packet filter feature capabilities from driver */
+ QCA_WLAN_GET_PACKET_FILTER = 2,
+ /**
+ * Write packet filter program and/or data. User space will send the
+ * %QCA_WLAN_DISABLE_PACKET_FILTER command before issuing this command
+ * and will send the %QCA_WLAN_ENABLE_PACKET_FILTER afterwards. The key
+ * difference from that %QCA_WLAN_SET_PACKET_FILTER is the control over
+ * enable/disable is given to user space with this command. Also,
+ * user space sends the length of program portion in the buffer within
+ * %QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH.
+ */
+ QCA_WLAN_WRITE_PACKET_FILTER = 3,
+ /* Read packet filter program and/or data */
+ QCA_WLAN_READ_PACKET_FILTER = 4,
+ /* Enable APF feature */
+ QCA_WLAN_ENABLE_PACKET_FILTER = 5,
+ /* Disable APF feature */
+ QCA_WLAN_DISABLE_PACKET_FILTER = 6,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_packet_filter - BPF control commands used by
+ * vendor QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER.
+ */
+enum qca_wlan_vendor_attr_packet_filter {
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID = 0,
+ /* Unsigned 32-bit enum passed using packet_filter_sub_cmd */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD,
+ /* Unsigned 32-bit value indicating the packet filter version */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION,
+ /* Unsigned 32-bit value indicating the packet filter id */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID,
+ /**
+ * Unsigned 32-bit value indicating the packet filter size including
+ * program + data.
+ */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE,
+ /* Unsigned 32-bit value indicating the packet filter current offset */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET,
+ /* Program and/or data in bytes */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM,
+ /* Unsigned 32-bit value of the length of the program section in packet
+ * filter buffer.
+ */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX =
+ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_drv_info - WLAN driver info used by vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE.
+ */
+enum qca_wlan_vendor_drv_info {
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_INVALID = 0,
+ /* Maximum Message size info between firmware & HOST
+ * Unsigned 32-bit value
+ */
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_wake_stats - Wake lock stats used by vendor
+ * command QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS.
+ */
+enum qca_wlan_vendor_attr_wake_stats {
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_INVALID = 0,
+ /* Unsigned 32-bit value indicating the total count of wake event */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE,
+ /* Array of individual wake count, each index representing wake reason
+ */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR,
+ /* Unsigned 32-bit value representing wake count array */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ,
+ /* Unsigned 32-bit total wake count value of driver/fw */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE,
+ /* Array of wake stats of driver/fw */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR,
+ /* Unsigned 32-bit total wake count value of driver/fw */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ,
+ /* Unsigned 32-bit total wake count value of packets received */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE,
+ /* Unsigned 32-bit wake count value unicast packets received */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT,
+ /* Unsigned 32-bit wake count value multicast packets received */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT,
+ /* Unsigned 32-bit wake count value broadcast packets received */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT,
+ /* Unsigned 32-bit wake count value of ICMP packets */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT,
+ /* Unsigned 32-bit wake count value of ICMP6 packets */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT,
+ /* Unsigned 32-bit value ICMP6 router advertisement */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA,
+ /* Unsigned 32-bit value ICMP6 neighbor advertisement */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA,
+ /* Unsigned 32-bit value ICMP6 neighbor solicitation */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS,
+ /* Unsigned 32-bit wake count value of receive side ICMP4 multicast */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT,
+ /* Unsigned 32-bit wake count value of receive side ICMP6 multicast */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT,
+ /* Unsigned 32-bit wake count value of receive side multicast */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT,
+ /* Unsigned 32-bit wake count value of a given RSSI breach */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RSSI_BREACH_CNT,
+ /* Unsigned 32-bit wake count value of low RSSI */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_LOW_RSSI_CNT,
+ /* Unsigned 32-bit value GSCAN count */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_GSCAN_CNT,
+ /* Unsigned 32-bit value PNO complete count */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_COMPLETE_CNT,
+ /* Unsigned 32-bit value PNO match count */
+ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_MATCH_CNT,
+ /* keep last */
+ QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX =
+ QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_thermal_cmd - Vendor subcmd attributes to set
+ * cmd value. Used for NL attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
+ */
+enum qca_wlan_vendor_attr_thermal_cmd {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_INVALID = 0,
+ /* The value of command, driver will implement different operations
+ * according to this value. It uses values defined in
+ * enum qca_wlan_vendor_attr_thermal_cmd_type.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX =
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST - 1
+};
+
+/**
+ * qca_wlan_vendor_attr_thermal_cmd_type: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE to the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD. This represents the
+ * thermal command types sent to driver.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS: Request to
+ * get thermal shutdown configuration parameters for display. Parameters
+ * responded from driver are defined in
+ * enum qca_wlan_vendor_attr_get_thermal_params_rsp.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE: Request to
+ * get temperature. Host should respond with a temperature data. It is defined
+ * in enum qca_wlan_vendor_attr_thermal_get_temperature.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND: Request to execute thermal
+ * suspend action.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME: Request to execute thermal
+ * resume action.
+ */
+enum qca_wlan_vendor_attr_thermal_cmd_type {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_thermal_get_temperature - vendor subcmd attributes
+ * to get chip temperature by user.
+ * enum values are used for NL attributes for data used by
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE command for data used
+ * by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
+ */
+enum qca_wlan_vendor_attr_thermal_get_temperature {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_INVALID = 0,
+ /* Temperature value (degree Celsius) from driver.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_DATA,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_MAX =
+ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_thermal_params_rsp - vendor subcmd attributes
+ * to get configuration parameters of thermal shutdown feature. Enum values are
+ * used by QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS command for data
+ * used by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
+ */
+enum qca_wlan_vendor_attr_get_thermal_params_rsp {
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_INVALID = 0,
+ /* Indicate if the thermal shutdown feature is enabled.
+ * NLA_FLAG attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_EN,
+ /* Indicate if the auto mode is enabled.
+ * Enable: Driver triggers the suspend/resume action.
+ * Disable: User space triggers the suspend/resume action.
+ * NLA_FLAG attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_AUTO_EN,
+ /* Thermal resume threshold (degree Celsius). Issue the resume command
+ * if the temperature value is lower than this threshold.
+ * u16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_RESUME_THRESH,
+ /* Thermal warning threshold (degree Celsius). FW reports temperature
+ * to driver if it's higher than this threshold.
+ * u16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_WARNING_THRESH,
+ /* Thermal suspend threshold (degree Celsius). Issue the suspend command
+ * if the temperature value is higher than this threshold.
+ * u16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SUSPEND_THRESH,
+ /* FW reports temperature data periodically at this interval (ms).
+ * u16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SAMPLE_RATE,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_MAX =
+ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_thermal_event - vendor subcmd attributes to
+ * report thermal events from driver to user space.
+ * enum values are used for NL attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT sub command.
+ */
+enum qca_wlan_vendor_attr_thermal_event {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_INVALID = 0,
+ /* Temperature value (degree Celsius) from driver.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_TEMPERATURE,
+ /* Indication of resume completion from power save mode.
+ * NLA_FLAG attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_MAX =
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * enum he_fragmentation_val - HE fragmentation support values
+ * Indicates level of dynamic fragmentation that is supported by
+ * a STA as a recipient.
+ * HE fragmentation values are defined in IEEE P802.11ax/D2.0, 9.4.2.237.2
+ * (HE MAC Capabilities Information field) and are used in HE Capabilities
+ * element to advertise the support. These values are validated in the driver
+ * to check the device capability and advertised in the HE Capabilities
+ * element. These values are used to configure testbed device to allow the
+ * advertised hardware capabilities to be downgraded for testing purposes.
+ *
+ * @HE_FRAG_DISABLE: no support for dynamic fragmentation
+ * @HE_FRAG_LEVEL1: support for dynamic fragments that are
+ * contained within an MPDU or S-MPDU, no support for dynamic fragments
+ * within an A-MPDU that is not an S-MPDU.
+ * @HE_FRAG_LEVEL2: support for dynamic fragments that are
+ * contained within an MPDU or S-MPDU and support for up to one dynamic
+ * fragment for each MSDU, each A-MSDU if supported by the recipient, and
+ * each MMPDU within an A-MPDU or multi-TID A-MPDU that is not an
+ * MPDU or S-MPDU.
+ * @HE_FRAG_LEVEL3: support for dynamic fragments that are
+ * contained within an MPDU or S-MPDU and support for multiple dynamic
+ * fragments for each MSDU and for each A-MSDU if supported by the
+ * recipient within an A-MPDU or multi-TID AMPDU and up to one dynamic
+ * fragment for each MMPDU in a multi-TID A-MPDU that is not an S-MPDU.
+ */
+enum he_fragmentation_val {
+ HE_FRAG_DISABLE,
+ HE_FRAG_LEVEL1,
+ HE_FRAG_LEVEL2,
+ HE_FRAG_LEVEL3,
+};
+
+/**
+ * enum he_mcs_config - HE MCS support configuration
+ *
+ * Configures the HE Tx/Rx MCS map in HE capability IE for given bandwidth.
+ * These values are used in driver to configure the HE MCS map to advertise
+ * Tx/Rx MCS map in HE capability and these values are applied for all the
+ * streams supported by the device. To configure MCS for different bandwidths,
+ * vendor command needs to be sent using this attribute with appropriate value.
+ * For example, to configure HE_80_MCS_0_7, send vendor command using HE MCS
+ * attribute with HE_80_MCS0_7. And to configure HE MCS for HE_160_MCS0_11
+ * send this command using HE MCS config attribute with value HE_160_MCS0_11.
+ * These values are used to configure testbed device to allow the advertised
+ * hardware capabilities to be downgraded for testing purposes. The enum values
+ * are defined such that BIT[1:0] indicates the MCS map value. Values 3,7 and
+ * 11 are not used as BIT[1:0] value is 3 which is used to disable MCS map.
+ * These values are validated in the driver before setting the MCS map and
+ * driver returns error if the input is other than these enum values.
+ *
+ * @HE_80_MCS0_7: support for HE 80/40/20 MHz MCS 0 to 7
+ * @HE_80_MCS0_9: support for HE 80/40/20 MHz MCS 0 to 9
+ * @HE_80_MCS0_11: support for HE 80/40/20 MHz MCS 0 to 11
+ * @HE_160_MCS0_7: support for HE 160 MHz MCS 0 to 7
+ * @HE_160_MCS0_9: support for HE 160 MHz MCS 0 to 9
+ * @HE_160_MCS0_11: support for HE 160 MHz MCS 0 to 11
+ * @HE_80P80_MCS0_7: support for HE 80p80 MHz MCS 0 to 7
+ * @HE_80P80_MCS0_9: support for HE 80p80 MHz MCS 0 to 9
+ * @HE_80P80_MCS0_11: support for HE 80p80 MHz MCS 0 to 11
+ */
+enum he_mcs_config {
+ HE_80_MCS0_7 = 0,
+ HE_80_MCS0_9 = 1,
+ HE_80_MCS0_11 = 2,
+ HE_160_MCS0_7 = 4,
+ HE_160_MCS0_9 = 5,
+ HE_160_MCS0_11 = 6,
+ HE_80P80_MCS0_7 = 8,
+ HE_80P80_MCS0_9 = 9,
+ HE_80P80_MCS0_11 = 10,
+};
+
+/**
+ * enum qca_wlan_ba_session_config - BA session configuration
+ *
+ * Indicates the configuration values for BA session configuration attribute.
+ *
+ * @QCA_WLAN_ADD_BA: Establish a new BA session with given configuration.
+ * @QCA_WLAN_DELETE_BA: Delete the existing BA session for given TID.
+ */
+enum qca_wlan_ba_session_config {
+ QCA_WLAN_ADD_BA = 1,
+ QCA_WLAN_DELETE_BA = 2,
+};
+
+/**
+ * enum qca_wlan_ac_type - Access category type
+ *
+ * Indicates the access category type value.
+ *
+ * @QCA_WLAN_AC_BE: BE access category
+ * @QCA_WLAN_AC_BK: BK access category
+ * @QCA_WLAN_AC_VI: VI access category
+ * @QCA_WLAN_AC_VO: VO access category
+ * @QCA_WLAN_AC_ALL: All ACs
+ */
+enum qca_wlan_ac_type {
+ QCA_WLAN_AC_BE = 0,
+ QCA_WLAN_AC_BK = 1,
+ QCA_WLAN_AC_VI = 2,
+ QCA_WLAN_AC_VO = 3,
+ QCA_WLAN_AC_ALL = 4,
+};
+
+/**
+ * enum qca_wlan_he_ltf_cfg - HE LTF configuration
+ *
+ * Indicates the HE LTF configuration value.
+ *
+ * @QCA_WLAN_HE_LTF_AUTO: HE-LTF is automatically set to the mandatory HE-LTF,
+ * based on the GI setting
+ * @QCA_WLAN_HE_LTF_1X: 1X HE LTF is 3.2us LTF
+ * @QCA_WLAN_HE_LTF_2X: 2X HE LTF is 6.4us LTF
+ * @QCA_WLAN_HE_LTF_4X: 4X HE LTF is 12.8us LTF
+ */
+enum qca_wlan_he_ltf_cfg {
+ QCA_WLAN_HE_LTF_AUTO = 0,
+ QCA_WLAN_HE_LTF_1X = 1,
+ QCA_WLAN_HE_LTF_2X = 2,
+ QCA_WLAN_HE_LTF_4X = 3,
+};
+
+/* Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
+ */
+enum qca_wlan_vendor_attr_wifi_test_config {
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_INVALID = 0,
+ /* 8-bit unsigned value to configure the driver to enable/disable
+ * WMM feature. This attribute is used to configure testbed device.
+ * 1-enable, 0-disable
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE = 1,
+
+ /* 8-bit unsigned value to configure the driver to accept/reject
+ * the addba request from peer. This attribute is used to configure
+ * the testbed device.
+ * 1-accept addba, 0-reject addba
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ = 2,
+
+ /* 8-bit unsigned value to configure the driver to send or not to
+ * send the addba request to peer.
+ * This attribute is used to configure the testbed device.
+ * 1-send addba, 0-do not send addba
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ = 3,
+
+ /* 8-bit unsigned value to indicate the HE fragmentation support.
+ * Uses enum he_fragmentation_val values.
+ * This attribute is used to configure the testbed device to
+ * allow the advertised hardware capabilities to be downgraded
+ * for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION = 4,
+
+ /* 8-bit unsigned value to indicate the HE MCS support.
+ * Uses enum he_mcs_config values.
+ * This attribute is used to configure the testbed device to
+ * allow the advertised hardware capabilities to be downgraded
+ * for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS = 5,
+
+ /* 8-bit unsigned value to configure the driver to allow or not to
+ * allow the connection with WEP/TKIP in HT/VHT/HE modes.
+ * This attribute is used to configure the testbed device.
+ * 1-allow WEP/TKIP in HT/VHT/HE, 0-do not allow WEP/TKIP.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE = 6,
+
+ /* 8-bit unsigned value to configure the driver to add a
+ * new BA session or delete the existing BA session for
+ * given TID. ADDBA command uses the buffer size and TID
+ * configuration if user specifies the values else default
+ * value for buffer size is used for all TIDs if the TID
+ * also not specified. For DEL_BA command TID value is
+ * required to process the command.
+ * Uses enum qca_wlan_ba_session_config values.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION = 7,
+
+ /* 8-bit unsigned value to configure the buffer size in addba
+ * request and response frames.
+ * This attribute is used to configure the testbed device.
+ * The range of the value is 0 to 255.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE = 8,
+
+ /* 8-bit unsigned value to configure the buffer size in addba
+ * request and response frames.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID = 9,
+
+ /* 8-bit unsigned value to configure the no ack policy.
+ * To configure no ack policy, access category value is
+ * required to process the command.
+ * This attribute is used to configure the testbed device.
+ * 1 - enable no ack, 0 - disable no ack.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK = 10,
+
+ /* 8-bit unsigned value to configure the AC for no ack policy
+ * This attribute is used to configure the testbed device.
+ * Uses the enum qca_wlan_ac_type values.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC = 11,
+
+ /* 8-bit unsigned value to configure the HE LTF
+ * This attribute is used to configure the testbed device.
+ * Uses the enum qca_wlan_he_ltf_cfg values.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF = 12,
+
+ /* 8-bit unsigned value to configure the tx beamformee.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE = 13,
+
+ /* 8-bit unsigned value to configure the tx beamformee number
+ * of space-time streams.
+ * This attribute is used to configure the testbed device.
+ * The range of the value is 0 to 8.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS = 14,
+
+ /* 8-bit unsigned value to configure the MU EDCA params for given AC
+ * This attribute is used to configure the testbed device.
+ * Uses the enum qca_wlan_ac_type values.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC = 15,
+
+ /* 8-bit unsigned value to configure the MU EDCA AIFSN for given AC
+ * To configure MU EDCA AIFSN value, MU EDCA access category value
+ * is required to process the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AIFSN = 16,
+
+ /* 8-bit unsigned value to configure the MU EDCA ECW min value for
+ * given AC.
+ * To configure MU EDCA ECW min value, MU EDCA access category value
+ * is required to process the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMIN = 17,
+
+ /* 8-bit unsigned value to configure the MU EDCA ECW max value for
+ * given AC.
+ * To configure MU EDCA ECW max value, MU EDCA access category value
+ * is required to process the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMAX = 18,
+
+ /* 8-bit unsigned value to configure the MU EDCA timer for given AC
+ * To configure MU EDCA timer value, MU EDCA access category value
+ * is required to process the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_TIMER = 19,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
+ * The user can add/delete the filter by specifying the BSSID/STA MAC address in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, filter type in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, add/delete action in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user can get the
+ * statistics of an unassociated station by specifying the MAC address in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, station type in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, GET action in
+ * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user also can get
+ * the statistics of all unassociated stations by specifying the Broadcast MAC
+ * address (ff:ff:ff:ff:ff:ff) in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR with
+ * above procedure. In the response, driver shall specify statistics
+ * information nested in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS.
+ */
+enum qca_wlan_vendor_attr_bss_filter {
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR = 1,
+ /* Other BSS filter type, unsigned 8 bit value. One of the values
+ * in enum qca_wlan_vendor_bss_filter_type.
+ */
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE = 2,
+ /* Other BSS filter action, unsigned 8 bit value. One of the values
+ * in enum qca_wlan_vendor_bss_filter_action.
+ */
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION = 3,
+ /* Array of nested attributes where each entry is the statistics
+ * information of the specified station that belong to another BSS.
+ * Attributes for each entry are taken from enum
+ * qca_wlan_vendor_bss_filter_sta_stats.
+ * Other BSS station configured in
+ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER with filter type
+ * QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA.
+ * Statistics returned by QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER
+ * with filter action QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET.
+ */
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAX =
+ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_bss_filter_type - Type of
+ * filter used in other BSS filter operations. Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
+ *
+ * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID: BSSID filter
+ * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA: Station MAC address filter
+ */
+enum qca_wlan_vendor_bss_filter_type {
+ QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID,
+ QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA,
+};
+
+/**
+ * enum qca_wlan_vendor_bss_filter_action - Type of
+ * action in other BSS filter operations. Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
+ *
+ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD: Add filter
+ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL: Delete filter
+ * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET: Get the statistics
+ */
+enum qca_wlan_vendor_bss_filter_action {
+ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD,
+ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL,
+ QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET,
+};
+
+/**
+ * enum qca_wlan_vendor_bss_filter_sta_stats - Attributes for
+ * the statistics of a specific unassociated station belonging to another BSS.
+ * The statistics provides information of the unassociated station
+ * filtered by other BSS operation - such as MAC, signal value.
+ * Used by the vendor command QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
+ *
+ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC: MAC address of the station.
+ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI: Last received signal strength
+ * of the station. Unsigned 8 bit number containing RSSI.
+ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS: Time stamp of the host
+ * driver for the last received RSSI. Unsigned 64 bit number containing
+ * nanoseconds from the boottime.
+ */
+enum qca_wlan_vendor_bss_filter_sta_stats {
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC = 1,
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI = 2,
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAX =
+ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST - 1
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 9f70f03..981e788 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -29,6 +29,8 @@
/* First, check if this is an ECC group */
tmp->ec = crypto_ec_init(group);
if (tmp->ec) {
+ wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d",
+ group);
sae->group = group;
tmp->prime_len = crypto_ec_prime_len(tmp->ec);
tmp->prime = crypto_ec_get_prime(tmp->ec);
@@ -39,6 +41,8 @@
/* Not an ECC group, check FFC */
tmp->dh = dh_groups_get(group);
if (tmp->dh) {
+ wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d",
+ group);
sae->group = group;
tmp->prime_len = tmp->dh->prime_len;
if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
@@ -66,6 +70,8 @@
}
/* Unsupported group */
+ wpa_printf(MSG_DEBUG,
+ "SAE: Group %d not supported by the crypto library", group);
return -1;
}
@@ -88,6 +94,7 @@
crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
wpabuf_free(tmp->anti_clogging_token);
+ os_free(tmp->pw_id);
bin_clear_free(tmp, sizeof(*tmp));
sae->tmp = NULL;
}
@@ -417,12 +424,13 @@
static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
- size_t password_len)
+ size_t password_len, const char *identifier)
{
u8 counter, k = 40;
u8 addrs[2 * ETH_ALEN];
- const u8 *addr[2];
- size_t len[2];
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem;
u8 dummy_password[32];
size_t dummy_password_len;
int pwd_seed_odd = 0;
@@ -454,10 +462,13 @@
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
+ if (identifier)
+ wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
+ identifier);
/*
* H(salt, ikm) = HMAC-SHA256(salt, ikm)
- * base = password
+ * base = password [|| identifier]
* pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
* base || counter)
*/
@@ -465,8 +476,15 @@
addr[0] = password;
len[0] = password_len;
- addr[1] = &counter;
- len[1] = sizeof(counter);
+ num_elem = 1;
+ if (identifier) {
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ addr[num_elem] = &counter;
+ len[num_elem] = sizeof(counter);
+ num_elem++;
/*
* Continue for at least k iterations to protect against side-channel
@@ -484,8 +502,8 @@
}
wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
- if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
- pwd_seed) < 0)
+ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+ addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
@@ -544,12 +562,13 @@
static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
- size_t password_len)
+ size_t password_len, const char *identifier)
{
u8 counter;
u8 addrs[2 * ETH_ALEN];
- const u8 *addr[2];
- size_t len[2];
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem;
int found = 0;
if (sae->tmp->pwe_ffc == NULL) {
@@ -564,14 +583,21 @@
/*
* H(salt, ikm) = HMAC-SHA256(salt, ikm)
* pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
- * password || counter)
+ * password [|| identifier] || counter)
*/
sae_pwd_seed_key(addr1, addr2, addrs);
addr[0] = password;
len[0] = password_len;
- addr[1] = &counter;
- len[1] = sizeof(counter);
+ num_elem = 1;
+ if (identifier) {
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ addr[num_elem] = &counter;
+ len[num_elem] = sizeof(counter);
+ num_elem++;
for (counter = 1; !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
@@ -584,8 +610,8 @@
}
wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
- if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
- pwd_seed) < 0)
+ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+ addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
if (res < 0)
@@ -696,13 +722,15 @@
int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len,
- struct sae_data *sae)
+ const char *identifier, struct sae_data *sae)
{
if (sae->tmp == NULL ||
(sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
- password_len) < 0) ||
+ password_len,
+ identifier) < 0) ||
(sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
- password_len) < 0) ||
+ password_len,
+ identifier) < 0) ||
sae_derive_commit(sae) < 0)
return -1;
return 0;
@@ -842,7 +870,7 @@
void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
- const struct wpabuf *token)
+ const struct wpabuf *token, const char *identifier)
{
u8 *pos;
@@ -876,6 +904,16 @@
wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
pos, sae->tmp->prime_len);
}
+
+ if (identifier) {
+ /* Password Identifier element */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + os_strlen(identifier));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER);
+ wpabuf_put_str(buf, identifier);
+ wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s",
+ identifier);
+ }
}
@@ -921,25 +959,70 @@
}
+static int sae_is_password_id_elem(const u8 *pos, const u8 *end)
+{
+ return end - pos >= 3 &&
+ pos[0] == WLAN_EID_EXTENSION &&
+ pos[1] >= 1 &&
+ end - pos - 2 >= pos[1] &&
+ pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER;
+}
+
+
static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
const u8 *end, const u8 **token,
size_t *token_len)
{
- if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) {
- size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
- sae->tmp->prime_len);
- wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
- if (token)
- *token = *pos;
- if (token_len)
- *token_len = tlen;
- *pos += tlen;
- } else {
- if (token)
- *token = NULL;
- if (token_len)
- *token_len = 0;
+ size_t scalar_elem_len, tlen;
+ const u8 *elem;
+
+ if (token)
+ *token = NULL;
+ if (token_len)
+ *token_len = 0;
+
+ scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len;
+ if (scalar_elem_len >= (size_t) (end - *pos))
+ return; /* No extra data beyond peer scalar and element */
+
+ /* It is a bit difficult to parse this now that there is an
+ * optional variable length Anti-Clogging Token field and
+ * optional variable length Password Identifier element in the
+ * frame. We are sending out fixed length Anti-Clogging Token
+ * fields, so use that length as a requirement for the received
+ * token and check for the presence of possible Password
+ * Identifier element based on the element header information.
+ */
+ tlen = end - (*pos + scalar_elem_len);
+
+ if (tlen < SHA256_MAC_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token",
+ (unsigned int) tlen);
+ return;
}
+
+ elem = *pos + scalar_elem_len;
+ if (sae_is_password_id_elem(elem, end)) {
+ /* Password Identifier element takes out all available
+ * extra octets, so there can be no Anti-Clogging token in
+ * this frame. */
+ return;
+ }
+
+ elem += SHA256_MAC_LEN;
+ if (sae_is_password_id_elem(elem, end)) {
+ /* Password Identifier element is included in the end, so
+ * remove its length from the Anti-Clogging token field. */
+ tlen -= 2 + elem[1];
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
+ if (token)
+ *token = *pos;
+ if (token_len)
+ *token_len = tlen;
+ *pos += tlen;
}
@@ -991,12 +1074,12 @@
}
-static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
u8 prime[SAE_MAX_ECC_PRIME_LEN];
- if (2 * sae->tmp->prime_len > end - pos) {
+ if (2 * sae->tmp->prime_len > end - *pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
"commit-element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1007,8 +1090,8 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/* element x and y coordinates < p */
- if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
- os_memcmp(pos + sae->tmp->prime_len, prime,
+ if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 ||
+ os_memcmp(*pos + sae->tmp->prime_len, prime,
sae->tmp->prime_len) >= 0) {
wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
"element");
@@ -1016,13 +1099,13 @@
}
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
- pos, sae->tmp->prime_len);
+ *pos, sae->tmp->prime_len);
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
- pos + sae->tmp->prime_len, sae->tmp->prime_len);
+ *pos + sae->tmp->prime_len, sae->tmp->prime_len);
crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
sae->tmp->peer_commit_element_ecc =
- crypto_ec_point_from_bin(sae->tmp->ec, pos);
+ crypto_ec_point_from_bin(sae->tmp->ec, *pos);
if (sae->tmp->peer_commit_element_ecc == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1032,27 +1115,29 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
+ *pos += 2 * sae->tmp->prime_len;
+
return WLAN_STATUS_SUCCESS;
}
-static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
struct crypto_bignum *res, *one;
const u8 one_bin[1] = { 0x01 };
- if (sae->tmp->prime_len > end - pos) {
+ if (sae->tmp->prime_len > end - *pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
"commit-element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
+ wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos,
sae->tmp->prime_len);
crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
sae->tmp->peer_commit_element_ffc =
- crypto_bignum_init_set(pos, sae->tmp->prime_len);
+ crypto_bignum_init_set(*pos, sae->tmp->prime_len);
if (sae->tmp->peer_commit_element_ffc == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/* 1 < element < p - 1 */
@@ -1080,11 +1165,13 @@
}
crypto_bignum_deinit(res, 0);
+ *pos += sae->tmp->prime_len;
+
return WLAN_STATUS_SUCCESS;
}
-static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
+static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
if (sae->tmp->dh)
@@ -1093,6 +1180,44 @@
}
+static int sae_parse_password_identifier(struct sae_data *sae,
+ const u8 *pos, const u8 *end)
+{
+ wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
+ pos, end - pos);
+ if (!sae_is_password_id_elem(pos, end)) {
+ if (sae->tmp->pw_id) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No Password Identifier included, but expected one (%s)",
+ sae->tmp->pw_id);
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ }
+ os_free(sae->tmp->pw_id);
+ sae->tmp->pw_id = NULL;
+ return WLAN_STATUS_SUCCESS; /* No Password Identifier */
+ }
+
+ if (sae->tmp->pw_id &&
+ (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) ||
+ os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: The included Password Identifier does not match the expected one (%s)",
+ sae->tmp->pw_id);
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ }
+
+ os_free(sae->tmp->pw_id);
+ sae->tmp->pw_id = os_malloc(pos[1]);
+ if (!sae->tmp->pw_id)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1);
+ sae->tmp->pw_id[pos[1] - 1] = '\0';
+ wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
+ sae->tmp->pw_id, pos[1] - 1);
+ return WLAN_STATUS_SUCCESS;
+}
+
+
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups)
{
@@ -1116,7 +1241,12 @@
return res;
/* commit-element */
- res = sae_parse_commit_element(sae, pos, end);
+ res = sae_parse_commit_element(sae, &pos, end);
+ if (res != WLAN_STATUS_SUCCESS)
+ return res;
+
+ /* Optional Password Identifier element */
+ res = sae_parse_password_identifier(sae, pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
@@ -1235,7 +1365,8 @@
/* Send-Confirm */
sc = wpabuf_put(buf, 0);
wpabuf_put_le16(buf, sae->send_confirm);
- sae->send_confirm++;
+ if (sae->send_confirm < 0xffff)
+ sae->send_confirm++;
if (sae->tmp->ec)
sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
@@ -1292,3 +1423,19 @@
return 0;
}
+
+
+const char * sae_state_txt(enum sae_state state)
+{
+ switch (state) {
+ case SAE_NOTHING:
+ return "Nothing";
+ case SAE_COMMITTED:
+ return "Committed";
+ case SAE_CONFIRMED:
+ return "Confirmed";
+ case SAE_ACCEPTED:
+ return "Accepted";
+ }
+ return "?";
+}
diff --git a/src/common/sae.h b/src/common/sae.h
index a4270bc..3fbcb58 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -39,16 +39,22 @@
struct crypto_bignum *prime_buf;
struct crypto_bignum *order_buf;
struct wpabuf *anti_clogging_token;
+ char *pw_id;
+};
+
+enum sae_state {
+ SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED
};
struct sae_data {
- enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
+ enum sae_state state;
u16 send_confirm;
u8 pmk[SAE_PMK_LEN];
u8 pmkid[SAE_PMKID_LEN];
struct crypto_bignum *peer_commit_scalar;
int group;
- int sync;
+ unsigned int sync; /* protocol instance variable: Sync */
+ u16 rc; /* protocol instance variable: Rc (received send-confirm) */
struct sae_temporary_data *tmp;
};
@@ -58,14 +64,15 @@
int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len,
- struct sae_data *sae);
+ const char *identifier, struct sae_data *sae);
int sae_process_commit(struct sae_data *sae);
void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
- const struct wpabuf *token);
+ const struct wpabuf *token, const char *identifier);
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups);
void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
+const char * sae_state_txt(enum sae_state state);
#endif /* SAE_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 4bab6b9..14c5769 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1,6 +1,6 @@
/*
* WPA/RSN - Shared functions for supplicant and authenticator
- * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -25,6 +25,7 @@
{
switch (akmp) {
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 24;
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FT_FILS_SHA256:
@@ -41,6 +42,21 @@
}
+#ifdef CONFIG_IEEE80211R
+static unsigned int wpa_kck2_len(int akmp)
+{
+ switch (akmp) {
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ return 16;
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ return 24;
+ default:
+ return 0;
+ }
+}
+#endif /* CONFIG_IEEE80211R */
+
+
static unsigned int wpa_kek_len(int akmp, size_t pmk_len)
{
switch (akmp) {
@@ -50,6 +66,7 @@
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FT_FILS_SHA256:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 32;
case WPA_KEY_MGMT_DPP:
return pmk_len <= 32 ? 16 : 32;
@@ -61,10 +78,26 @@
}
+#ifdef CONFIG_IEEE80211R
+static unsigned int wpa_kek2_len(int akmp)
+{
+ switch (akmp) {
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ return 16;
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ return 32;
+ default:
+ return 0;
+ }
+}
+#endif /* CONFIG_IEEE80211R */
+
+
unsigned int wpa_mic_len(int akmp, size_t pmk_len)
{
switch (akmp) {
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 24;
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
@@ -82,6 +115,61 @@
/**
+ * wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: 1 if AKM-defined Key Descriptor Version is used; 0 otherwise
+ */
+int wpa_use_akm_defined(int akmp)
+{
+ return akmp == WPA_KEY_MGMT_OSEN ||
+ akmp == WPA_KEY_MGMT_OWE ||
+ akmp == WPA_KEY_MGMT_DPP ||
+ akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
+ wpa_key_mgmt_sae(akmp) ||
+ wpa_key_mgmt_suite_b(akmp) ||
+ wpa_key_mgmt_fils(akmp);
+}
+
+
+/**
+ * wpa_use_cmac - Is CMAC integrity algorithm used for EAPOL-Key MIC
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: 1 if CMAC is used; 0 otherwise
+ */
+int wpa_use_cmac(int akmp)
+{
+ return akmp == WPA_KEY_MGMT_OSEN ||
+ akmp == WPA_KEY_MGMT_OWE ||
+ akmp == WPA_KEY_MGMT_DPP ||
+ wpa_key_mgmt_ft(akmp) ||
+ wpa_key_mgmt_sha256(akmp) ||
+ wpa_key_mgmt_sae(akmp) ||
+ wpa_key_mgmt_suite_b(akmp);
+}
+
+
+/**
+ * wpa_use_aes_key_wrap - Is AES Keywrap algorithm used for EAPOL-Key Key Data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: 1 if AES Keywrap is used; 0 otherwise
+ *
+ * Note: AKM 00-0F-AC:1 and 00-0F-AC:2 have special rules for selecting whether
+ * to use AES Keywrap based on the negotiated pairwise cipher. This function
+ * does not cover those special cases.
+ */
+int wpa_use_aes_key_wrap(int akmp)
+{
+ return akmp == WPA_KEY_MGMT_OSEN ||
+ akmp == WPA_KEY_MGMT_OWE ||
+ akmp == WPA_KEY_MGMT_DPP ||
+ wpa_key_mgmt_ft(akmp) ||
+ wpa_key_mgmt_sha256(akmp) ||
+ wpa_key_mgmt_sae(akmp) ||
+ wpa_key_mgmt_suite_b(akmp);
+}
+
+
+/**
* wpa_eapol_key_mic - Calculate EAPOL-Key MIC
* @key: EAPOL-Key Key Confirmation Key (KCK)
* @key_len: KCK length in octets
@@ -131,6 +219,13 @@
#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
case WPA_KEY_INFO_TYPE_AKM_DEFINED:
switch (akmp) {
+#ifdef CONFIG_SAE
+ case WPA_KEY_MGMT_SAE:
+ case WPA_KEY_MGMT_FT_SAE:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)");
+ return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_SAE */
#ifdef CONFIG_HS20
case WPA_KEY_MGMT_OSEN:
wpa_printf(MSG_DEBUG,
@@ -201,6 +296,15 @@
os_memcpy(mic, hash, key_len);
break;
#endif /* CONFIG_DPP */
+#if defined(CONFIG_IEEE80211R) && defined(CONFIG_SHA384)
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - FT 802.1X SHA384)");
+ if (hmac_sha384(key, key_len, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 24);
+ break;
+#endif /* CONFIG_IEEE80211R && CONFIG_SHA384 */
default:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)",
@@ -248,7 +352,7 @@
size_t ptk_len;
if (pmk_len == 0) {
- wpa_printf(MSG_ERROR, "WPA: No PMK set for PT derivation");
+ wpa_printf(MSG_ERROR, "WPA: No PMK set for PTK derivation");
return -1;
}
@@ -273,6 +377,12 @@
ptk->kck_len = wpa_kck_len(akmp, pmk_len);
ptk->kek_len = wpa_kek_len(akmp, pmk_len);
ptk->tk_len = wpa_cipher_key_len(cipher);
+ if (ptk->tk_len == 0) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Unsupported cipher (0x%x) used in PTK derivation",
+ cipher);
+ return -1;
+ }
ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
if (wpa_key_mgmt_sha384(akmp)) {
@@ -285,14 +395,14 @@
return -1;
#endif /* CONFIG_SUITEB192 || CONFIG_FILS */
} else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
-#ifdef CONFIG_IEEE80211W
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS)
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
tmp, ptk_len) < 0)
return -1;
-#else /* CONFIG_IEEE80211W */
+#else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
return -1;
-#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
#ifdef CONFIG_DPP
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
@@ -337,6 +447,9 @@
os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
+ ptk->kek2_len = 0;
+ ptk->kck2_len = 0;
+
os_memset(tmp, 0, sizeof(tmp));
return 0;
}
@@ -386,6 +499,8 @@
addr, len, pmk);
if (res == 0)
wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len);
+ else
+ *pmk_len = 0;
return res;
}
@@ -515,6 +630,9 @@
fils_ft, *fils_ft_len);
}
+ ptk->kek2_len = 0;
+ ptk->kck2_len = 0;
+
os_memset(tmp, 0, sizeof(tmp));
ret = 0;
err:
@@ -625,14 +743,23 @@
const u8 *addr[9];
size_t len[9];
size_t i, num_elem = 0;
- u8 zero_mic[16];
+ u8 zero_mic[24];
+ size_t mic_len, fte_fixed_len;
- if (kck_len != 16) {
+ if (kck_len == 16) {
+ mic_len = 16;
+#ifdef CONFIG_SHA384
+ } else if (kck_len == 24) {
+ mic_len = 24;
+#endif /* CONFIG_SHA384 */
+ } else {
wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
(unsigned int) kck_len);
return -1;
}
+ fte_fixed_len = sizeof(struct rsn_ftie) - 16 + mic_len;
+
addr[num_elem] = sta_addr;
len[num_elem] = ETH_ALEN;
num_elem++;
@@ -656,7 +783,7 @@
num_elem++;
}
if (ftie) {
- if (ftie_len < 2 + sizeof(struct rsn_ftie))
+ if (ftie_len < 2 + fte_fixed_len)
return -1;
/* IE hdr and mic_control */
@@ -665,14 +792,14 @@
num_elem++;
/* MIC field with all zeros */
- os_memset(zero_mic, 0, sizeof(zero_mic));
+ os_memset(zero_mic, 0, mic_len);
addr[num_elem] = zero_mic;
- len[num_elem] = sizeof(zero_mic);
+ len[num_elem] = mic_len;
num_elem++;
/* Rest of FTIE */
- addr[num_elem] = ftie + 2 + 2 + 16;
- len[num_elem] = ftie_len - (2 + 2 + 16);
+ addr[num_elem] = ftie + 2 + 2 + mic_len;
+ len[num_elem] = ftie_len - (2 + 2 + mic_len);
num_elem++;
}
if (ric) {
@@ -683,7 +810,17 @@
for (i = 0; i < num_elem; i++)
wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
- if (omac1_aes_128_vector(kck, num_elem, addr, len, mic))
+#ifdef CONFIG_SHA384
+ if (kck_len == 24) {
+ u8 hash[SHA384_MAC_LEN];
+
+ if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 24);
+ }
+#endif /* CONFIG_SHA384 */
+ if (kck_len == 16 &&
+ omac1_aes_128_vector(kck, num_elem, addr, len, mic))
return -1;
return 0;
@@ -691,23 +828,27 @@
static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
- struct wpa_ft_ies *parse)
+ struct wpa_ft_ies *parse, int use_sha384)
{
const u8 *end, *pos;
parse->ftie = ie;
parse->ftie_len = ie_len;
- pos = ie + sizeof(struct rsn_ftie);
+ pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) :
+ sizeof(struct rsn_ftie));
end = ie + ie_len;
+ wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
while (end - pos >= 2) {
u8 id, len;
id = *pos++;
len = *pos++;
- if (len > end - pos)
+ if (len > end - pos) {
+ wpa_printf(MSG_DEBUG, "FT: Truncated subelement");
break;
+ }
switch (id) {
case FTIE_SUBELEM_R1KH_ID:
@@ -739,6 +880,9 @@
parse->igtk_len = len;
break;
#endif /* CONFIG_IEEE80211W */
+ default:
+ wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id);
+ break;
}
pos += len;
@@ -749,13 +893,19 @@
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse)
+ struct wpa_ft_ies *parse, int use_sha384)
{
const u8 *end, *pos;
struct wpa_ie_data data;
int ret;
const struct rsn_ftie *ftie;
int prot_ie_count = 0;
+ int update_use_sha384 = 0;
+
+ if (use_sha384 < 0) {
+ use_sha384 = 0;
+ update_use_sha384 = 1;
+ }
os_memset(parse, 0, sizeof(*parse));
if (ies == NULL)
@@ -773,6 +923,7 @@
switch (id) {
case WLAN_EID_RSN:
+ wpa_hexdump(MSG_DEBUG, "FT: RSNE", pos, len);
parse->rsn = pos;
parse->rsn_len = len;
ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
@@ -787,22 +938,63 @@
parse->rsn_pmkid = data.pmkid;
parse->key_mgmt = data.key_mgmt;
parse->pairwise_cipher = data.pairwise_cipher;
+ if (update_use_sha384) {
+ use_sha384 =
+ wpa_key_mgmt_sha384(parse->key_mgmt);
+ update_use_sha384 = 0;
+ }
break;
case WLAN_EID_MOBILITY_DOMAIN:
+ wpa_hexdump(MSG_DEBUG, "FT: MDE", pos, len);
if (len < sizeof(struct rsn_mdie))
return -1;
parse->mdie = pos;
parse->mdie_len = len;
break;
case WLAN_EID_FAST_BSS_TRANSITION:
+ wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len);
+ if (use_sha384) {
+ const struct rsn_ftie_sha384 *ftie_sha384;
+
+ if (len < sizeof(*ftie_sha384))
+ return -1;
+ ftie_sha384 =
+ (const struct rsn_ftie_sha384 *) pos;
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
+ ftie_sha384->mic_control, 2);
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
+ ftie_sha384->mic,
+ sizeof(ftie_sha384->mic));
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
+ ftie_sha384->anonce,
+ WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
+ ftie_sha384->snonce,
+ WPA_NONCE_LEN);
+ prot_ie_count = ftie_sha384->mic_control[1];
+ if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0)
+ return -1;
+ break;
+ }
+
if (len < sizeof(*ftie))
return -1;
ftie = (const struct rsn_ftie *) pos;
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
+ ftie->mic_control, 2);
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
+ ftie->mic, sizeof(ftie->mic));
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
+ ftie->anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
+ ftie->snonce, WPA_NONCE_LEN);
prot_ie_count = ftie->mic_control[1];
- if (wpa_ft_parse_ftie(pos, len, parse) < 0)
+ if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0)
return -1;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
+ wpa_hexdump(MSG_DEBUG, "FT: Timeout Interval",
+ pos, len);
if (len != 5)
break;
parse->tie = pos;
@@ -904,6 +1096,10 @@
return WPA_KEY_MGMT_FT_IEEE8021X;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
return WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_SHA384
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384)
+ return WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
@@ -1279,24 +1475,36 @@
int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
const u8 *ssid, size_t ssid_len,
const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
- const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
+ const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
+ int use_sha384)
{
u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
FT_R0KH_ID_MAX_LEN + ETH_ALEN];
- u8 *pos, r0_key_data[48], hash[32];
+ u8 *pos, r0_key_data[64], hash[48];
const u8 *addr[2];
size_t len[2];
+ size_t q = use_sha384 ? 48 : 32;
+ size_t r0_key_data_len = q + 16;
/*
* R0-Key-Data = KDF-384(XXKey, "FT-R0",
* SSIDlength || SSID || MDID || R0KHlength ||
* R0KH-ID || S0KH-ID)
- * XXKey is either the second 256 bits of MSK or PSK.
- * PMK-R0 = L(R0-Key-Data, 0, 256)
- * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
+ * XXKey is either the second 256 bits of MSK or PSK; or the first
+ * 384 bits of MSK for FT-EAP-SHA384.
+ * PMK-R0 = L(R0-Key-Data, 0, Q)
+ * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
+ * Q = 384 for FT-EAP-SHA384; otherwise, 256
*/
if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
return -1;
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s",
+ use_sha384 ? "SHA384" : "SHA256");
+ wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len);
+ wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len);
+ wpa_printf(MSG_DEBUG, "FT: S0KH-ID: " MACSTR, MAC2STR(s0kh_id));
pos = buf;
*pos++ = ssid_len;
os_memcpy(pos, ssid, ssid_len);
@@ -1309,22 +1517,50 @@
os_memcpy(pos, s0kh_id, ETH_ALEN);
pos += ETH_ALEN;
- if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
- r0_key_data, sizeof(r0_key_data)) < 0)
- return -1;
- os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
+#ifdef CONFIG_SHA384
+ if (use_sha384) {
+ if (xxkey_len != SHA384_MAC_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected XXKey length %d (expected %d)",
+ (int) xxkey_len, SHA384_MAC_LEN);
+ return -1;
+ }
+ if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len) < 0)
+ return -1;
+ }
+#endif /* CONFIG_SHA384 */
+ if (!use_sha384) {
+ if (xxkey_len != PMK_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected XXKey length %d (expected %d)",
+ (int) xxkey_len, PMK_LEN);
+ return -1;
+ }
+ if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len) < 0)
+ return -1;
+ }
+ os_memcpy(pmk_r0, r0_key_data, q);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16);
/*
- * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)
+ * PMKR0Name = Truncate-128(Hash("FT-R0N" || PMK-R0Name-Salt)
*/
addr[0] = (const u8 *) "FT-R0N";
len[0] = 6;
- addr[1] = r0_key_data + PMK_LEN;
+ addr[1] = &r0_key_data[q];
len[1] = 16;
- if (sha256_vector(2, addr, len, hash) < 0)
+#ifdef CONFIG_SHA384
+ if (use_sha384 && sha384_vector(2, addr, len, hash) < 0)
+ return -1;
+#endif /* CONFIG_SHA384 */
+ if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
return -1;
os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
+ os_memset(r0_key_data, 0, sizeof(r0_key_data));
return 0;
}
@@ -1335,15 +1571,15 @@
* 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)
+ const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384)
{
- u8 hash[32];
+ u8 hash[48];
const u8 *addr[4];
size_t len[4];
/*
- * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name ||
- * R1KH-ID || S1KH-ID))
+ * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name ||
+ * R1KH-ID || S1KH-ID))
*/
addr[0] = (const u8 *) "FT-R1N";
len[0] = 6;
@@ -1354,7 +1590,11 @@
addr[3] = s1kh_id;
len[3] = ETH_ALEN;
- if (sha256_vector(4, addr, len, hash) < 0)
+#ifdef CONFIG_SHA384
+ if (use_sha384 && sha384_vector(4, addr, len, hash) < 0)
+ return -1;
+#endif /* CONFIG_SHA384 */
+ if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
return -1;
os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
return 0;
@@ -1366,7 +1606,8 @@
*
* IEEE Std 802.11r-2008 - 8.5.1.5.4
*/
-int wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
+ const u8 *pmk_r0_name,
const u8 *r1kh_id, const u8 *s1kh_id,
u8 *pmk_r1, u8 *pmk_r1_name)
{
@@ -1374,18 +1615,37 @@
u8 *pos;
/* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s",
+ pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256");
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN);
+ wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id));
pos = buf;
os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
pos += FT_R1KH_ID_LEN;
os_memcpy(pos, s1kh_id, ETH_ALEN);
pos += ETH_ALEN;
- if (sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf,
- pmk_r1, PMK_LEN) < 0)
+#ifdef CONFIG_SHA384
+ if (pmk_r0_len == SHA384_MAC_LEN &&
+ sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
return -1;
+#endif /* CONFIG_SHA384 */
+ if (pmk_r0_len == PMK_LEN &&
+ sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
+ return -1;
+ if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) {
+ wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d",
+ (int) pmk_r0_len);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len);
return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id,
- pmk_r1_name);
+ pmk_r1_name,
+ pmk_r0_len == SHA384_MAC_LEN);
}
@@ -1394,7 +1654,8 @@
*
* IEEE Std 802.11r-2008 - 8.5.1.5.5
*/
-int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len,
+ const u8 *snonce, const u8 *anonce,
const u8 *sta_addr, const u8 *bssid,
const u8 *pmk_r1_name,
struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher)
@@ -1403,13 +1664,21 @@
u8 *pos, hash[32];
const u8 *addr[6];
size_t len[6];
- u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
- size_t ptk_len;
+ u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+ size_t ptk_len, offset;
+ int use_sha384 = wpa_key_mgmt_sha384(akmp);
/*
* PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
* BSSID || STA-ADDR)
*/
+ wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s",
+ use_sha384 ? "SHA384" : "SHA256");
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
+ wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
+ wpa_printf(MSG_DEBUG, "FT: BSSID=" MACSTR " STA-ADDR=" MACSTR,
+ MAC2STR(bssid), MAC2STR(sta_addr));
pos = buf;
os_memcpy(pos, snonce, WPA_NONCE_LEN);
pos += WPA_NONCE_LEN;
@@ -1421,18 +1690,44 @@
pos += ETH_ALEN;
ptk->kck_len = wpa_kck_len(akmp, PMK_LEN);
+ ptk->kck2_len = wpa_kck2_len(akmp);
ptk->kek_len = wpa_kek_len(akmp, PMK_LEN);
+ ptk->kek2_len = wpa_kek2_len(akmp);
ptk->tk_len = wpa_cipher_key_len(cipher);
- ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+ ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len +
+ ptk->kck2_len + ptk->kek2_len;
- if (sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf,
- tmp, ptk_len) < 0)
- return -1;
+#ifdef CONFIG_SHA384
+ if (use_sha384) {
+ if (pmk_r1_len != SHA384_MAC_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected PMK-R1 length %d (expected %d)",
+ (int) pmk_r1_len, SHA384_MAC_LEN);
+ return -1;
+ }
+ if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len) < 0)
+ return -1;
+ }
+#endif /* CONFIG_SHA384 */
+ if (!use_sha384) {
+ if (pmk_r1_len != PMK_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected PMK-R1 length %d (expected %d)",
+ (int) pmk_r1_len, PMK_LEN);
+ return -1;
+ }
+ if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len) < 0)
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len);
/*
* PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
* ANonce || BSSID || STA-ADDR))
*/
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
addr[0] = pmk_r1_name;
len[0] = WPA_PMK_NAME_LEN;
addr[1] = (const u8 *) "FT-PTKN";
@@ -1451,11 +1746,23 @@
os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
os_memcpy(ptk->kck, tmp, ptk->kck_len);
- os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
- os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+ offset = ptk->kck_len;
+ os_memcpy(ptk->kek, tmp + offset, ptk->kek_len);
+ offset += ptk->kek_len;
+ os_memcpy(ptk->tk, tmp + offset, ptk->tk_len);
+ offset += ptk->tk_len;
+ os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len);
+ offset = ptk->kck2_len;
+ os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len);
wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
+ if (ptk->kck2_len)
+ wpa_hexdump_key(MSG_DEBUG, "FT: KCK2",
+ ptk->kck2, ptk->kck2_len);
+ if (ptk->kek2_len)
+ wpa_hexdump_key(MSG_DEBUG, "FT: KEK2",
+ ptk->kek2, ptk->kek2_len);
wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
@@ -1483,7 +1790,7 @@
* See rsn_pmkid_suite_b()
* AKM: 00-0F-AC:12
* See rsn_pmkid_suite_b_192()
- * AKM: 00-0F-AC:15, 00-0F-AC:17
+ * AKM: 00-0F-AC:13, 00-0F-AC:15, 00-0F-AC:17
* PMKID = Truncate-128(HMAC-SHA-384(PMK, "PMK Name" || AA || SPA))
* Otherwise:
* PMKID = Truncate-128(HMAC-SHA-1(PMK, "PMK Name" || AA || SPA))
@@ -1501,11 +1808,11 @@
addr[2] = spa;
if (0) {
-#ifdef CONFIG_FILS
+#if defined(CONFIG_FILS) || defined(CONFIG_SHA384)
} else if (wpa_key_mgmt_sha384(akmp)) {
wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384");
hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash);
-#endif /* CONFIG_FILS */
+#endif /* CONFIG_FILS || CONFIG_SHA384 */
#if defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS)
} else if (wpa_key_mgmt_sha256(akmp)) {
wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256");
@@ -1656,6 +1963,8 @@
#ifdef CONFIG_IEEE80211R
case WPA_KEY_MGMT_FT_IEEE8021X:
return "FT-EAP";
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ return "FT-EAP-SHA384";
case WPA_KEY_MGMT_FT_PSK:
return "FT-PSK";
#endif /* CONFIG_IEEE80211R */
@@ -1697,6 +2006,8 @@
u32 wpa_akm_to_suite(int akm)
{
+ if (akm & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ return RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
return RSN_AUTH_KEY_MGMT_FT_802_1X;
if (akm & WPA_KEY_MGMT_FT_PSK)
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 3b8c1fb..6261744 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -1,6 +1,6 @@
/*
* WPA definitions shared between hostapd and wpa_supplicant
- * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -62,8 +62,7 @@
#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
-#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
-RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
#define RSN_AUTH_KEY_MGMT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
#define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15)
#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
@@ -210,9 +209,13 @@
u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */
u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */
u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */
+ u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */
+ u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */
size_t kck_len;
size_t kek_len;
size_t tk_len;
+ size_t kck2_len;
+ size_t kek2_len;
int installed; /* 1 if key has already been installed to driver */
};
@@ -311,6 +314,14 @@
/* followed by optional parameters */
} STRUCT_PACKED;
+struct rsn_ftie_sha384 {
+ u8 mic_control[2];
+ u8 mic[24];
+ u8 anonce[WPA_NONCE_LEN];
+ u8 snonce[WPA_NONCE_LEN];
+ /* followed by optional parameters */
+} STRUCT_PACKED;
+
#define FTIE_SUBELEM_R1KH_ID 1
#define FTIE_SUBELEM_GTK 2
#define FTIE_SUBELEM_R0KH_ID 3
@@ -361,14 +372,16 @@
int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
const u8 *ssid, size_t ssid_len,
const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
- const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
+ const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
+ int use_sha384);
int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
- const u8 *s1kh_id, u8 *pmk_r1_name);
-int wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+ const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384);
+int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
+ const u8 *pmk_r0_name,
const u8 *r1kh_id, const u8 *s1kh_id,
u8 *pmk_r1, u8 *pmk_r1_name);
-int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
- const u8 *sta_addr, const u8 *bssid,
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce,
+ const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
const u8 *pmk_r1_name,
struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher);
#endif /* CONFIG_IEEE80211R */
@@ -444,7 +457,8 @@
int pairwise_cipher;
};
-int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
+ int use_sha384);
int wpa_cipher_key_len(int cipher);
int wpa_cipher_rsc_len(int cipher);
@@ -461,6 +475,9 @@
int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
unsigned int wpa_mic_len(int akmp, size_t pmk_len);
+int wpa_use_akm_defined(int akmp);
+int wpa_use_cmac(int akmp);
+int wpa_use_aes_key_wrap(int akmp);
int fils_domain_name_hash(const char *domain, u8 *hash);
#endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index c59bc60..4eb7356 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -50,10 +50,19 @@
#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
/** EAP status */
#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS "
+/** Retransmit the previous request packet */
+#define WPA_EVENT_EAP_RETRANSMIT "CTRL-EVENT-EAP-RETRANSMIT "
+#define WPA_EVENT_EAP_RETRANSMIT2 "CTRL-EVENT-EAP-RETRANSMIT2 "
/** EAP authentication completed successfully */
#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
+#define WPA_EVENT_EAP_SUCCESS2 "CTRL-EVENT-EAP-SUCCESS2 "
/** EAP authentication failed (EAP-Failure received) */
#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+#define WPA_EVENT_EAP_FAILURE2 "CTRL-EVENT-EAP-FAILURE2 "
+/** EAP authentication failed due to no response received */
+#define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE "
+#define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 "
+#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE "
/** Network block temporarily disabled (e.g., due to authentication failure) */
#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
/** Temporarily disabled network block re-enabled */
@@ -80,6 +89,9 @@
#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
/** Channel switch (followed by freq=<MHz> and other channel parameters) */
#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
+/** SAE authentication failed due to unknown password identifier */
+#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
+ "CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
/** IP subnet status change notification
*
@@ -145,12 +157,15 @@
/* DPP events */
#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
+#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED "
#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
+#define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION "
#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
+#define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM "
#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
@@ -159,6 +174,13 @@
#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
+#define DPP_EVENT_RX "DPP-RX "
+#define DPP_EVENT_TX "DPP-TX "
+#define DPP_EVENT_TX_STATUS "DPP-TX-STATUS "
+#define DPP_EVENT_FAIL "DPP-FAIL "
+#define DPP_EVENT_PKEX_T_LIMIT "DPP-PKEX-T-LIMIT "
+#define DPP_EVENT_INTRO "DPP-INTRO "
+#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
@@ -255,6 +277,7 @@
#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
+#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE "
#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
@@ -278,6 +301,9 @@
#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
+#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD "
+#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE "
+
#define AP_EVENT_ENABLED "AP-ENABLED "
#define AP_EVENT_DISABLED "AP-DISABLED "
@@ -323,6 +349,19 @@
*/
#define FILS_HLP_RX "FILS-HLP-RX "
+/* Event to indicate Probe Request frame;
+ * parameters: sa=<STA MAC address> signal=<signal> */
+#define RX_PROBE_REQUEST "RX-PROBE-REQUEST "
+
+/* Event to indicate station's HT/VHT operation mode change information */
+#define STA_OPMODE_MAX_BW_CHANGED "STA-OPMODE-MAX-BW-CHANGED "
+#define STA_OPMODE_SMPS_MODE_CHANGED "STA-OPMODE-SMPS-MODE-CHANGED "
+#define STA_OPMODE_N_SS_CHANGED "STA-OPMODE-N_SS-CHANGED "
+
+/* New interface addition or removal for 4addr WDS SDA */
+#define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED "
+#define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED "
+
/* BSS command information masks */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF
@@ -424,7 +463,7 @@
*
* This function is used to send commands to wpa_supplicant/hostapd. Received
* response will be written to reply and reply_len is set to the actual length
- * of the reply. This function will block for up to two seconds while waiting
+ * of the reply. This function will block for up to 10 seconds while waiting
* for the reply. If unsolicited messages are received, the blocking time may
* be longer.
*
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index a723201..507b7ca 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -417,6 +417,13 @@
struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
u8 *plain, size_t *plain_len);
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey);
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len);
+
/**
* crypto_global_init - Initialize crypto wrapper
*
@@ -529,6 +536,14 @@
u8 *buf, size_t buflen, size_t padlen);
/**
+ * crypto_bignum_rand - Create a random number in range of modulus
+ * @r: Bignum; set to a random value
+ * @m: Bignum; modulus
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m);
+
+/**
* crypto_bignum_add - c = a + b
* @a: Bignum
* @b: Bignum
@@ -610,6 +625,16 @@
struct crypto_bignum *d);
/**
+ * crypto_bignum_rshift - r = a >> n
+ * @a: Bignum
+ * @n: Number of bits
+ * @r: Bignum; used to store the result of a >> n
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+ struct crypto_bignum *r);
+
+/**
* crypto_bignum_cmp - Compare two bignums
* @a: Bignum
* @b: Bignum
@@ -640,6 +665,13 @@
int crypto_bignum_is_one(const struct crypto_bignum *a);
/**
+ * crypto_bignum_is_odd - Is the given bignum odd
+ * @a: Bignum
+ * Returns: 1 if @a is odd or 0 if not
+ */
+int crypto_bignum_is_odd(const struct crypto_bignum *a);
+
+/**
* crypto_bignum_legendre - Compute the Legendre symbol (a/p)
* @a: Bignum
* @p: Bignum
@@ -671,6 +703,14 @@
void crypto_ec_deinit(struct crypto_ec *e);
/**
+ * crypto_ec_cofactor - Set the cofactor into the big number
+ * @e: EC context from crypto_ec_init()
+ * @cofactor: Cofactor of curve.
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor);
+
+/**
* crypto_ec_prime_len - Get length of the prime in octets
* @e: EC context from crypto_ec_init()
* Returns: Length of the prime defining the group
@@ -685,6 +725,13 @@
size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
/**
+ * crypto_ec_order_len - Get length of the order in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the order defining the group
+ */
+size_t crypto_ec_order_len(struct crypto_ec *e);
+
+/**
* crypto_ec_get_prime - Get prime defining an EC group
* @e: EC context from crypto_ec_init()
* Returns: Prime (bignum) defining the group
@@ -721,6 +768,16 @@
void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
/**
+ * crypto_ec_point_x - Copies the x-ordinate point into big number
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data
+ * @x: Big number to set to the copy of x-ordinate
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+ struct crypto_bignum *x);
+
+/**
* crypto_ec_point_to_bin - Write EC point value as binary data
* @e: EC context from crypto_ec_init()
* @p: EC point data from crypto_ec_point_init()
@@ -749,7 +806,7 @@
const u8 *val);
/**
- * crypto_bignum_add - c = a + b
+ * crypto_ec_point_add - c = a + b
* @e: EC context from crypto_ec_init()
* @a: Bignum
* @b: Bignum
@@ -761,7 +818,7 @@
struct crypto_ec_point *c);
/**
- * crypto_bignum_mul - res = b * p
+ * crypto_ec_point_mul - res = b * p
* @e: EC context from crypto_ec_init()
* @p: EC point
* @b: Bignum
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 31a580e..7a797b5 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / wrapper functions for libgcrypt
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,26 +10,41 @@
#include <gcrypt.h>
#include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
#include "crypto.h"
-int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+static int gnutls_digest_vector(int algo, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
{
gcry_md_hd_t hd;
unsigned char *p;
size_t i;
- if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR)
+ if (TEST_FAIL())
+ return -1;
+
+ if (gcry_md_open(&hd, algo, 0) != GPG_ERR_NO_ERROR)
return -1;
for (i = 0; i < num_elem; i++)
gcry_md_write(hd, addr[i], len[i]);
- p = gcry_md_read(hd, GCRY_MD_MD4);
+ p = gcry_md_read(hd, algo);
if (p)
- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4));
+ memcpy(mac, p, gcry_md_get_algo_dlen(algo));
gcry_md_close(hd);
return 0;
}
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_digest_vector(GCRY_MD_MD4, num_elem, addr, len, mac);
+}
+
+
int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
{
gcry_cipher_hd_t hd;
@@ -55,44 +70,155 @@
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
- gcry_md_hd_t hd;
- unsigned char *p;
- size_t i;
-
- if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR)
- return -1;
- for (i = 0; i < num_elem; i++)
- gcry_md_write(hd, addr[i], len[i]);
- p = gcry_md_read(hd, GCRY_MD_MD5);
- if (p)
- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5));
- gcry_md_close(hd);
- return 0;
+ return gnutls_digest_vector(GCRY_MD_MD5, num_elem, addr, len, mac);
}
int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
+ return gnutls_digest_vector(GCRY_MD_SHA1, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_digest_vector(GCRY_MD_SHA256, num_elem, addr, len, mac);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_digest_vector(GCRY_MD_SHA384, num_elem, addr, len, mac);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_digest_vector(GCRY_MD_SHA512, num_elem, addr, len, mac);
+}
+
+
+static int gnutls_hmac_vector(int algo, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac)
+{
gcry_md_hd_t hd;
unsigned char *p;
size_t i;
- if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR)
+ if (TEST_FAIL())
return -1;
+
+ if (gcry_md_open(&hd, algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+ return -1;
+ if (gcry_md_setkey(hd, key, key_len) != GPG_ERR_NO_ERROR) {
+ gcry_md_close(hd);
+ return -1;
+ }
for (i = 0; i < num_elem; i++)
gcry_md_write(hd, addr[i], len[i]);
- p = gcry_md_read(hd, GCRY_MD_SHA1);
+ p = gcry_md_read(hd, algo);
if (p)
- memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
+ memcpy(mac, p, gcry_md_get_algo_dlen(algo));
gcry_md_close(hd);
return 0;
}
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_MD5, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_SHA1, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_SHA256, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_SHA384, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return gnutls_hmac_vector(GCRY_MD_SHA512, key, key_len, num_elem, addr,
+ len, mac);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
void * aes_encrypt_init(const u8 *key, size_t len)
{
gcry_cipher_hd_t hd;
+ if (TEST_FAIL())
+ return NULL;
+
if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
GPG_ERR_NO_ERROR) {
printf("cipher open failed\n");
@@ -127,6 +253,9 @@
{
gcry_cipher_hd_t hd;
+ if (TEST_FAIL())
+ return NULL;
+
if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
GPG_ERR_NO_ERROR)
return NULL;
@@ -154,6 +283,42 @@
}
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c
index 9dcabb9..92581ac 100644
--- a/src/crypto/crypto_internal-modexp.c
+++ b/src/crypto/crypto_internal-modexp.c
@@ -13,6 +13,42 @@
#include "crypto.h"
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index b80ad57..259f995 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -694,6 +694,42 @@
#ifdef CONFIG_MODEXP
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c
new file mode 100644
index 0000000..4e31bc8
--- /dev/null
+++ b/src/crypto/crypto_nettle.c
@@ -0,0 +1,437 @@
+/*
+ * Wrapper functions for libnettle and libgmp
+ * Copyright (c) 2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <nettle/nettle-meta.h>
+#include <nettle/des.h>
+#undef des_encrypt
+#include <nettle/hmac.h>
+#include <nettle/aes.h>
+#undef aes_encrypt
+#undef aes_decrypt
+#include <nettle/arcfour.h>
+#include <nettle/bignum.h>
+
+#include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ struct des_ctx ctx;
+ u8 pkey[8], next, tmp;
+ int i;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ nettle_des_set_key(&ctx, pkey);
+ nettle_des_encrypt(&ctx, DES_BLOCK_SIZE, cypher, clear);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+static int nettle_digest_vector(const struct nettle_hash *alg, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ void *ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ctx = os_malloc(alg->context_size);
+ if (!ctx)
+ return -1;
+ alg->init(ctx);
+ for (i = 0; i < num_elem; i++)
+ alg->update(ctx, len[i], addr[i]);
+ alg->digest(ctx, alg->digest_size, mac);
+ bin_clear_free(ctx, alg->context_size);
+ return 0;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_md4, num_elem, addr, len, mac);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_md5, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_sha1, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_sha256, num_elem, addr, len, mac);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_sha384, num_elem, addr, len, mac);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nettle_digest_vector(&nettle_sha512, num_elem, addr, len, mac);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_md5_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_md5_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_md5_update(&ctx, len[i], addr[i]);
+ hmac_md5_digest(&ctx, MD5_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_sha1_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_sha1_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_sha1_update(&ctx, len[i], addr[i]);
+ hmac_sha1_digest(&ctx, SHA1_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_sha256_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_sha256_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_sha256_update(&ctx, len[i], addr[i]);
+ hmac_sha256_digest(&ctx, SHA256_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_sha384_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_sha384_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_sha384_update(&ctx, len[i], addr[i]);
+ hmac_sha384_digest(&ctx, SHA384_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ struct hmac_sha512_ctx ctx;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ hmac_sha512_set_key(&ctx, key_len, key);
+ for (i = 0; i < num_elem; i++)
+ hmac_sha512_update(&ctx, len[i], addr[i]);
+ hmac_sha512_digest(&ctx, SHA512_DIGEST_SIZE, mac);
+ os_memset(&ctx, 0, sizeof(ctx));
+ return 0;
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ struct aes_ctx *ctx;
+
+ if (TEST_FAIL())
+ return NULL;
+ ctx = os_malloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ nettle_aes_set_encrypt_key(ctx, len, key);
+
+ return ctx;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ struct aes_ctx *actx = ctx;
+ nettle_aes_encrypt(actx, AES_BLOCK_SIZE, crypt, plain);
+ return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ struct aes_ctx *actx = ctx;
+ bin_clear_free(actx, sizeof(*actx));
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ struct aes_ctx *ctx;
+
+ if (TEST_FAIL())
+ return NULL;
+ ctx = os_malloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ nettle_aes_set_decrypt_key(ctx, len, key);
+
+ return ctx;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ struct aes_ctx *actx = ctx;
+ nettle_aes_decrypt(actx, AES_BLOCK_SIZE, plain, crypt);
+ return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ struct aes_ctx *actx = ctx;
+ bin_clear_free(actx, sizeof(*actx));
+}
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ mpz_t bn_base, bn_exp, bn_modulus, bn_result;
+ int ret = -1;
+ size_t len;
+
+ mpz_inits(bn_base, bn_exp, bn_modulus, bn_result, NULL);
+ mpz_import(bn_base, base_len, 1, 1, 1, 0, base);
+ mpz_import(bn_exp, power_len, 1, 1, 1, 0, power);
+ mpz_import(bn_modulus, modulus_len, 1, 1, 1, 0, modulus);
+
+ mpz_powm(bn_result, bn_base, bn_exp, bn_modulus);
+ len = mpz_sizeinbase(bn_result, 2);
+ len = (len + 7) / 8;
+ if (*result_len < len)
+ goto error;
+ mpz_export(result, result_len, 1, 1, 1, 0, bn_result);
+ ret = 0;
+
+error:
+ mpz_clears(bn_base, bn_exp, bn_modulus, bn_result, NULL);
+ return ret;
+}
+
+
+struct crypto_cipher {
+ enum crypto_cipher_alg alg;
+ union {
+ struct arcfour_ctx arcfour_ctx;
+ } u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ ctx->alg = alg;
+
+ switch (alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ nettle_arcfour_set_key(&ctx->u.arcfour_ctx, key_len, key);
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ switch (ctx->alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, crypt, plain);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ switch (ctx->alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, plain, crypt);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ bin_clear_free(ctx, sizeof(*ctx));
+}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 6bff202..f4cff43 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -33,7 +33,9 @@
#include "aes_wrap.h"
#include "crypto.h"
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
/* Compatibility wrappers for older versions. */
static HMAC_CTX * HMAC_CTX_new(void)
@@ -79,7 +81,9 @@
static BIGNUM * get_group5_prime(void)
{
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
return BN_get_rfc3526_prime_1536(NULL);
#elif !defined(OPENSSL_IS_BORINGSSL)
return get_rfc3526_prime_1536(NULL);
@@ -270,10 +274,8 @@
switch (keylen) {
case 16:
return EVP_aes_128_ecb();
-#ifndef OPENSSL_IS_BORINGSSL
case 24:
return EVP_aes_192_ecb();
-#endif /* OPENSSL_IS_BORINGSSL */
case 32:
return EVP_aes_256_ecb();
}
@@ -291,8 +293,11 @@
return NULL;
type = aes_get_evp_cipher(len);
- if (type == NULL)
+ if (!type) {
+ wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
+ __func__, (unsigned int) len);
return NULL;
+ }
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
@@ -345,8 +350,11 @@
return NULL;
type = aes_get_evp_cipher(len);
- if (type == NULL)
+ if (!type) {
+ wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
+ __func__, (unsigned int) len);
return NULL;
+ }
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
@@ -482,6 +490,42 @@
}
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ size_t pubkey_len, pad;
+
+ if (os_get_random(privkey, prime_len) < 0)
+ return -1;
+ if (os_memcmp(privkey, prime, prime_len) > 0) {
+ /* Make sure private value is smaller than prime */
+ privkey[0] = 0;
+ }
+
+ pubkey_len = prime_len;
+ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+ pubkey, &pubkey_len) < 0)
+ return -1;
+ if (pubkey_len < prime_len) {
+ pad = prime_len - pubkey_len;
+ os_memmove(pubkey + pad, pubkey, pubkey_len);
+ os_memset(pubkey, 0, pad);
+ }
+
+ return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
@@ -641,7 +685,9 @@
void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
DH *dh;
struct wpabuf *pubkey = NULL, *privkey = NULL;
size_t publen, privlen;
@@ -742,7 +788,9 @@
void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
DH *dh;
dh = DH_new();
@@ -1199,6 +1247,12 @@
}
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+ return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1;
+}
+
+
int crypto_bignum_add(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
@@ -1324,6 +1378,15 @@
}
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+ struct crypto_bignum *r)
+{
+ /* Note: BN_rshift() does not modify the first argument even though it
+ * has not been marked const. */
+ return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1;
+}
+
+
int crypto_bignum_cmp(const struct crypto_bignum *a,
const struct crypto_bignum *b)
{
@@ -1349,6 +1412,12 @@
}
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+ return BN_is_odd((const BIGNUM *) a);
+}
+
+
int crypto_bignum_legendre(const struct crypto_bignum *a,
const struct crypto_bignum *p)
{
@@ -1483,6 +1552,13 @@
}
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor)
+{
+ return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor,
+ e->bnctx) == 0 ? -1 : 0;
+}
+
+
struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
{
if (TEST_FAIL())
@@ -1505,6 +1581,12 @@
}
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+ return BN_num_bytes(e->order);
+}
+
+
const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
{
return (const struct crypto_bignum *) e->prime;
@@ -1526,6 +1608,16 @@
}
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+ struct crypto_bignum *x)
+{
+ return EC_POINT_get_affine_coordinates_GFp(e->group,
+ (const EC_POINT *) p,
+ (BIGNUM *) x, NULL,
+ e->bnctx) == 1 ? 0 : -1;
+}
+
+
int crypto_ec_point_to_bin(struct crypto_ec *e,
const struct crypto_ec_point *point, u8 *x, u8 *y)
{
@@ -1701,7 +1793,7 @@
{
struct crypto_ecdh *ecdh;
EVP_PKEY *params = NULL;
- EVP_PKEY_CTX *pctx = NULL;
+ EC_KEY *ec_params;
EVP_PKEY_CTX *kctx = NULL;
ecdh = os_zalloc(sizeof(*ecdh));
@@ -1712,27 +1804,17 @@
if (!ecdh->ec)
goto fail;
- pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
- if (!pctx)
- goto fail;
-
- if (EVP_PKEY_paramgen_init(pctx) != 1) {
+ ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid);
+ if (!ec_params) {
wpa_printf(MSG_ERROR,
- "OpenSSL: EVP_PKEY_paramgen_init failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ "OpenSSL: Failed to generate EC_KEY parameters");
goto fail;
}
-
- if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ecdh->ec->nid) != 1) {
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
wpa_printf(MSG_ERROR,
- "OpenSSL: EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (EVP_PKEY_paramgen(pctx, ¶ms) != 1) {
- wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_paramgen failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ "OpenSSL: Failed to generate EVP_PKEY parameters");
goto fail;
}
@@ -1755,7 +1837,6 @@
done:
EVP_PKEY_free(params);
- EVP_PKEY_CTX_free(pctx);
EVP_PKEY_CTX_free(kctx);
return ecdh;
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
new file mode 100644
index 0000000..b5a1e3f
--- /dev/null
+++ b/src/crypto/crypto_wolfssl.c
@@ -0,0 +1,1791 @@
+/*
+ * Wrapper functions for libwolfssl
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+/* wolfSSL headers */
+#include <wolfssl/options.h>
+#include <wolfssl/wolfcrypt/md4.h>
+#include <wolfssl/wolfcrypt/md5.h>
+#include <wolfssl/wolfcrypt/sha.h>
+#include <wolfssl/wolfcrypt/sha256.h>
+#include <wolfssl/wolfcrypt/sha512.h>
+#include <wolfssl/wolfcrypt/hmac.h>
+#include <wolfssl/wolfcrypt/pwdbased.h>
+#include <wolfssl/wolfcrypt/arc4.h>
+#include <wolfssl/wolfcrypt/des3.h>
+#include <wolfssl/wolfcrypt/aes.h>
+#include <wolfssl/wolfcrypt/dh.h>
+#include <wolfssl/wolfcrypt/cmac.h>
+#include <wolfssl/wolfcrypt/ecc.h>
+#include <wolfssl/openssl/bn.h>
+
+
+#ifndef CONFIG_FIPS
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ Md4 md4;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitMd4(&md4);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Md4Update(&md4, addr[i], len[i]);
+
+ wc_Md4Final(&md4, mac);
+
+ return 0;
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ wc_Md5 md5;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitMd5(&md5);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Md5Update(&md5, addr[i], len[i]);
+
+ wc_Md5Final(&md5, mac);
+
+ return 0;
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ wc_Sha sha;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitSha(&sha);
+
+ for (i = 0; i < num_elem; i++)
+ wc_ShaUpdate(&sha, addr[i], len[i]);
+
+ wc_ShaFinal(&sha, mac);
+
+ return 0;
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ wc_Sha256 sha256;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitSha256(&sha256);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Sha256Update(&sha256, addr[i], len[i]);
+
+ wc_Sha256Final(&sha256, mac);
+
+ return 0;
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+#ifdef CONFIG_SHA384
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ wc_Sha384 sha384;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitSha384(&sha384);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Sha384Update(&sha384, addr[i], len[i]);
+
+ wc_Sha384Final(&sha384, mac);
+
+ return 0;
+}
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ wc_Sha512 sha512;
+ size_t i;
+
+ if (TEST_FAIL())
+ return -1;
+
+ wc_InitSha512(&sha512);
+
+ for (i = 0; i < num_elem; i++)
+ wc_Sha512Update(&sha512, addr[i], len[i]);
+
+ wc_Sha512Final(&sha512, mac);
+
+ return 0;
+}
+#endif /* CONFIG_SHA512 */
+
+
+static int wolfssl_hmac_vector(int type, const u8 *key,
+ size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac,
+ unsigned int mdlen)
+{
+ Hmac hmac;
+ size_t i;
+
+ (void) mdlen;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0)
+ return -1;
+ for (i = 0; i < num_elem; i++)
+ if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0)
+ return -1;
+ if (wc_HmacFinal(&hmac, mac) != 0)
+ return -1;
+ return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len,
+ mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len,
+ mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len,
+ mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len,
+ mac, 48);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len,
+ mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid,
+ ssid_len, iterations, buflen, WC_SHA) != 0)
+ return -1;
+ return 0;
+}
+
+
+#ifdef CONFIG_DES
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ Des des;
+ u8 pkey[8], next, tmp;
+ int i;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION);
+ wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE);
+
+ return 0;
+}
+#endif /* CONFIG_DES */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ Aes *aes;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ aes = os_malloc(sizeof(Aes));
+ if (!aes)
+ return NULL;
+
+ if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) {
+ os_free(aes);
+ return NULL;
+ }
+
+ return aes;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ wc_AesEncryptDirect(ctx, crypt, plain);
+ return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ os_free(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ Aes *aes;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ aes = os_malloc(sizeof(Aes));
+ if (!aes)
+ return NULL;
+
+ if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) {
+ os_free(aes);
+ return NULL;
+ }
+
+ return aes;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ wc_AesDecryptDirect(ctx, plain, crypt);
+ return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ os_free(ctx);
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ Aes aes;
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION);
+ if (ret != 0)
+ return -1;
+
+ ret = wc_AesCbcEncrypt(&aes, data, data, data_len);
+ if (ret != 0)
+ return -1;
+ return 0;
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ Aes aes;
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION);
+ if (ret != 0)
+ return -1;
+
+ ret = wc_AesCbcDecrypt(&aes, data, data, data_len);
+ if (ret != 0)
+ return -1;
+ return 0;
+}
+
+
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8,
+ NULL);
+ return ret != (n + 1) * 8 ? -1 : 0;
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+ u8 *plain)
+{
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8,
+ NULL);
+ return ret != n * 8 ? -1 : 0;
+}
+
+
+#ifndef CONFIG_NO_RC4
+int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data,
+ size_t data_len)
+{
+#ifndef NO_RC4
+ Arc4 arc4;
+ unsigned char skip_buf[16];
+
+ wc_Arc4SetKey(&arc4, key, keylen);
+
+ while (skip >= sizeof(skip_buf)) {
+ size_t len = skip;
+
+ if (len > sizeof(skip_buf))
+ len = sizeof(skip_buf);
+ wc_Arc4Process(&arc4, skip_buf, skip_buf, len);
+ skip -= len;
+ }
+
+ wc_Arc4Process(&arc4, data, data, data_len);
+
+ return 0;
+#else /* NO_RC4 */
+ return -1;
+#endif /* NO_RC4 */
+}
+#endif /* CONFIG_NO_RC4 */
+
+
+#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \
+ || defined(EAP_SERVER_IKEV2)
+union wolfssl_cipher {
+ Aes aes;
+ Des3 des3;
+ Arc4 arc4;
+};
+
+struct crypto_cipher {
+ enum crypto_cipher_alg alg;
+ union wolfssl_cipher enc;
+ union wolfssl_cipher dec;
+};
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ switch (alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+ case CRYPTO_CIPHER_ALG_RC4:
+ wc_Arc4SetKey(&ctx->enc.arc4, key, key_len);
+ wc_Arc4SetKey(&ctx->dec.arc4, key, key_len);
+ break;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+ case CRYPTO_CIPHER_ALG_AES:
+ switch (key_len) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+ if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv,
+ AES_ENCRYPTION) ||
+ wc_AesSetKey(&ctx->dec.aes, key, key_len, iv,
+ AES_DECRYPTION)) {
+ os_free(ctx);
+ return NULL;
+ }
+ break;
+#endif /* NO_AES */
+#ifndef NO_DES3
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (key_len != DES3_KEYLEN ||
+ wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) ||
+ wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) {
+ os_free(ctx);
+ return NULL;
+ }
+ break;
+#endif /* NO_DES3 */
+ case CRYPTO_CIPHER_ALG_RC2:
+ case CRYPTO_CIPHER_ALG_DES:
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ ctx->alg = alg;
+
+ return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ switch (ctx->alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+ case CRYPTO_CIPHER_ALG_RC4:
+ wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len);
+ return 0;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+ case CRYPTO_CIPHER_ALG_AES:
+ if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0)
+ return -1;
+ return 0;
+#endif /* NO_AES */
+#ifndef NO_DES3
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0)
+ return -1;
+ return 0;
+#endif /* NO_DES3 */
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ switch (ctx->alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+ case CRYPTO_CIPHER_ALG_RC4:
+ wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len);
+ return 0;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+ case CRYPTO_CIPHER_ALG_AES:
+ if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0)
+ return -1;
+ return 0;
+#endif /* NO_AES */
+#ifndef NO_DES3
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0)
+ return -1;
+ return 0;
+#endif /* NO_DES3 */
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ os_free(ctx);
+}
+
+#endif
+
+
+#ifdef CONFIG_WPS_NFC
+
+static const unsigned char RFC3526_PRIME_1536[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+ 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const unsigned char RFC3526_GENERATOR_1536[] = {
+ 0x02
+};
+
+#define RFC3526_LEN sizeof(RFC3526_PRIME_1536)
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+ WC_RNG rng;
+ DhKey *ret = NULL;
+ DhKey *dh = NULL;
+ struct wpabuf *privkey = NULL;
+ struct wpabuf *pubkey = NULL;
+ word32 priv_sz, pub_sz;
+
+ *priv = NULL;
+ wpabuf_free(*publ);
+ *publ = NULL;
+
+ dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (!dh)
+ return NULL;
+ wc_InitDhKey(dh);
+
+ if (wc_InitRng(&rng) != 0) {
+ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return NULL;
+ }
+
+ privkey = wpabuf_alloc(RFC3526_LEN);
+ pubkey = wpabuf_alloc(RFC3526_LEN);
+ if (!privkey || !pubkey)
+ goto done;
+
+ if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
+ RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
+ != 0)
+ goto done;
+
+ if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz,
+ wpabuf_mhead(pubkey), &pub_sz) != 0)
+ goto done;
+
+ wpabuf_put(privkey, priv_sz);
+ wpabuf_put(pubkey, pub_sz);
+
+ ret = dh;
+ *priv = privkey;
+ *publ = pubkey;
+ dh = NULL;
+ privkey = NULL;
+ pubkey = NULL;
+done:
+ wpabuf_clear_free(pubkey);
+ wpabuf_clear_free(privkey);
+ if (dh) {
+ wc_FreeDhKey(dh);
+ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ }
+ wc_FreeRng(&rng);
+ return ret;
+}
+
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+ DhKey *ret = NULL;
+ DhKey *dh;
+ byte *secret;
+ word32 secret_sz;
+
+ dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (!dh)
+ return NULL;
+ wc_InitDhKey(dh);
+
+ secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (!secret)
+ goto done;
+
+ if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
+ RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
+ != 0)
+ goto done;
+
+ if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv),
+ wpabuf_len(priv), RFC3526_GENERATOR_1536,
+ sizeof(RFC3526_GENERATOR_1536)) != 0)
+ goto done;
+
+ if (secret_sz != wpabuf_len(publ) ||
+ os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0)
+ goto done;
+
+ ret = dh;
+ dh = NULL;
+done:
+ if (dh) {
+ wc_FreeDhKey(dh);
+ XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ }
+ XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+ const struct wpabuf *own_private)
+{
+ struct wpabuf *ret = NULL;
+ struct wpabuf *secret;
+ word32 secret_sz;
+
+ secret = wpabuf_alloc(RFC3526_LEN);
+ if (!secret)
+ goto done;
+
+ if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz,
+ wpabuf_head(own_private), wpabuf_len(own_private),
+ wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0)
+ goto done;
+
+ wpabuf_put(secret, secret_sz);
+
+ ret = secret;
+ secret = NULL;
+done:
+ wpabuf_clear_free(secret);
+ return ret;
+}
+
+
+void dh5_free(void *ctx)
+{
+ if (!ctx)
+ return;
+
+ wc_FreeDhKey(ctx);
+ XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+ u8 *pubkey)
+{
+ int ret = -1;
+ WC_RNG rng;
+ DhKey *dh = NULL;
+ word32 priv_sz, pub_sz;
+
+ if (TEST_FAIL())
+ return -1;
+
+ dh = os_malloc(sizeof(DhKey));
+ if (!dh)
+ return -1;
+ wc_InitDhKey(dh);
+
+ if (wc_InitRng(&rng) != 0) {
+ os_free(dh);
+ return -1;
+ }
+
+ if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
+ goto done;
+
+ if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz)
+ != 0)
+ goto done;
+
+ if (priv_sz < prime_len) {
+ size_t pad_sz = prime_len - priv_sz;
+
+ os_memmove(privkey + pad_sz, privkey, priv_sz);
+ os_memset(privkey, 0, pad_sz);
+ }
+
+ if (pub_sz < prime_len) {
+ size_t pad_sz = prime_len - pub_sz;
+
+ os_memmove(pubkey + pad_sz, pubkey, pub_sz);
+ os_memset(pubkey, 0, pad_sz);
+ }
+ ret = 0;
+done:
+ wc_FreeDhKey(dh);
+ os_free(dh);
+ wc_FreeRng(&rng);
+ return ret;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *privkey, size_t privkey_len,
+ const u8 *pubkey, size_t pubkey_len,
+ u8 *secret, size_t *len)
+{
+ int ret = -1;
+ DhKey *dh;
+ word32 secret_sz;
+
+ dh = os_malloc(sizeof(DhKey));
+ if (!dh)
+ return -1;
+ wc_InitDhKey(dh);
+
+ if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
+ goto done;
+
+ if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey,
+ pubkey_len) != 0)
+ goto done;
+
+ *len = secret_sz;
+ ret = 0;
+done:
+ wc_FreeDhKey(dh);
+ os_free(dh);
+ return ret;
+}
+
+
+#ifdef CONFIG_FIPS
+int crypto_get_random(void *buf, size_t len)
+{
+ int ret = 0;
+ WC_RNG rng;
+
+ if (wc_InitRng(&rng) != 0)
+ return -1;
+ if (wc_RNG_GenerateBlock(&rng, buf, len) != 0)
+ ret = -1;
+ wc_FreeRng(&rng);
+ return ret;
+}
+#endif /* CONFIG_FIPS */
+
+
+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD)
+struct crypto_hash {
+ Hmac hmac;
+ int size;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_hash *ret = NULL;
+ struct crypto_hash *hash;
+ int type;
+
+ hash = os_zalloc(sizeof(*hash));
+ if (!hash)
+ goto done;
+
+ switch (alg) {
+#ifndef NO_MD5
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ hash->size = 16;
+ type = WC_MD5;
+ break;
+#endif /* NO_MD5 */
+#ifndef NO_SHA
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ type = WC_SHA;
+ hash->size = 20;
+ break;
+#endif /* NO_SHA */
+#ifdef CONFIG_SHA256
+#ifndef NO_SHA256
+ case CRYPTO_HASH_ALG_HMAC_SHA256:
+ type = WC_SHA256;
+ hash->size = 32;
+ break;
+#endif /* NO_SHA256 */
+#endif /* CONFIG_SHA256 */
+ default:
+ goto done;
+ }
+
+ if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0)
+ goto done;
+
+ ret = hash;
+ hash = NULL;
+done:
+ os_free(hash);
+ return ret;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+ if (!ctx)
+ return;
+ wc_HmacUpdate(&ctx->hmac, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+ int ret = 0;
+
+ if (!ctx)
+ return -2;
+
+ if (!mac || !len)
+ goto done;
+
+ if (wc_HmacFinal(&ctx->hmac, mac) != 0) {
+ ret = -1;
+ goto done;
+ }
+
+ *len = ctx->size;
+ ret = 0;
+done:
+ bin_clear_free(ctx, sizeof(*ctx));
+ return ret;
+}
+
+#endif
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ Cmac cmac;
+ size_t i;
+ word32 sz;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0)
+ return -1;
+
+ for (i = 0; i < num_elem; i++)
+ if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0)
+ return -1;
+
+ sz = AES_BLOCK_SIZE;
+ if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE)
+ return -1;
+
+ return 0;
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+ mp_int *a;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ a = os_malloc(sizeof(*a));
+ if (!a || mp_init(a) != MP_OKAY) {
+ os_free(a);
+ a = NULL;
+ }
+
+ return (struct crypto_bignum *) a;
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+ mp_int *a;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ a = (mp_int *) crypto_bignum_init();
+ if (!a)
+ return NULL;
+
+ if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) {
+ os_free(a);
+ a = NULL;
+ }
+
+ return (struct crypto_bignum *) a;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+ if (!n)
+ return;
+
+ if (clear)
+ mp_forcezero((mp_int *) n);
+ mp_clear((mp_int *) n);
+ os_free((mp_int *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+ u8 *buf, size_t buflen, size_t padlen)
+{
+ int num_bytes, offset;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (padlen > buflen)
+ return -1;
+
+ num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8;
+ if ((size_t) num_bytes > buflen)
+ return -1;
+ if (padlen > (size_t) num_bytes)
+ offset = padlen - num_bytes;
+ else
+ offset = 0;
+
+ os_memset(buf, 0, offset);
+ mp_to_unsigned_bin((mp_int *) a, buf + offset);
+
+ return num_bytes + offset;
+}
+
+
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+ int ret = 0;
+ WC_RNG rng;
+
+ if (wc_InitRng(&rng) != 0)
+ return -1;
+ if (mp_rand_prime((mp_int *) r,
+ (mp_count_bits((mp_int *) m) + 7) / 8 * 2,
+ &rng, NULL) != 0)
+ ret = -1;
+ if (ret == 0 &&
+ mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
+ ret = -1;
+ wc_FreeRng(&rng);
+ return ret;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *r)
+{
+ return mp_add((mp_int *) a, (mp_int *) b,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+ const struct crypto_bignum *m,
+ struct crypto_bignum *r)
+{
+ return mp_mod((mp_int *) a, (mp_int *) m,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *b,
+ const struct crypto_bignum *e,
+ const struct crypto_bignum *m,
+ struct crypto_bignum *r)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+ const struct crypto_bignum *m,
+ struct crypto_bignum *r)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_invmod((mp_int *) a, (mp_int *) m,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *r)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_add((mp_int *) a, (mp_int *) b,
+ (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ struct crypto_bignum *d)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d,
+ NULL) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+ const struct crypto_bignum *b,
+ const struct crypto_bignum *m,
+ struct crypto_bignum *d)
+{
+ if (TEST_FAIL())
+ return -1;
+
+ return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m,
+ (mp_int *) d) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+ struct crypto_bignum *r)
+{
+ if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY)
+ return -1;
+ mp_rshb((mp_int *) r, n);
+ return 0;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+ const struct crypto_bignum *b)
+{
+ return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+int crypto_bignum_bits(const struct crypto_bignum *a)
+{
+ return mp_count_bits((mp_int *) a);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+ return mp_iszero((mp_int *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+ return mp_isone((const mp_int *) a);
+}
+
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+ return mp_isodd((mp_int *) a);
+}
+
+
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+ const struct crypto_bignum *p)
+{
+ mp_int t;
+ int ret;
+ int res = -2;
+
+ if (TEST_FAIL())
+ return -2;
+
+ if (mp_init(&t) != MP_OKAY)
+ return -2;
+
+ /* t = (p-1) / 2 */
+ ret = mp_sub_d((mp_int *) p, 1, &t);
+ if (ret == MP_OKAY)
+ mp_rshb(&t, 1);
+ if (ret == MP_OKAY)
+ ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t);
+ if (ret == MP_OKAY) {
+ if (mp_isone(&t))
+ res = 1;
+ else if (mp_iszero(&t))
+ res = 0;
+ else
+ res = -1;
+ }
+
+ mp_clear(&t);
+ return res;
+}
+
+
+#ifdef CONFIG_ECC
+
+int ecc_map(ecc_point *, mp_int *, mp_digit);
+int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R,
+ mp_int *a, mp_int *modulus, mp_digit mp);
+
+struct crypto_ec {
+ ecc_key key;
+ mp_int a;
+ mp_int prime;
+ mp_int order;
+ mp_digit mont_b;
+ mp_int b;
+};
+
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+ int built = 0;
+ struct crypto_ec *e;
+ int curve_id;
+
+ /* Map from IANA registry for IKE D-H groups to OpenSSL NID */
+ switch (group) {
+ case 19:
+ curve_id = ECC_SECP256R1;
+ break;
+ case 20:
+ curve_id = ECC_SECP384R1;
+ break;
+ case 21:
+ curve_id = ECC_SECP521R1;
+ break;
+ case 25:
+ curve_id = ECC_SECP192R1;
+ break;
+ case 26:
+ curve_id = ECC_SECP224R1;
+ break;
+#ifdef HAVE_ECC_BRAINPOOL
+ case 27:
+ curve_id = ECC_BRAINPOOLP224R1;
+ break;
+ case 28:
+ curve_id = ECC_BRAINPOOLP256R1;
+ break;
+ case 29:
+ curve_id = ECC_BRAINPOOLP384R1;
+ break;
+ case 30:
+ curve_id = ECC_BRAINPOOLP512R1;
+ break;
+#endif /* HAVE_ECC_BRAINPOOL */
+ default:
+ return NULL;
+ }
+
+ e = os_zalloc(sizeof(*e));
+ if (!e)
+ return NULL;
+
+ if (wc_ecc_init(&e->key) != 0 ||
+ wc_ecc_set_curve(&e->key, 0, curve_id) != 0 ||
+ mp_init(&e->a) != MP_OKAY ||
+ mp_init(&e->prime) != MP_OKAY ||
+ mp_init(&e->order) != MP_OKAY ||
+ mp_init(&e->b) != MP_OKAY ||
+ mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY ||
+ mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY ||
+ mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY ||
+ mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY ||
+ mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY)
+ goto done;
+
+ built = 1;
+done:
+ if (!built) {
+ crypto_ec_deinit(e);
+ e = NULL;
+ }
+ return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec* e)
+{
+ if (!e)
+ return;
+
+ mp_clear(&e->b);
+ mp_clear(&e->order);
+ mp_clear(&e->prime);
+ mp_clear(&e->a);
+ wc_ecc_free(&e->key);
+ os_free(e);
+}
+
+
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor)
+{
+ if (!e || !cofactor)
+ return -1;
+
+ mp_set((mp_int *) cofactor, e->key.dp->cofactor);
+ return 0;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+ if (TEST_FAIL())
+ return NULL;
+ if (!e)
+ return NULL;
+ return (struct crypto_ec_point *) wc_ecc_new_point();
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+ return (mp_count_bits(&e->prime) + 7) / 8;
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+ return mp_count_bits(&e->prime);
+}
+
+
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+ return (mp_count_bits(&e->order) + 7) / 8;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+ return (const struct crypto_bignum *) &e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+ return (const struct crypto_bignum *) &e->order;
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+ ecc_point *point = (ecc_point *) p;
+
+ if (!p)
+ return;
+
+ if (clear) {
+ mp_forcezero(point->x);
+ mp_forcezero(point->y);
+ mp_forcezero(point->z);
+ }
+ wc_ecc_del_point(point);
+}
+
+
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+ struct crypto_bignum *x)
+{
+ return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+ const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+ ecc_point *p = (ecc_point *) point;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (!mp_isone(p->z)) {
+ if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY)
+ return -1;
+ }
+
+ if (x) {
+ if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x,
+ e->key.dp->size,
+ e->key.dp->size) <= 0)
+ return -1;
+ }
+
+ if (y) {
+ if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y,
+ e->key.dp->size,
+ e->key.dp->size) <= 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+ const u8 *val)
+{
+ ecc_point *point = NULL;
+ int loaded = 0;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ point = wc_ecc_new_point();
+ if (!point)
+ goto done;
+
+ if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY)
+ goto done;
+ val += e->key.dp->size;
+ if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY)
+ goto done;
+ mp_set(point->z, 1);
+
+ loaded = 1;
+done:
+ if (!loaded) {
+ wc_ecc_del_point(point);
+ point = NULL;
+ }
+ return (struct crypto_ec_point *) point;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b,
+ struct crypto_ec_point *c)
+{
+ mp_int mu;
+ ecc_point *ta = NULL, *tb = NULL;
+ ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b;
+ mp_int *modulus = &e->prime;
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = mp_init(&mu);
+ if (ret != MP_OKAY)
+ return -1;
+
+ ret = mp_montgomery_calc_normalization(&mu, modulus);
+ if (ret != MP_OKAY) {
+ mp_clear(&mu);
+ return -1;
+ }
+
+ if (!mp_isone(&mu)) {
+ ta = wc_ecc_new_point();
+ if (!ta) {
+ mp_clear(&mu);
+ return -1;
+ }
+ tb = wc_ecc_new_point();
+ if (!tb) {
+ wc_ecc_del_point(ta);
+ mp_clear(&mu);
+ return -1;
+ }
+
+ if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY ||
+ mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY ||
+ mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY ||
+ mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY ||
+ mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY ||
+ mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) {
+ ret = -1;
+ goto end;
+ }
+ pa = ta;
+ pb = tb;
+ }
+
+ ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a,
+ &e->prime, e->mont_b);
+ if (ret != 0) {
+ ret = -1;
+ goto end;
+ }
+
+ if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY)
+ ret = -1;
+ else
+ ret = 0;
+end:
+ wc_ecc_del_point(tb);
+ wc_ecc_del_point(ta);
+ mp_clear(&mu);
+ return ret;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+ const struct crypto_bignum *b,
+ struct crypto_ec_point *res)
+{
+ int ret;
+
+ if (TEST_FAIL())
+ return -1;
+
+ ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res,
+ &e->a, &e->prime, 1);
+ return ret == 0 ? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+ ecc_point *point = (ecc_point *) p;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY)
+ return -1;
+
+ return 0;
+}
+
+
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit)
+{
+ byte buf[1 + 2 * MAX_ECC_BYTES];
+ int ret;
+ int prime_len = crypto_ec_prime_len(e);
+
+ if (TEST_FAIL())
+ return -1;
+
+ buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN;
+ ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len);
+ if (ret <= 0)
+ return -1;
+ ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx,
+ (ecc_point *) p);
+ if (ret != 0)
+ return -1;
+
+ return 0;
+}
+
+
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+ const struct crypto_bignum *x)
+{
+ mp_int *y2 = NULL;
+ mp_int t;
+ int calced = 0;
+
+ if (TEST_FAIL())
+ return NULL;
+
+ if (mp_init(&t) != MP_OKAY)
+ return NULL;
+
+ y2 = (mp_int *) crypto_bignum_init();
+ if (!y2)
+ goto done;
+
+ if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 ||
+ mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 ||
+ mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 ||
+ mp_addmod(y2, &t, &e->prime, y2) != 0 ||
+ mp_addmod(y2, &e->b, &e->prime, y2) != 0)
+ goto done;
+
+ calced = 1;
+done:
+ if (!calced) {
+ if (y2) {
+ mp_clear(y2);
+ os_free(y2);
+ }
+ mp_clear(&t);
+ }
+
+ return (struct crypto_bignum *) y2;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+ const struct crypto_ec_point *p)
+{
+ return wc_ecc_point_is_at_infinity((ecc_point *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+ const struct crypto_ec_point *p)
+{
+ return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) ==
+ MP_OKAY;
+}
+
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+ const struct crypto_ec_point *a,
+ const struct crypto_ec_point *b)
+{
+ return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b);
+}
+
+
+struct crypto_ecdh {
+ struct crypto_ec *ec;
+};
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+ struct crypto_ecdh *ecdh = NULL;
+ WC_RNG rng;
+ int ret;
+
+ if (wc_InitRng(&rng) != 0)
+ goto fail;
+
+ ecdh = os_zalloc(sizeof(*ecdh));
+ if (!ecdh)
+ goto fail;
+
+ ecdh->ec = crypto_ec_init(group);
+ if (!ecdh->ec)
+ goto fail;
+
+ ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key,
+ ecdh->ec->key.dp->id);
+ if (ret < 0)
+ goto fail;
+
+done:
+ wc_FreeRng(&rng);
+
+ return ecdh;
+fail:
+ crypto_ecdh_deinit(ecdh);
+ ecdh = NULL;
+ goto done;
+}
+
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+ if (ecdh) {
+ crypto_ec_deinit(ecdh->ec);
+ os_free(ecdh);
+ }
+}
+
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+ struct wpabuf *buf = NULL;
+ int ret;
+ int len = ecdh->ec->key.dp->size;
+
+ buf = wpabuf_alloc(inc_y ? 2 * len : len);
+ if (!buf)
+ goto fail;
+
+ ret = crypto_bignum_to_bin((struct crypto_bignum *)
+ ecdh->ec->key.pubkey.x, wpabuf_put(buf, len),
+ len, len);
+ if (ret < 0)
+ goto fail;
+ if (inc_y) {
+ ret = crypto_bignum_to_bin((struct crypto_bignum *)
+ ecdh->ec->key.pubkey.y,
+ wpabuf_put(buf, len), len, len);
+ if (ret < 0)
+ goto fail;
+ }
+
+done:
+ return buf;
+fail:
+ wpabuf_free(buf);
+ buf = NULL;
+ goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+ const u8 *key, size_t len)
+{
+ int ret;
+ struct wpabuf *pubkey = NULL;
+ struct wpabuf *secret = NULL;
+ word32 key_len = ecdh->ec->key.dp->size;
+ ecc_point *point = NULL;
+ size_t need_key_len = inc_y ? 2 * key_len : key_len;
+
+ if (len < need_key_len)
+ goto fail;
+ pubkey = wpabuf_alloc(1 + 2 * key_len);
+ if (!pubkey)
+ goto fail;
+ wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN);
+ wpabuf_put_data(pubkey, key, need_key_len);
+
+ point = wc_ecc_new_point();
+ if (!point)
+ goto fail;
+
+ ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len,
+ ecdh->ec->key.idx, point);
+ if (ret != MP_OKAY)
+ goto fail;
+
+ secret = wpabuf_alloc(key_len);
+ if (!secret)
+ goto fail;
+
+ ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point,
+ wpabuf_put(secret, key_len), &key_len);
+ if (ret != MP_OKAY)
+ goto fail;
+
+done:
+ wc_ecc_del_point(point);
+ wpabuf_free(pubkey);
+ return secret;
+fail:
+ wpabuf_free(secret);
+ secret = NULL;
+ goto done;
+}
+
+#endif /* CONFIG_ECC */
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 7912361..a9b770e 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1151,7 +1151,7 @@
{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
-
+
static const struct dh_group dh_groups[] = {
DH_GROUP(5, 1),
@@ -1203,19 +1203,6 @@
if (*priv == NULL)
return NULL;
- if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
- {
- wpabuf_clear_free(*priv);
- *priv = NULL;
- return NULL;
- }
-
- if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) {
- /* Make sure private value is smaller than prime */
- *(wpabuf_mhead_u8(*priv)) = 0;
- }
- wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
-
pv_len = dh->prime_len;
pv = wpabuf_alloc(pv_len);
if (pv == NULL) {
@@ -1223,17 +1210,17 @@
*priv = NULL;
return NULL;
}
- if (crypto_mod_exp(dh->generator, dh->generator_len,
- wpabuf_head(*priv), wpabuf_len(*priv),
- dh->prime, dh->prime_len, wpabuf_mhead(pv),
- &pv_len) < 0) {
+ if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len,
+ wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) {
wpabuf_clear_free(pv);
- wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+ wpa_printf(MSG_INFO, "DH: crypto_dh_init failed");
wpabuf_clear_free(*priv);
*priv = NULL;
return NULL;
}
- wpabuf_put(pv, pv_len);
+ wpabuf_put(*priv, dh->prime_len);
+ wpabuf_put(pv, dh->prime_len);
+ wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
return pv;
@@ -1261,12 +1248,14 @@
shared = wpabuf_alloc(shared_len);
if (shared == NULL)
return NULL;
- if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public),
- wpabuf_head(own_private), wpabuf_len(own_private),
- dh->prime, dh->prime_len,
- wpabuf_mhead(shared), &shared_len) < 0) {
+ if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+ wpabuf_head(own_private),
+ wpabuf_len(own_private),
+ wpabuf_head(peer_public),
+ wpabuf_len(peer_public),
+ wpabuf_mhead(shared), &shared_len) < 0) {
wpabuf_clear_free(shared);
- wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+ wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed");
return NULL;
}
wpabuf_put(shared, shared_len);
diff --git a/src/crypto/fips_prf_wolfssl.c b/src/crypto/fips_prf_wolfssl.c
new file mode 100644
index 0000000..feb39db
--- /dev/null
+++ b/src/crypto/fips_prf_wolfssl.c
@@ -0,0 +1,87 @@
+/*
+ * FIPS 186-2 PRF for libcrypto
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <wolfssl/options.h>
+#include <wolfssl/wolfcrypt/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static void sha1_transform(u32 *state, const u8 data[64])
+{
+ wc_Sha sha;
+
+ os_memset(&sha, 0, sizeof(sha));
+ sha.digest[0] = state[0];
+ sha.digest[1] = state[1];
+ sha.digest[2] = state[2];
+ sha.digest[3] = state[3];
+ sha.digest[4] = state[4];
+ wc_ShaUpdate(&sha, data, 64);
+ state[0] = sha.digest[0];
+ state[1] = sha.digest[1];
+ state[2] = sha.digest[2];
+ state[3] = sha.digest[3];
+ state[4] = sha.digest[4];
+}
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+ u8 xkey[64];
+ u32 t[5], _t[5];
+ int i, j, m, k;
+ u8 *xpos = x;
+ u32 carry;
+
+ if (seed_len < sizeof(xkey))
+ os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+ else
+ seed_len = sizeof(xkey);
+
+ /* FIPS 186-2 + change notice 1 */
+
+ os_memcpy(xkey, seed, seed_len);
+ t[0] = 0x67452301;
+ t[1] = 0xEFCDAB89;
+ t[2] = 0x98BADCFE;
+ t[3] = 0x10325476;
+ t[4] = 0xC3D2E1F0;
+
+ m = xlen / 40;
+ for (j = 0; j < m; j++) {
+ /* XSEED_j = 0 */
+ for (i = 0; i < 2; i++) {
+ /* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+ /* w_i = G(t, XVAL) */
+ os_memcpy(_t, t, 20);
+ sha1_transform(_t, xkey);
+ WPA_PUT_BE32(xpos, _t[0]);
+ WPA_PUT_BE32(xpos + 4, _t[1]);
+ WPA_PUT_BE32(xpos + 8, _t[2]);
+ WPA_PUT_BE32(xpos + 12, _t[3]);
+ WPA_PUT_BE32(xpos + 16, _t[4]);
+
+ /* XKEY = (1 + XKEY + w_i) mod 2^b */
+ carry = 1;
+ for (k = 19; k >= 0; k--) {
+ carry += xkey[k] + xpos[k];
+ xkey[k] = carry & 0xff;
+ carry >>= 8;
+ }
+
+ xpos += 20;
+ }
+ /* x_j = w_0|w_1 */
+ }
+
+ return 0;
+}
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index dc4117c..585db8b 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -101,6 +101,7 @@
#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
#define TLS_CONN_SUITEB BIT(11)
#define TLS_CONN_SUITEB_NO_ECDH BIT(12)
+#define TLS_CONN_DISABLE_TLSv1_3 BIT(13)
/**
* struct tls_connection_params - Parameters for TLS connection
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index 8c76bfa..7ee3fa3 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -1,6 +1,6 @@
/*
* SSL/TLS interface functions for GnuTLS
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -346,6 +346,9 @@
const struct tls_connection_params *params)
{
int ret;
+ const char *err;
+ char prio_buf[100];
+ const char *prio = NULL;
if (conn == NULL || params == NULL)
return -1;
@@ -397,9 +400,57 @@
conn->flags = params->flags;
+ if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 |
+ TLS_CONN_DISABLE_TLSv1_1 |
+ TLS_CONN_DISABLE_TLSv1_2)) {
+ os_snprintf(prio_buf, sizeof(prio_buf),
+ "NORMAL:-VERS-SSL3.0%s%s%s",
+ params->flags & TLS_CONN_DISABLE_TLSv1_0 ?
+ ":-VERS-TLS1.0" : "",
+ params->flags & TLS_CONN_DISABLE_TLSv1_1 ?
+ ":-VERS-TLS1.1" : "",
+ params->flags & TLS_CONN_DISABLE_TLSv1_2 ?
+ ":-VERS-TLS1.2" : "");
+ prio = prio_buf;
+ }
+
if (params->openssl_ciphers) {
- wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
- return -1;
+ if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
+ prio = "SUITEB128";
+ } else if (os_strcmp(params->openssl_ciphers,
+ "SUITEB192") == 0) {
+ prio = "SUITEB192";
+ } else if ((params->flags & TLS_CONN_SUITEB) &&
+ os_strcmp(params->openssl_ciphers,
+ "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+ } else if (os_strcmp(params->openssl_ciphers,
+ "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+ } else if (os_strcmp(params->openssl_ciphers,
+ "DHE-RSA-AES256-GCM-SHA384") == 0) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
+ } else if (os_strcmp(params->openssl_ciphers,
+ "ECDHE-ECDSA-AES256-GCM-SHA384") == 0) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+ } else {
+ wpa_printf(MSG_INFO,
+ "GnuTLS: openssl_ciphers not supported");
+ return -1;
+ }
+ } else if (params->flags & TLS_CONN_SUITEB) {
+ prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
+ }
+
+ if (prio) {
+ wpa_printf(MSG_DEBUG, "GnuTLS: Set priority string: %s", prio);
+ ret = gnutls_priority_set_direct(conn->session, prio, &err);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ "GnuTLS: Priority string failure at '%s'",
+ err);
+ return -1;
+ }
}
/* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
@@ -425,6 +476,13 @@
gnutls_strerror(ret));
return -1;
}
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Successfully read CA cert '%s' in PEM format",
+ params->ca_cert);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Successfully read CA cert '%s' in DER format",
+ params->ca_cert);
}
} else if (params->ca_cert_blob) {
gnutls_datum_t ca;
@@ -472,6 +530,9 @@
}
if (params->client_cert && params->private_key) {
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Try to parse client cert '%s' and key '%s' in DER format",
+ params->client_cert, params->private_key);
#if GNUTLS_VERSION_NUMBER >= 0x03010b
ret = gnutls_certificate_set_x509_key_file2(
conn->xcred, params->client_cert, params->private_key,
@@ -483,8 +544,9 @@
GNUTLS_X509_FMT_DER);
#endif
if (ret < 0) {
- wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
- "in DER format: %s", gnutls_strerror(ret));
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Failed to read client cert/key in DER format (%s) - try in PEM format",
+ gnutls_strerror(ret));
#if GNUTLS_VERSION_NUMBER >= 0x03010b
ret = gnutls_certificate_set_x509_key_file2(
conn->xcred, params->client_cert,
@@ -501,11 +563,19 @@
gnutls_strerror(ret));
return ret;
}
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Successfully read client cert/key in PEM format");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Successfully read client cert/key in DER format");
}
} else if (params->private_key) {
int pkcs12_ok = 0;
#ifdef PKCS12_FUNCS
/* Try to load in PKCS#12 format */
+ wpa_printf(MSG_DEBUG,
+ "GnuTLS: Try to parse client cert/key '%s'in PKCS#12 DER format",
+ params->private_key);
ret = gnutls_certificate_set_x509_simple_pkcs12_file(
conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
params->private_key_passwd);
@@ -1333,6 +1403,7 @@
ret = gnutls_handshake(conn->session);
if (ret < 0) {
gnutls_alert_description_t alert;
+ union tls_event_data ev;
switch (ret) {
case GNUTLS_E_AGAIN:
@@ -1343,14 +1414,29 @@
conn->push_buf = wpabuf_alloc(0);
}
break;
+ case GNUTLS_E_DH_PRIME_UNACCEPTABLE:
+ wpa_printf(MSG_DEBUG, "GnuTLS: Unacceptable DH prime");
+ if (conn->global->event_cb) {
+ os_memset(&ev, 0, sizeof(ev));
+ ev.alert.is_local = 1;
+ ev.alert.type = "fatal";
+ ev.alert.description = "insufficient security";
+ conn->global->event_cb(conn->global->cb_ctx,
+ TLS_ALERT, &ev);
+ }
+ /*
+ * Could send a TLS Alert to the server, but for now,
+ * simply terminate handshake.
+ */
+ conn->failed++;
+ conn->write_alerts++;
+ break;
case GNUTLS_E_FATAL_ALERT_RECEIVED:
alert = gnutls_alert_get(conn->session);
wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
__func__, gnutls_alert_get_name(alert));
conn->read_alerts++;
if (conn->global->event_cb != NULL) {
- union tls_event_data ev;
-
os_memset(&ev, 0, sizeof(ev));
ev.alert.is_local = 0;
ev.alert.type = gnutls_alert_get_name(alert);
@@ -1501,16 +1587,53 @@
int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
- /* TODO */
- return -1;
+ gnutls_protocol_t ver;
+
+ ver = gnutls_protocol_get_version(conn->session);
+ if (ver == GNUTLS_TLS1_0)
+ os_strlcpy(buf, "TLSv1", buflen);
+ else if (ver == GNUTLS_TLS1_1)
+ os_strlcpy(buf, "TLSv1.1", buflen);
+ else if (ver == GNUTLS_TLS1_2)
+ os_strlcpy(buf, "TLSv1.2", buflen);
+ else
+ return -1;
+ return 0;
}
int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
- /* TODO */
- buf[0] = '\0';
+ gnutls_cipher_algorithm_t cipher;
+ gnutls_kx_algorithm_t kx;
+ gnutls_mac_algorithm_t mac;
+ const char *kx_str, *cipher_str, *mac_str;
+ int res;
+
+ cipher = gnutls_cipher_get(conn->session);
+ cipher_str = gnutls_cipher_get_name(cipher);
+ if (!cipher_str)
+ cipher_str = "";
+
+ kx = gnutls_kx_get(conn->session);
+ kx_str = gnutls_kx_get_name(kx);
+ if (!kx_str)
+ kx_str = "";
+
+ mac = gnutls_mac_get(conn->session);
+ mac_str = gnutls_mac_get_name(mac);
+ if (!mac_str)
+ mac_str = "";
+
+ if (kx == GNUTLS_KX_RSA)
+ res = os_snprintf(buf, buflen, "%s-%s", cipher_str, mac_str);
+ else
+ res = os_snprintf(buf, buflen, "%s-%s-%s",
+ kx_str, cipher_str, mac_str);
+ if (os_snprintf_error(buflen, res))
+ return -1;
+
return 0;
}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 7243d9f..47ef642 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -59,7 +59,8 @@
#endif /* SSL_set_tlsext_status_type */
#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
- defined(LIBRESSL_VERSION_NUMBER)) && \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \
!defined(BORINGSSL_API_VERSION)
/*
* SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
@@ -245,6 +246,8 @@
unsigned int server_cert_only:1;
unsigned int invalid_hb_used:1;
unsigned int success_data:1;
+ unsigned int client_hello_generated:1;
+ unsigned int server:1;
u8 srv_cert_hash[32];
@@ -945,7 +948,9 @@
}
#endif /* OPENSSL_FIPS */
#endif /* CONFIG_FIPS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
SSL_load_error_strings();
SSL_library_init();
#ifndef OPENSSL_NO_SHA256
@@ -1077,7 +1082,9 @@
tls_openssl_ref_count--;
if (tls_openssl_ref_count == 0) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif /* OPENSSL_NO_ENGINE */
@@ -2451,7 +2458,8 @@
#endif /* CONFIG_SUITEB */
-static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags)
+static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
+ const char *openssl_ciphers)
{
SSL *ssl = conn->ssl;
@@ -2480,11 +2488,27 @@
else
SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
#endif /* SSL_OP_NO_TLSv1_2 */
+#ifdef SSL_OP_NO_TLSv1_3
+ if (flags & TLS_CONN_DISABLE_TLSv1_3)
+ SSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
+ else
+ SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3);
+#endif /* SSL_OP_NO_TLSv1_3 */
#ifdef CONFIG_SUITEB
+#ifdef OPENSSL_IS_BORINGSSL
+ /* Start with defaults from BoringSSL */
+ SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0);
+#endif /* OPENSSL_IS_BORINGSSL */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
if (flags & TLS_CONN_SUITEB_NO_ECDH) {
const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
+ if (openssl_ciphers) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Override ciphers for Suite B (no ECDH): %s",
+ openssl_ciphers);
+ ciphers = openssl_ciphers;
+ }
if (SSL_set_cipher_list(ssl, ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B ciphers");
@@ -2494,14 +2518,21 @@
EC_KEY *ecdh;
const char *ciphers =
"ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
+ int nid[1] = { NID_secp384r1 };
+ if (openssl_ciphers) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Override ciphers for Suite B: %s",
+ openssl_ciphers);
+ ciphers = openssl_ciphers;
+ }
if (SSL_set_cipher_list(ssl, ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B ciphers");
return -1;
}
- if (SSL_set1_curves_list(ssl, "P-384") != 1) {
+ if (SSL_set1_curves(ssl, nid, 1) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B curves");
return -1;
@@ -2517,12 +2548,23 @@
EC_KEY_free(ecdh);
}
if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+#ifdef OPENSSL_IS_BORINGSSL
+ uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 };
+
+ if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
+ 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B sigalgs");
+ return -1;
+ }
+#else /* OPENSSL_IS_BORINGSSL */
/* ECDSA+SHA384 if need to add EC support here */
if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B sigalgs");
return -1;
}
+#endif /* OPENSSL_IS_BORINGSSL */
SSL_set_options(ssl, SSL_OP_NO_TLSv1);
SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
@@ -2535,6 +2577,26 @@
return -1;
}
#endif /* OPENSSL_VERSION_NUMBER */
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
+ uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 };
+ int nid[1] = { NID_secp384r1 };
+
+ if (SSL_set1_curves(ssl, nid, 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B curves");
+ return -1;
+ }
+
+ if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
+ 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set Suite B sigalgs");
+ return -1;
+ }
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
#endif /* CONFIG_SUITEB */
return 0;
@@ -2561,7 +2623,7 @@
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
}
- if (tls_set_conn_flags(conn, flags) < 0)
+ if (tls_set_conn_flags(conn, flags, NULL) < 0)
return -1;
conn->flags = flags;
@@ -2702,16 +2764,6 @@
}
-static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
-{
- if (password == NULL) {
- return 0;
- }
- os_strlcpy(buf, (char *) password, size);
- return os_strlen(buf);
-}
-
-
#ifdef PKCS12_FUNCS
static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
const char *passwd)
@@ -3030,16 +3082,61 @@
}
-static void tls_clear_default_passwd_cb(SSL_CTX *ssl_ctx, SSL *ssl)
+#ifndef OPENSSL_NO_STDIO
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
{
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
- if (ssl) {
- SSL_set_default_passwd_cb(ssl, NULL);
- SSL_set_default_passwd_cb_userdata(ssl, NULL);
+ if (!password)
+ return 0;
+ os_strlcpy(buf, (const char *) password, size);
+ return os_strlen(buf);
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
+static int tls_use_private_key_file(struct tls_data *data, SSL *ssl,
+ const char *private_key,
+ const char *private_key_passwd)
+{
+#ifndef OPENSSL_NO_STDIO
+ BIO *bio;
+ EVP_PKEY *pkey;
+ int ret;
+
+ /* First try ASN.1 (DER). */
+ bio = BIO_new_file(private_key, "r");
+ if (!bio)
+ return -1;
+ pkey = d2i_PrivateKey_bio(bio, NULL);
+ BIO_free(bio);
+
+ if (pkey) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__);
+ } else {
+ /* Try PEM with the provided password. */
+ bio = BIO_new_file(private_key, "r");
+ if (!bio)
+ return -1;
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb,
+ (void *) private_key_passwd);
+ BIO_free(bio);
+ if (!pkey)
+ return -1;
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__);
+ /* Clear errors from the previous failed load. */
+ ERR_clear_error();
}
-#endif /* >= 1.1.0f && !LibreSSL && !BoringSSL */
- SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
- SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, NULL);
+
+ if (ssl)
+ ret = SSL_use_PrivateKey(ssl, pkey);
+ else
+ ret = SSL_CTX_use_PrivateKey(data->ssl, pkey);
+
+ EVP_PKEY_free(pkey);
+ return ret == 1 ? 0 : -1;
+#else /* OPENSSL_NO_STDIO */
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+ return -1;
+#endif /* OPENSSL_NO_STDIO */
}
@@ -3050,32 +3147,11 @@
const u8 *private_key_blob,
size_t private_key_blob_len)
{
- SSL_CTX *ssl_ctx = data->ssl;
- char *passwd;
int ok;
if (private_key == NULL && private_key_blob == NULL)
return 0;
- if (private_key_passwd) {
- passwd = os_strdup(private_key_passwd);
- if (passwd == NULL)
- return -1;
- } else
- passwd = NULL;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
- /*
- * In OpenSSL >= 1.1.0f SSL_use_PrivateKey_file() uses the callback
- * from the SSL object. See OpenSSL commit d61461a75253.
- */
- SSL_set_default_passwd_cb(conn->ssl, tls_passwd_cb);
- SSL_set_default_passwd_cb_userdata(conn->ssl, passwd);
-#endif /* >= 1.1.0f && !LibreSSL && !BoringSSL */
- /* Keep these for OpenSSL < 1.1.0f */
- SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
- SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
-
ok = 0;
while (private_key_blob) {
if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
@@ -3106,7 +3182,8 @@
}
if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
- private_key_blob_len, passwd) == 0) {
+ private_key_blob_len,
+ private_key_passwd) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
"OK");
ok = 1;
@@ -3117,29 +3194,14 @@
}
while (!ok && private_key) {
-#ifndef OPENSSL_NO_STDIO
- if (SSL_use_PrivateKey_file(conn->ssl, private_key,
- SSL_FILETYPE_ASN1) == 1) {
- wpa_printf(MSG_DEBUG, "OpenSSL: "
- "SSL_use_PrivateKey_File (DER) --> OK");
+ if (tls_use_private_key_file(data, conn->ssl, private_key,
+ private_key_passwd) == 0) {
ok = 1;
break;
}
- if (SSL_use_PrivateKey_file(conn->ssl, private_key,
- SSL_FILETYPE_PEM) == 1) {
- wpa_printf(MSG_DEBUG, "OpenSSL: "
- "SSL_use_PrivateKey_File (PEM) --> OK");
- ok = 1;
- break;
- }
-#else /* OPENSSL_NO_STDIO */
- wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
- __func__);
-#endif /* OPENSSL_NO_STDIO */
-
- if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
- == 0) {
+ if (tls_read_pkcs12(data, conn->ssl, private_key,
+ private_key_passwd) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
"--> OK");
ok = 1;
@@ -3159,13 +3221,9 @@
if (!ok) {
tls_show_errors(MSG_INFO, __func__,
"Failed to load private key");
- tls_clear_default_passwd_cb(ssl_ctx, conn->ssl);
- os_free(passwd);
return -1;
}
ERR_clear_error();
- tls_clear_default_passwd_cb(ssl_ctx, conn->ssl);
- os_free(passwd);
if (!SSL_check_private_key(conn->ssl)) {
tls_show_errors(MSG_INFO, __func__, "Private key failed "
@@ -3183,37 +3241,18 @@
const char *private_key_passwd)
{
SSL_CTX *ssl_ctx = data->ssl;
- char *passwd;
if (private_key == NULL)
return 0;
- if (private_key_passwd) {
- passwd = os_strdup(private_key_passwd);
- if (passwd == NULL)
- return -1;
- } else
- passwd = NULL;
-
- SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
- SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
- if (
-#ifndef OPENSSL_NO_STDIO
- SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
- SSL_FILETYPE_ASN1) != 1 &&
- SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
- SSL_FILETYPE_PEM) != 1 &&
-#endif /* OPENSSL_NO_STDIO */
- tls_read_pkcs12(data, NULL, private_key, passwd)) {
+ if (tls_use_private_key_file(data, NULL, private_key,
+ private_key_passwd) &&
+ tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) {
tls_show_errors(MSG_INFO, __func__,
"Failed to load private key");
- tls_clear_default_passwd_cb(ssl_ctx, NULL);
- os_free(passwd);
ERR_clear_error();
return -1;
}
- tls_clear_default_passwd_cb(ssl_ctx, NULL);
- os_free(passwd);
ERR_clear_error();
if (!SSL_CTX_check_private_key(ssl_ctx)) {
@@ -3401,7 +3440,9 @@
#ifdef OPENSSL_NEED_EAP_FAST_PRF
static int openssl_get_keyblock_size(SSL *ssl)
{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
const EVP_CIPHER *c;
const EVP_MD *h;
int md_size;
@@ -3548,8 +3589,7 @@
static struct wpabuf *
-openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
- int server)
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data)
{
int res;
struct wpabuf *out_data;
@@ -3567,7 +3607,7 @@
}
/* Initiate TLS handshake or continue the existing handshake */
- if (server)
+ if (conn->server)
res = SSL_accept(conn->ssl);
else
res = SSL_connect(conn->ssl);
@@ -3582,11 +3622,25 @@
else {
tls_show_errors(MSG_INFO, __func__, "SSL_connect");
conn->failed++;
+ if (!conn->server && !conn->client_hello_generated) {
+ /* The server would not understand TLS Alert
+ * before ClientHello, so simply terminate
+ * handshake on this type of error case caused
+ * by a likely internal error like no ciphers
+ * available. */
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not generate ClientHello");
+ conn->write_alerts++;
+ return NULL;
+ }
}
}
+ if (!conn->server && !conn->failed)
+ conn->client_hello_generated = 1;
+
#ifdef CONFIG_SUITEB
- if ((conn->flags & TLS_CONN_SUITEB) && !server &&
+ if ((conn->flags & TLS_CONN_SUITEB) && !conn->server &&
os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
conn->server_dh_prime_len < 3072) {
struct tls_context *context = conn->context;
@@ -3689,14 +3743,14 @@
static struct wpabuf *
openssl_connection_handshake(struct tls_connection *conn,
const struct wpabuf *in_data,
- struct wpabuf **appl_data, int server)
+ struct wpabuf **appl_data)
{
struct wpabuf *out_data;
if (appl_data)
*appl_data = NULL;
- out_data = openssl_handshake(conn, in_data, server);
+ out_data = openssl_handshake(conn, in_data);
if (out_data == NULL)
return NULL;
if (conn->invalid_hb_used) {
@@ -3733,7 +3787,7 @@
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
- return openssl_connection_handshake(conn, in_data, appl_data, 0);
+ return openssl_connection_handshake(conn, in_data, appl_data);
}
@@ -3742,7 +3796,8 @@
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
- return openssl_connection_handshake(conn, in_data, appl_data, 1);
+ conn->server = 1;
+ return openssl_connection_handshake(conn, in_data, appl_data);
}
@@ -4283,6 +4338,7 @@
const char *cert_id = params->cert_id;
const char *ca_cert_id = params->ca_cert_id;
const char *engine_id = params->engine ? params->engine_id : NULL;
+ const char *ciphers;
if (conn == NULL)
return -1;
@@ -4335,6 +4391,17 @@
}
}
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+#ifdef SSL_OP_NO_TLSv1_3
+ if (params->flags & TLS_CONN_EAP_FAST) {
+ /* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1
+ * refuses to start the handshake with the modified ciphersuite
+ * list (no TLS v1.3 ciphersuites included) for EAP-FAST. */
+ wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST");
+ SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3);
+ }
+#endif /* SSL_OP_NO_TLSv1_3 */
+#endif
#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
while ((err = ERR_get_error())) {
@@ -4393,15 +4460,26 @@
return -1;
}
- if (params->openssl_ciphers &&
- SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+ ciphers = params->openssl_ciphers;
+#ifdef CONFIG_SUITEB
+#ifdef OPENSSL_IS_BORINGSSL
+ if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) {
+ /* BoringSSL removed support for SUITEB192, so need to handle
+ * this with hardcoded ciphersuite and additional checks for
+ * other parameters. */
+ ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384";
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+#endif /* CONFIG_SUITEB */
+ if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set cipher string '%s'",
- params->openssl_ciphers);
+ ciphers);
return -1;
}
- if (tls_set_conn_flags(conn, params->flags) < 0)
+ if (tls_set_conn_flags(conn, params->flags,
+ params->openssl_ciphers) < 0)
return -1;
#ifdef OPENSSL_IS_BORINGSSL
@@ -4506,7 +4584,9 @@
struct tls_connection *conn = arg;
int ret;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
if (conn == NULL || conn->session_ticket_cb == NULL)
return 0;
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
new file mode 100644
index 0000000..9544e2f
--- /dev/null
+++ b/src/crypto/tls_wolfssl.c
@@ -0,0 +1,2167 @@
+/*
+ * SSL/TLS interface functions for wolfSSL TLS case
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "tls.h"
+
+/* wolfSSL includes */
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/error-ssl.h>
+#include <wolfssl/wolfcrypt/asn.h>
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#define HAVE_AESGCM
+#include <wolfssl/wolfcrypt/aes.h>
+#endif
+
+#if !defined(CONFIG_FIPS) && \
+ (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
+ defined(EAP_SERVER_FAST))
+#define WOLFSSL_NEED_EAP_FAST_PRF
+#endif
+
+#define SECRET_LEN 48
+#define RAN_LEN 32
+#define SESSION_TICKET_LEN 256
+
+static int tls_ref_count = 0;
+
+static int tls_ex_idx_session = 0;
+
+
+/* tls input data for wolfSSL Read Callback */
+struct tls_in_data {
+ const struct wpabuf *in_data;
+ size_t consumed; /* how many bytes have we used already */
+};
+
+/* tls output data for wolfSSL Write Callback */
+struct tls_out_data {
+ struct wpabuf *out_data;
+};
+
+struct tls_context {
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
+ int cert_in_cb;
+ char *ocsp_stapling_response;
+};
+
+static struct tls_context *tls_global = NULL;
+
+/* wolfssl tls_connection */
+struct tls_connection {
+ struct tls_context *context;
+ WOLFSSL *ssl;
+ int read_alerts;
+ int write_alerts;
+ int failed;
+ struct tls_in_data input;
+ struct tls_out_data output;
+ char *subject_match;
+ char *alt_subject_match;
+ char *suffix_match;
+ char *domain_match;
+
+ u8 srv_cert_hash[32];
+
+ unsigned char client_random[RAN_LEN];
+ unsigned char server_random[RAN_LEN];
+ unsigned int flags;
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+ tls_session_ticket_cb session_ticket_cb;
+ void *session_ticket_cb_ctx;
+ byte session_ticket[SESSION_TICKET_LEN];
+#endif
+ unsigned int ca_cert_verify:1;
+ unsigned int cert_probe:1;
+ unsigned int server_cert_only:1;
+ unsigned int success_data:1;
+
+ WOLFSSL_X509 *peer_cert;
+ WOLFSSL_X509 *peer_issuer;
+ WOLFSSL_X509 *peer_issuer_issuer;
+};
+
+
+static struct tls_context * tls_context_new(const struct tls_config *conf)
+{
+ struct tls_context *context = os_zalloc(sizeof(*context));
+
+ if (!context)
+ return NULL;
+
+ if (conf) {
+ context->event_cb = conf->event_cb;
+ context->cb_ctx = conf->cb_ctx;
+ context->cert_in_cb = conf->cert_in_cb;
+ }
+
+ return context;
+}
+
+
+static void wolfssl_reset_in_data(struct tls_in_data *in,
+ const struct wpabuf *buf)
+{
+ /* old one not owned by us so don't free */
+ in->in_data = buf;
+ in->consumed = 0;
+}
+
+
+static void wolfssl_reset_out_data(struct tls_out_data *out)
+{
+ /* old one not owned by us so don't free */
+ out->out_data = wpabuf_alloc_copy("", 0);
+}
+
+
+/* wolfSSL I/O Receive CallBack */
+static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
+{
+ size_t get = sz;
+ struct tls_in_data *data = ctx;
+
+ if (!data)
+ return -1;
+
+ if (get > (wpabuf_len(data->in_data) - data->consumed))
+ get = wpabuf_len(data->in_data) - data->consumed;
+
+ os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get);
+ data->consumed += get;
+
+ if (get == 0)
+ return -2; /* WANT_READ */
+
+ return (int) get;
+}
+
+
+/* wolfSSL I/O Send CallBack */
+static int wolfssl_send_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
+{
+ struct wpabuf *tmp;
+ struct tls_out_data *data = ctx;
+
+ if (!data)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "SSL: adding %d bytes", sz);
+
+ tmp = wpabuf_alloc_copy(buf, sz);
+ if (!tmp)
+ return -1;
+ data->out_data = wpabuf_concat(data->out_data, tmp);
+ if (!data->out_data)
+ return -1;
+
+ return sz;
+}
+
+
+static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess)
+{
+ struct wpabuf *buf;
+
+ buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+ if (!buf)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Free application session data %p (sess %p)",
+ buf, sess);
+ wpabuf_free(buf);
+
+ wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+ WOLFSSL_CTX *ssl_ctx;
+ struct tls_context *context;
+ const char *ciphers;
+
+#ifdef DEBUG_WOLFSSL
+ wolfSSL_Debugging_ON();
+#endif /* DEBUG_WOLFSSL */
+
+ context = tls_context_new(conf);
+ if (!context)
+ return NULL;
+
+ if (tls_ref_count == 0) {
+ tls_global = context;
+
+ if (wolfSSL_Init() < 0)
+ return NULL;
+ /* wolfSSL_Debugging_ON(); */
+ }
+
+ tls_ref_count++;
+
+ /* start as client */
+ ssl_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
+ if (!ssl_ctx) {
+ tls_ref_count--;
+ if (context != tls_global)
+ os_free(context);
+ if (tls_ref_count == 0) {
+ os_free(tls_global);
+ tls_global = NULL;
+ }
+ }
+ wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb);
+ wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb);
+ wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context);
+
+ if (conf->tls_session_lifetime > 0) {
+ wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1);
+ wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
+ SSL_SESS_CACHE_SERVER);
+ wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime);
+ wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb);
+ } else {
+ wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
+ SSL_SESS_CACHE_CLIENT);
+ }
+
+ if (conf && conf->openssl_ciphers)
+ ciphers = conf->openssl_ciphers;
+ else
+ ciphers = "ALL";
+ if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
+ wpa_printf(MSG_ERROR,
+ "wolfSSL: Failed to set cipher string '%s'",
+ ciphers);
+ tls_deinit(ssl_ctx);
+ return NULL;
+ }
+
+ return ssl_ctx;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+ struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+
+ if (context != tls_global)
+ os_free(context);
+
+ wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx);
+
+ tls_ref_count--;
+ if (tls_ref_count == 0) {
+ wolfSSL_Cleanup();
+ os_free(tls_global);
+ tls_global = NULL;
+ }
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+#ifdef DEBUG_WOLFSSL
+#if 0
+ unsigned long err;
+
+ err = wolfSSL_ERR_peek_last_error_line(NULL, NULL);
+ if (err != 0) {
+ wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+ wolfSSL_ERR_error_string(err, NULL));
+ return 1;
+ }
+#endif
+#endif /* DEBUG_WOLFSSL */
+ return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+ WOLFSSL_CTX *ssl_ctx = tls_ctx;
+ struct tls_connection *conn;
+
+ wpa_printf(MSG_DEBUG, "SSL: connection init");
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ return NULL;
+ conn->ssl = wolfSSL_new(ssl_ctx);
+ if (!conn->ssl) {
+ os_free(conn);
+ return NULL;
+ }
+
+ wolfSSL_SetIOReadCtx(conn->ssl, &conn->input);
+ wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output);
+ wolfSSL_set_ex_data(conn->ssl, 0, conn);
+ conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+
+ /* Need randoms post-hanshake for EAP-FAST, export key and deriving
+ * session ID in EAP methods. */
+ wolfSSL_KeepArrays(conn->ssl);
+ wolfSSL_KeepHandshakeResources(conn->ssl);
+ wolfSSL_UseClientSuites(conn->ssl);
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+ if (!conn)
+ return;
+
+ wpa_printf(MSG_DEBUG, "SSL: connection deinit");
+
+ /* parts */
+ wolfSSL_free(conn->ssl);
+ os_free(conn->subject_match);
+ os_free(conn->alt_subject_match);
+ os_free(conn->suffix_match);
+ os_free(conn->domain_match);
+
+ /* self */
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+ return conn ? wolfSSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+ WOLFSSL_SESSION *session;
+
+ if (!conn)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "SSL: connection shutdown");
+
+ /* Set quiet as OpenSSL does */
+ wolfSSL_set_quiet_shutdown(conn->ssl, 1);
+ wolfSSL_shutdown(conn->ssl);
+
+ session = wolfSSL_get_session(conn->ssl);
+ if (wolfSSL_clear(conn->ssl) != 1)
+ return -1;
+ wolfSSL_set_session(conn->ssl, session);
+
+ return 0;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_connection *conn,
+ const char *subject_match,
+ const char *alt_subject_match,
+ const char *suffix_match,
+ const char *domain_match)
+{
+ os_free(conn->subject_match);
+ conn->subject_match = NULL;
+ if (subject_match) {
+ conn->subject_match = os_strdup(subject_match);
+ if (!conn->subject_match)
+ return -1;
+ }
+
+ os_free(conn->alt_subject_match);
+ conn->alt_subject_match = NULL;
+ if (alt_subject_match) {
+ conn->alt_subject_match = os_strdup(alt_subject_match);
+ if (!conn->alt_subject_match)
+ return -1;
+ }
+
+ os_free(conn->suffix_match);
+ conn->suffix_match = NULL;
+ if (suffix_match) {
+ conn->suffix_match = os_strdup(suffix_match);
+ if (!conn->suffix_match)
+ return -1;
+ }
+
+ os_free(conn->domain_match);
+ conn->domain_match = NULL;
+ if (domain_match) {
+ conn->domain_match = os_strdup(domain_match);
+ if (!conn->domain_match)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int tls_connection_dh(struct tls_connection *conn, const char *dh_file,
+ const u8 *dh_blob, size_t blob_len)
+{
+ if (!dh_file && !dh_blob)
+ return 0;
+
+ wolfSSL_set_accept_state(conn->ssl);
+
+ if (dh_blob) {
+ if (wolfSSL_SetTmpDH_buffer(conn->ssl, dh_blob, blob_len,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO, "SSL: use DH DER blob failed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use DH blob OK");
+ return 0;
+ }
+
+ if (dh_file) {
+ wpa_printf(MSG_INFO, "SSL: use DH PEM file: %s", dh_file);
+ if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
+ SSL_FILETYPE_PEM) < 0) {
+ wpa_printf(MSG_INFO, "SSL: use DH PEM file failed");
+ if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use DH DER file failed");
+ return -1;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use DH file OK");
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+ const char *client_cert,
+ const u8 *client_cert_blob,
+ size_t blob_len)
+{
+ if (!client_cert && !client_cert_blob)
+ return 0;
+
+ if (client_cert_blob) {
+ if (wolfSSL_use_certificate_chain_buffer_format(
+ conn->ssl, client_cert_blob, blob_len,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use client cert DER blob failed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK");
+ return 0;
+ }
+
+ if (client_cert) {
+ if (wolfSSL_use_certificate_chain_file(conn->ssl,
+ client_cert) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use client cert PEM file failed");
+ if (wolfSSL_use_certificate_chain_file_format(
+ conn->ssl, client_cert,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use client cert DER file failed");
+ return -1;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use client cert file OK");
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+ if (!password)
+ return 0;
+ os_strlcpy(buf, (char *) password, size);
+ return os_strlen(buf);
+}
+
+
+static int tls_connection_private_key(void *tls_ctx,
+ struct tls_connection *conn,
+ const char *private_key,
+ const char *private_key_passwd,
+ const u8 *private_key_blob,
+ size_t blob_len)
+{
+ WOLFSSL_CTX *ctx = tls_ctx;
+ char *passwd = NULL;
+ int ok = 0;
+
+ if (!private_key && !private_key_blob)
+ return 0;
+
+ if (private_key_passwd) {
+ passwd = os_strdup(private_key_passwd);
+ if (!passwd)
+ return -1;
+ }
+
+ wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
+ wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
+
+ if (private_key_blob) {
+ if (wolfSSL_use_PrivateKey_buffer(conn->ssl,
+ private_key_blob, blob_len,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use private DER blob failed");
+ } else {
+ wpa_printf(MSG_DEBUG, "SSL: use private key blob OK");
+ ok = 1;
+ }
+ }
+
+ if (!ok && private_key) {
+ if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
+ SSL_FILETYPE_PEM) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: use private key PEM file failed");
+ if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
+ SSL_FILETYPE_ASN1) < 0)
+ {
+ wpa_printf(MSG_INFO,
+ "SSL: use private key DER file failed");
+ } else {
+ ok = 1;
+ }
+ } else {
+ ok = 1;
+ }
+
+ if (ok)
+ wpa_printf(MSG_DEBUG, "SSL: use private key file OK");
+ }
+
+ wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
+ os_free(passwd);
+
+ if (!ok)
+ return -1;
+
+ return 0;
+}
+
+
+static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type,
+ const char *value, size_t len)
+{
+ WOLFSSL_ASN1_OBJECT *gen;
+ void *ext;
+ int found = 0;
+ int i;
+
+ ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
+
+ for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
+ gen = wolfSSL_sk_value(ext, i);
+ if (gen->type != type)
+ continue;
+ if (os_strlen((char *) gen->obj) == len &&
+ os_memcmp(value, gen->obj, len) == 0)
+ found++;
+ }
+
+ wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+ return found;
+}
+
+
+static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match)
+{
+ int type;
+ const char *pos, *end;
+ size_t len;
+
+ pos = match;
+ do {
+ if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+ type = GEN_EMAIL;
+ pos += 6;
+ } else if (os_strncmp(pos, "DNS:", 4) == 0) {
+ type = GEN_DNS;
+ pos += 4;
+ } else if (os_strncmp(pos, "URI:", 4) == 0) {
+ type = GEN_URI;
+ pos += 4;
+ } else {
+ wpa_printf(MSG_INFO,
+ "TLS: Invalid altSubjectName match '%s'",
+ pos);
+ return 0;
+ }
+ end = os_strchr(pos, ';');
+ while (end) {
+ if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+ os_strncmp(end + 1, "DNS:", 4) == 0 ||
+ os_strncmp(end + 1, "URI:", 4) == 0)
+ break;
+ end = os_strchr(end + 1, ';');
+ }
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (tls_match_alt_subject_component(cert, type, pos, len) > 0)
+ return 1;
+ pos = end + 1;
+ } while (end);
+
+ return 0;
+}
+
+
+static int domain_suffix_match(const char *val, size_t len, const char *match,
+ int full)
+{
+ size_t i, match_len;
+
+ /* Check for embedded nuls that could mess up suffix matching */
+ for (i = 0; i < len; i++) {
+ if (val[i] == '\0') {
+ wpa_printf(MSG_DEBUG,
+ "TLS: Embedded null in a string - reject");
+ return 0;
+ }
+ }
+
+ match_len = os_strlen(match);
+ if (match_len > len || (full && match_len != len))
+ return 0;
+
+ if (os_strncasecmp(val + len - match_len, match, match_len) != 0)
+ return 0; /* no match */
+
+ if (match_len == len)
+ return 1; /* exact match */
+
+ if (val[len - match_len - 1] == '.')
+ return 1; /* full label match completes suffix match */
+
+ wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
+ return 0;
+}
+
+
+static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
+{
+ WOLFSSL_ASN1_OBJECT *gen;
+ void *ext;
+ int i;
+ int j;
+ int dns_name = 0;
+ WOLFSSL_X509_NAME *name;
+
+ wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+ full ? "" : "suffix ", match);
+
+ ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
+
+ for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
+ gen = wolfSSL_sk_value(ext, j);
+ if (gen->type != ALT_NAMES_OID)
+ continue;
+ dns_name++;
+ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
+ gen->obj, os_strlen((char *)gen->obj));
+ if (domain_suffix_match((const char *) gen->obj,
+ os_strlen((char *) gen->obj), match,
+ full) == 1) {
+ wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+ full ? "Match" : "Suffix match");
+ wolfSSL_sk_ASN1_OBJECT_free(ext);
+ return 1;
+ }
+ }
+ wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+ if (dns_name) {
+ wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+ return 0;
+ }
+
+ name = wolfSSL_X509_get_subject_name(cert);
+ i = -1;
+ for (;;) {
+ WOLFSSL_X509_NAME_ENTRY *e;
+ WOLFSSL_ASN1_STRING *cn;
+
+ i = wolfSSL_X509_NAME_get_index_by_NID(name, ASN_COMMON_NAME,
+ i);
+ if (i == -1)
+ break;
+ e = wolfSSL_X509_NAME_get_entry(name, i);
+ if (!e)
+ continue;
+ cn = wolfSSL_X509_NAME_ENTRY_get_data(e);
+ if (!cn)
+ continue;
+ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+ cn->data, cn->length);
+ if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
+ {
+ wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+ full ? "Match" : "Suffix match");
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+ full ? "" : "suffix ");
+ return 0;
+}
+
+
+static enum tls_fail_reason wolfssl_tls_fail_reason(int err)
+{
+ switch (err) {
+ case X509_V_ERR_CERT_REVOKED:
+ return TLS_FAIL_REVOKED;
+ case ASN_BEFORE_DATE_E:
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ return TLS_FAIL_NOT_YET_VALID;
+ case ASN_AFTER_DATE_E:
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ return TLS_FAIL_EXPIRED;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ case X509_V_ERR_INVALID_CA:
+ return TLS_FAIL_UNTRUSTED;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_CERT_REJECTED:
+ return TLS_FAIL_BAD_CERTIFICATE;
+ default:
+ return TLS_FAIL_UNSPECIFIED;
+ }
+}
+
+
+static const char * wolfssl_tls_err_string(int err, const char *err_str)
+{
+ switch (err) {
+ case ASN_BEFORE_DATE_E:
+ return "certificate is not yet valid";
+ case ASN_AFTER_DATE_E:
+ return "certificate has expired";
+ default:
+ return err_str;
+ }
+}
+
+
+static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert)
+{
+ struct wpabuf *buf = NULL;
+ const u8 *data;
+ int cert_len;
+
+ data = wolfSSL_X509_get_der(cert, &cert_len);
+ if (!data)
+ buf = wpabuf_alloc_copy(data, cert_len);
+
+ return buf;
+}
+
+
+static void wolfssl_tls_fail_event(struct tls_connection *conn,
+ WOLFSSL_X509 *err_cert, int err, int depth,
+ const char *subject, const char *err_str,
+ enum tls_fail_reason reason)
+{
+ union tls_event_data ev;
+ struct wpabuf *cert = NULL;
+ struct tls_context *context = conn->context;
+
+ if (!context->event_cb)
+ return;
+
+ cert = get_x509_cert(err_cert);
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+ reason : wolfssl_tls_fail_reason(err);
+ ev.cert_fail.depth = depth;
+ ev.cert_fail.subject = subject;
+ ev.cert_fail.reason_txt = wolfssl_tls_err_string(err, err_str);
+ ev.cert_fail.cert = cert;
+ context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+ wpabuf_free(cert);
+}
+
+
+static void wolfssl_tls_cert_event(struct tls_connection *conn,
+ WOLFSSL_X509 *err_cert, int depth,
+ const char *subject)
+{
+ struct wpabuf *cert = NULL;
+ union tls_event_data ev;
+ struct tls_context *context = conn->context;
+ char *alt_subject[TLS_MAX_ALT_SUBJECT];
+ int alt, num_alt_subject = 0;
+ WOLFSSL_ASN1_OBJECT *gen;
+ void *ext;
+ int i;
+#ifdef CONFIG_SHA256
+ u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+ if (!context->event_cb)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
+ if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
+ context->cert_in_cb) {
+ cert = get_x509_cert(err_cert);
+ ev.peer_cert.cert = cert;
+ }
+
+#ifdef CONFIG_SHA256
+ if (cert) {
+ const u8 *addr[1];
+ size_t len[1];
+
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) == 0) {
+ ev.peer_cert.hash = hash;
+ ev.peer_cert.hash_len = sizeof(hash);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+
+ ev.peer_cert.depth = depth;
+ ev.peer_cert.subject = subject;
+
+ ext = wolfSSL_X509_get_ext_d2i(err_cert, ALT_NAMES_OID, NULL, NULL);
+ for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
+ char *pos;
+
+ if (num_alt_subject == TLS_MAX_ALT_SUBJECT)
+ break;
+ gen = wolfSSL_sk_value((void *) ext, i);
+ if (gen->type != GEN_EMAIL &&
+ gen->type != GEN_DNS &&
+ gen->type != GEN_URI)
+ continue;
+
+ pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1);
+ if (!pos)
+ break;
+ alt_subject[num_alt_subject++] = pos;
+
+ switch (gen->type) {
+ case GEN_EMAIL:
+ os_memcpy(pos, "EMAIL:", 6);
+ pos += 6;
+ break;
+ case GEN_DNS:
+ os_memcpy(pos, "DNS:", 4);
+ pos += 4;
+ break;
+ case GEN_URI:
+ os_memcpy(pos, "URI:", 4);
+ pos += 4;
+ break;
+ }
+
+ os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj));
+ pos += os_strlen((char *)gen->obj);
+ *pos = '\0';
+ }
+ wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+ for (alt = 0; alt < num_alt_subject; alt++)
+ ev.peer_cert.altsubject[alt] = alt_subject[alt];
+ ev.peer_cert.num_altsubject = num_alt_subject;
+
+ context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+ wpabuf_free(cert);
+ for (alt = 0; alt < num_alt_subject; alt++)
+ os_free(alt_subject[alt]);
+}
+
+
+static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx)
+{
+ char buf[256];
+ WOLFSSL_X509 *err_cert;
+ int err, depth;
+ WOLFSSL *ssl;
+ struct tls_connection *conn;
+ struct tls_context *context;
+ char *match, *altmatch, *suffix_match, *domain_match;
+ const char *err_str;
+
+ err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
+ if (!err_cert) {
+ wpa_printf(MSG_DEBUG, "wolfSSL: No Cert");
+ return 0;
+ }
+
+ err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx);
+ depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx);
+ ssl = wolfSSL_X509_STORE_CTX_get_ex_data(
+ x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx());
+ wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf,
+ sizeof(buf));
+
+ conn = wolfSSL_get_ex_data(ssl, 0);
+ if (!conn) {
+ wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data");
+ return 0;
+ }
+
+ if (depth == 0)
+ conn->peer_cert = err_cert;
+ else if (depth == 1)
+ conn->peer_issuer = err_cert;
+ else if (depth == 2)
+ conn->peer_issuer_issuer = err_cert;
+
+ context = conn->context;
+ match = conn->subject_match;
+ altmatch = conn->alt_subject_match;
+ suffix_match = conn->suffix_match;
+ domain_match = conn->domain_match;
+
+ if (!preverify_ok && !conn->ca_cert_verify)
+ preverify_ok = 1;
+ if (!preverify_ok && depth > 0 && conn->server_cert_only)
+ preverify_ok = 1;
+ if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
+ (err == X509_V_ERR_CERT_HAS_EXPIRED ||
+ err == ASN_AFTER_DATE_E || err == ASN_BEFORE_DATE_E ||
+ err == X509_V_ERR_CERT_NOT_YET_VALID)) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Ignore certificate validity time mismatch");
+ preverify_ok = 1;
+ }
+
+ err_str = wolfSSL_X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+ /*
+ * Do not require preverify_ok so we can explicity allow otherwise
+ * invalid pinned server certificates.
+ */
+ if (depth == 0 && conn->server_cert_only) {
+ struct wpabuf *cert;
+
+ cert = get_x509_cert(err_cert);
+ if (!cert) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Could not fetch server certificate data");
+ preverify_ok = 0;
+ } else {
+ u8 hash[32];
+ const u8 *addr[1];
+ size_t len[1];
+
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) < 0 ||
+ os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+ err_str = "Server certificate mismatch";
+ err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+ preverify_ok = 0;
+ } else if (!preverify_ok) {
+ /*
+ * Certificate matches pinned certificate, allow
+ * regardless of other problems.
+ */
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Ignore validation issues for a pinned server certificate");
+ preverify_ok = 1;
+ }
+ wpabuf_free(cert);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+
+ if (!preverify_ok) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'",
+ err, err_str, depth, buf);
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ err_str, TLS_FAIL_UNSPECIFIED);
+ return preverify_ok;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+ __func__, preverify_ok, err, err_str,
+ conn->ca_cert_verify, depth, buf);
+ if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Subject '%s' did not match with '%s'",
+ buf, match);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Subject mismatch",
+ TLS_FAIL_SUBJECT_MISMATCH);
+ } else if (depth == 0 && altmatch &&
+ !tls_match_alt_subject(err_cert, altmatch)) {
+ wpa_printf(MSG_WARNING,
+ "TLS: altSubjectName match '%s' not found",
+ altmatch);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "AltSubject mismatch",
+ TLS_FAIL_ALTSUBJECT_MISMATCH);
+ } else if (depth == 0 && suffix_match &&
+ !tls_match_suffix(err_cert, suffix_match, 0)) {
+ wpa_printf(MSG_WARNING,
+ "TLS: Domain suffix match '%s' not found",
+ suffix_match);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Domain suffix mismatch",
+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+ } else if (depth == 0 && domain_match &&
+ !tls_match_suffix(err_cert, domain_match, 1)) {
+ wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+ domain_match);
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Domain mismatch",
+ TLS_FAIL_DOMAIN_MISMATCH);
+ } else {
+ wolfssl_tls_cert_event(conn, err_cert, depth, buf);
+ }
+
+ if (conn->cert_probe && preverify_ok && depth == 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Reject server certificate on probe-only run");
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Server certificate chain probe",
+ TLS_FAIL_SERVER_CHAIN_PROBE);
+ }
+
+#ifdef HAVE_OCSP_WOLFSSL
+ if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+ preverify_ok) {
+ enum ocsp_result res;
+
+ res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+ conn->peer_issuer,
+ conn->peer_issuer_issuer);
+ if (res == OCSP_REVOKED) {
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "certificate revoked",
+ TLS_FAIL_REVOKED);
+ if (err == X509_V_OK)
+ X509_STORE_CTX_set_error(
+ x509_ctx, X509_V_ERR_CERT_REVOKED);
+ } else if (res != OCSP_GOOD &&
+ (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
+ preverify_ok = 0;
+ wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "bad certificate status response",
+ TLS_FAIL_UNSPECIFIED);
+ }
+ }
+#endif /* HAVE_OCSP_WOLFSSL */
+ if (depth == 0 && preverify_ok && context->event_cb != NULL)
+ context->event_cb(context->cb_ctx,
+ TLS_CERT_CHAIN_SUCCESS, NULL);
+
+ return preverify_ok;
+}
+
+
+static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn,
+ const char *ca_cert,
+ const u8 *ca_cert_blob, size_t blob_len,
+ const char *ca_path)
+{
+ WOLFSSL_CTX *ctx = tls_ctx;
+
+ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ conn->ca_cert_verify = 1;
+
+ if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Probe for server certificate chain");
+ conn->cert_probe = 1;
+ conn->ca_cert_verify = 0;
+ return 0;
+ }
+
+ if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+ const char *pos = ca_cert + 7;
+
+ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Unsupported ca_cert hash value '%s'",
+ ca_cert);
+ return -1;
+ }
+ pos += 14;
+ if (os_strlen(pos) != 32 * 2) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Unexpected SHA256 hash length in ca_cert '%s'",
+ ca_cert);
+ return -1;
+ }
+ if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Invalid SHA256 hash value in ca_cert '%s'",
+ ca_cert);
+ return -1;
+ }
+ conn->server_cert_only = 1;
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Checking only server certificate match");
+ return 0;
+#else /* CONFIG_SHA256 */
+ wpa_printf(MSG_INFO,
+ "No SHA256 included in the build - cannot validate server certificate hash");
+ return -1;
+#endif /* CONFIG_SHA256 */
+ }
+
+ if (ca_cert_blob) {
+ if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len,
+ SSL_FILETYPE_ASN1) !=
+ SSL_SUCCESS) {
+ wpa_printf(MSG_INFO, "SSL: failed to load CA blob");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK");
+ return 0;
+ }
+
+ if (ca_cert || ca_path) {
+ WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new();
+
+ if (!cm) {
+ wpa_printf(MSG_INFO,
+ "SSL: failed to create certificate store");
+ return -1;
+ }
+ wolfSSL_CTX_set_cert_store(ctx, cm);
+
+ if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) !=
+ SSL_SUCCESS) {
+ wpa_printf(MSG_INFO,
+ "SSL: failed to load ca_cert as PEM");
+
+ if (!ca_cert)
+ return -1;
+
+ if (wolfSSL_CTX_der_load_verify_locations(
+ ctx, ca_cert, SSL_FILETYPE_ASN1) !=
+ SSL_SUCCESS) {
+ wpa_printf(MSG_INFO,
+ "SSL: failed to load ca_cert as DER");
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ conn->ca_cert_verify = 0;
+ return 0;
+}
+
+
+static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags)
+{
+#ifdef HAVE_SESSION_TICKET
+#if 0
+ if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET))
+ wolfSSL_UseSessionTicket(ssl);
+#endif
+#endif /* HAVE_SESSION_TICKET */
+
+ if (flags & TLS_CONN_DISABLE_TLSv1_0)
+ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1);
+ if (flags & TLS_CONN_DISABLE_TLSv1_1)
+ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+ if (flags & TLS_CONN_DISABLE_TLSv1_2)
+ wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+ wpa_printf(MSG_DEBUG, "SSL: set params");
+
+ if (tls_connection_set_subject_match(conn, params->subject_match,
+ params->altsubject_match,
+ params->suffix_match,
+ params->domain_match) < 0) {
+ wpa_printf(MSG_INFO, "Error setting subject match");
+ return -1;
+ }
+
+ if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
+ params->ca_cert_blob,
+ params->ca_cert_blob_len,
+ params->ca_path) < 0) {
+ wpa_printf(MSG_INFO, "Error setting CA cert");
+ return -1;
+ }
+
+ if (tls_connection_client_cert(conn, params->client_cert,
+ params->client_cert_blob,
+ params->client_cert_blob_len) < 0) {
+ wpa_printf(MSG_INFO, "Error setting client cert");
+ return -1;
+ }
+
+ if (tls_connection_private_key(tls_ctx, conn, params->private_key,
+ params->private_key_passwd,
+ params->private_key_blob,
+ params->private_key_blob_len) < 0) {
+ wpa_printf(MSG_INFO, "Error setting private key");
+ return -1;
+ }
+
+ if (tls_connection_dh(conn, params->dh_file, params->dh_blob,
+ params->dh_blob_len) < 0) {
+ wpa_printf(MSG_INFO, "Error setting DH");
+ return -1;
+ }
+
+ if (params->openssl_ciphers &&
+ wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "wolfSSL: Failed to set cipher string '%s'",
+ params->openssl_ciphers);
+ return -1;
+ }
+
+ tls_set_conn_flags(conn->ssl, params->flags);
+
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ if (wolfSSL_UseOCSPStapling(conn->ssl, WOLFSSL_CSR_OCSP,
+ WOLFSSL_CSR_OCSP_USE_NONCE) !=
+ SSL_SUCCESS)
+ return -1;
+ wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
+ }
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ if (wolfSSL_UseOCSPStaplingV2(conn->ssl,
+ WOLFSSL_CSR2_OCSP_MULTI, 0) !=
+ SSL_SUCCESS)
+ return -1;
+ wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
+ }
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
+ !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
+#ifdef HAVE_OCSP
+ if (params->flags & TLS_CONN_REQUEST_OCSP)
+ wolfSSL_CTX_EnableOCSP(ctx, 0);
+#else /* HAVE_OCSP */
+ if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+ wpa_printf(MSG_INFO,
+ "wolfSSL: No OCSP support included - reject configuration");
+ return -1;
+ }
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: No OCSP support included - allow optional OCSP case to continue");
+ }
+#endif /* HAVE_OCSP */
+#endif /* !HAVE_CERTIFICATE_STATUS_REQUEST &&
+ * !HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+
+ conn->flags = params->flags;
+
+ return 0;
+}
+
+
+static int tls_global_ca_cert(void *ssl_ctx, const char *ca_cert)
+{
+ WOLFSSL_CTX *ctx = ssl_ctx;
+
+ if (ca_cert) {
+ if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL) != 1)
+ {
+ wpa_printf(MSG_WARNING,
+ "Failed to load root certificates");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "TLS: Trusted root certificate(s) loaded");
+ }
+
+ return 0;
+}
+
+
+static int tls_global_client_cert(void *ssl_ctx, const char *client_cert)
+{
+ WOLFSSL_CTX *ctx = ssl_ctx;
+
+ if (!client_cert)
+ return 0;
+
+ if (wolfSSL_CTX_use_certificate_chain_file_format(ctx, client_cert,
+ SSL_FILETYPE_ASN1) !=
+ SSL_SUCCESS &&
+ wolfSSL_CTX_use_certificate_chain_file(ctx, client_cert) !=
+ SSL_SUCCESS) {
+ wpa_printf(MSG_INFO, "Failed to load client certificate");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Loaded global client certificate: %s",
+ client_cert);
+
+ return 0;
+}
+
+
+static int tls_global_private_key(void *ssl_ctx, const char *private_key,
+ const char *private_key_passwd)
+{
+ WOLFSSL_CTX *ctx = ssl_ctx;
+ char *passwd = NULL;
+ int ret = 0;
+
+ if (!private_key)
+ return 0;
+
+ if (private_key_passwd) {
+ passwd = os_strdup(private_key_passwd);
+ if (!passwd)
+ return -1;
+ }
+
+ wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
+ wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
+
+ if (wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
+ SSL_FILETYPE_ASN1) != 1 &&
+ wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
+ SSL_FILETYPE_PEM) != 1) {
+ wpa_printf(MSG_INFO, "Failed to load private key");
+ ret = -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Loaded global private key");
+
+ os_free(passwd);
+ wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
+
+ return ret;
+}
+
+
+static int tls_global_dh(void *ssl_ctx, const char *dh_file,
+ const u8 *dh_blob, size_t blob_len)
+{
+ WOLFSSL_CTX *ctx = ssl_ctx;
+
+ if (!dh_file && !dh_blob)
+ return 0;
+
+ if (dh_blob) {
+ if (wolfSSL_CTX_SetTmpDH_buffer(ctx, dh_blob, blob_len,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: global use DH DER blob failed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: global use DH blob OK");
+ return 0;
+ }
+
+ if (dh_file) {
+ if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) <
+ 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: global use DH PEM file failed");
+ if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file,
+ SSL_FILETYPE_ASN1) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: global use DH DER file failed");
+ return -1;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "SSL: global use DH file OK");
+ return 0;
+ }
+
+ return 0;
+}
+
+
+#ifdef HAVE_OCSP
+
+int ocsp_status_cb(void *unused, const char *url, int url_sz,
+ unsigned char *request, int request_sz,
+ unsigned char **response)
+{
+ size_t len;
+
+ (void) unused;
+
+ if (!url) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: OCSP status callback - no response configured");
+ *response = NULL;
+ return 0;
+ }
+
+ *response = (unsigned char *) os_readfile(url, &len);
+ if (!*response) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: OCSP status callback - could not read response file");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: OCSP status callback - send cached response");
+ return len;
+}
+
+
+void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response)
+{
+ os_free(response);
+}
+
+#endif /* HAVE_OCSP */
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+ wpa_printf(MSG_DEBUG, "SSL: global set params");
+
+ if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
+ wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
+ params->ca_cert);
+ return -1;
+ }
+
+ if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: Failed to load client cert file '%s'",
+ params->client_cert);
+ return -1;
+ }
+
+ if (tls_global_private_key(tls_ctx, params->private_key,
+ params->private_key_passwd) < 0) {
+ wpa_printf(MSG_INFO,
+ "SSL: Failed to load private key file '%s'",
+ params->private_key);
+ return -1;
+ }
+
+ if (tls_global_dh(tls_ctx, params->dh_file, params->dh_blob,
+ params->dh_blob_len) < 0) {
+ wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'",
+ params->dh_file);
+ return -1;
+ }
+
+ if (params->openssl_ciphers &&
+ wolfSSL_CTX_set_cipher_list(tls_ctx,
+ params->openssl_ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "wolfSSL: Failed to set cipher string '%s'",
+ params->openssl_ciphers);
+ return -1;
+ }
+
+#ifdef HAVE_SESSION_TICKET
+ /* Session ticket is off by default - can't disable once on. */
+ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
+ wolfSSL_CTX_UseSessionTicket(tls_ctx);
+#endif /* HAVE_SESSION_TICKET */
+
+#ifdef HAVE_OCSP
+ if (params->ocsp_stapling_response) {
+ wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx,
+ params->ocsp_stapling_response);
+ wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb,
+ ocsp_resp_free_cb, NULL);
+ }
+#endif /* HAVE_OCSP */
+
+ return 0;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+ wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl);
+
+ if (check_crl) {
+ /* Hack to Enable CRLs. */
+ wolfSSL_CTX_LoadCRLBuffer(tls_ctx, NULL, 0, SSL_FILETYPE_PEM);
+ }
+
+ return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+ int verify_peer, unsigned int flags,
+ const u8 *session_ctx, size_t session_ctx_len)
+{
+ if (!conn)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer);
+
+ if (verify_peer) {
+ conn->ca_cert_verify = 1;
+ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ tls_verify_cb);
+ } else {
+ conn->ca_cert_verify = 0;
+ wolfSSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+ }
+
+ wolfSSL_set_accept_state(conn->ssl);
+
+ /* TODO: do we need to fake a session like OpenSSL does here? */
+
+ return 0;
+}
+
+
+static struct wpabuf * wolfssl_handshake(struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ int server)
+{
+ int res;
+
+ wolfssl_reset_out_data(&conn->output);
+
+ /* Initiate TLS handshake or continue the existing handshake */
+ if (server) {
+ wolfSSL_set_accept_state(conn->ssl);
+ res = wolfSSL_accept(conn->ssl);
+ wpa_printf(MSG_DEBUG, "SSL: wolfSSL_accept: %d", res);
+ } else {
+ wolfSSL_set_connect_state(conn->ssl);
+ res = wolfSSL_connect(conn->ssl);
+ wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res);
+ }
+
+ if (res != 1) {
+ int err = wolfSSL_get_error(conn->ssl, res);
+
+ if (err == SSL_ERROR_WANT_READ) {
+ wpa_printf(MSG_DEBUG,
+ "SSL: wolfSSL_connect - want more data");
+ } else if (err == SSL_ERROR_WANT_WRITE) {
+ wpa_printf(MSG_DEBUG,
+ "SSL: wolfSSL_connect - want to write");
+ } else {
+ char msg[80];
+
+ wpa_printf(MSG_DEBUG,
+ "SSL: wolfSSL_connect - failed %s",
+ wolfSSL_ERR_error_string(err, msg));
+ conn->failed++;
+ }
+ }
+
+ return conn->output.out_data;
+}
+
+
+static struct wpabuf * wolfssl_get_appl_data(struct tls_connection *conn,
+ size_t max_len)
+{
+ int res;
+ struct wpabuf *appl_data = wpabuf_alloc(max_len + 100);
+
+ if (!appl_data)
+ return NULL;
+
+ res = wolfSSL_read(conn->ssl, wpabuf_mhead(appl_data),
+ wpabuf_size(appl_data));
+ if (res < 0) {
+ int err = wolfSSL_get_error(conn->ssl, res);
+
+ if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+ wpa_printf(MSG_DEBUG,
+ "SSL: No Application Data included");
+ } else {
+ char msg[80];
+
+ wpa_printf(MSG_DEBUG,
+ "Failed to read possible Application Data %s",
+ wolfSSL_ERR_error_string(err, msg));
+ }
+
+ wpabuf_free(appl_data);
+ return NULL;
+ }
+
+ wpabuf_put(appl_data, res);
+ wpa_hexdump_buf_key(MSG_MSGDUMP,
+ "SSL: Application Data in Finished message",
+ appl_data);
+ return appl_data;
+}
+
+
+static struct wpabuf *
+wolfssl_connection_handshake(struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data, int server)
+{
+ struct wpabuf *out_data;
+
+ wolfssl_reset_in_data(&conn->input, in_data);
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ out_data = wolfssl_handshake(conn, in_data, server);
+ if (!out_data)
+ return NULL;
+
+ if (wolfSSL_is_init_finished(conn->ssl)) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Handshake finished - resumed=%d",
+ tls_connection_resumed(NULL, conn));
+ if (appl_data && in_data)
+ *appl_data = wolfssl_get_appl_data(conn,
+ wpabuf_len(in_data));
+ }
+
+ return out_data;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return wolfssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return wolfssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ int res;
+
+ if (!conn)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data));
+
+ wolfssl_reset_out_data(&conn->output);
+
+ res = wolfSSL_write(conn->ssl, wpabuf_head(in_data),
+ wpabuf_len(in_data));
+ if (res < 0) {
+ int err = wolfSSL_get_error(conn->ssl, res);
+ char msg[80];
+
+ wpa_printf(MSG_INFO, "Encryption failed - SSL_write: %s",
+ wolfSSL_ERR_error_string(err, msg));
+ return NULL;
+ }
+
+ return conn->output.out_data;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ int res;
+ struct wpabuf *buf;
+
+ if (!conn)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "SSL: decrypt");
+
+ wolfssl_reset_in_data(&conn->input, in_data);
+
+ /* Read decrypted data for further processing */
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (!buf)
+ return NULL;
+ res = wolfSSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "Decryption failed - SSL_read");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+
+ wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf));
+
+ return buf;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+ return conn ? wolfSSL_session_reused(conn->ssl) : 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+ char buf[128], *pos, *end;
+ u8 *c;
+ int ret;
+
+ if (!conn || !conn->ssl || !ciphers)
+ return -1;
+
+ buf[0] = '\0';
+ pos = buf;
+ end = pos + sizeof(buf);
+
+ c = ciphers;
+ while (*c != TLS_CIPHER_NONE) {
+ const char *suite;
+
+ switch (*c) {
+ case TLS_CIPHER_RC4_SHA:
+ suite = "RC4-SHA";
+ break;
+ case TLS_CIPHER_AES128_SHA:
+ suite = "AES128-SHA";
+ break;
+ case TLS_CIPHER_RSA_DHE_AES128_SHA:
+ suite = "DHE-RSA-AES128-SHA";
+ break;
+ case TLS_CIPHER_ANON_DH_AES128_SHA:
+ suite = "ADH-AES128-SHA";
+ break;
+ case TLS_CIPHER_RSA_DHE_AES256_SHA:
+ suite = "DHE-RSA-AES256-SHA";
+ break;
+ case TLS_CIPHER_AES256_SHA:
+ suite = "AES256-SHA";
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "TLS: Unsupported cipher selection: %d", *c);
+ return -1;
+ }
+ ret = os_snprintf(pos, end - pos, ":%s", suite);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+
+ c++;
+ }
+
+ wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1);
+
+ if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+ wpa_printf(MSG_DEBUG, "Cipher suite configuration failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ WOLFSSL_CIPHER *cipher;
+ const char *name;
+
+ if (!conn || !conn->ssl)
+ return -1;
+
+ cipher = wolfSSL_get_current_cipher(conn->ssl);
+ if (!cipher)
+ return -1;
+
+ name = wolfSSL_CIPHER_get_name(cipher);
+ if (!name)
+ return -1;
+
+ if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0)
+ os_strlcpy(buf, "RC4-SHA", buflen);
+ else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0)
+ os_strlcpy(buf, "AES128-SHA", buflen);
+ else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0)
+ os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen);
+ else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0)
+ os_strlcpy(buf, "ADH-AES128-SHA", buflen);
+ else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0)
+ os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen);
+ else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0)
+ os_strlcpy(buf, "AES256-SHA", buflen);
+ else
+ os_strlcpy(buf, name, buflen);
+
+ return 0;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* no empty fragments in wolfSSL for now */
+ return 0;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+ if (!conn)
+ return -1;
+
+ return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+ if (!conn)
+ return -1;
+
+ /* TODO: this is not incremented anywhere */
+ return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ if (!conn)
+ return -1;
+
+ /* TODO: this is not incremented anywhere */
+ return conn->write_alerts;
+}
+
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+ return os_snprintf(buf, buf_len, "wolfSSL build=%s run=%s",
+ WOLFSSL_VERSION, wolfSSL_lib_version());
+}
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ const char *name;
+
+ if (!conn || !conn->ssl)
+ return -1;
+
+ name = wolfSSL_get_version(conn->ssl);
+ if (!name)
+ return -1;
+
+ os_strlcpy(buf, name, buflen);
+ return 0;
+}
+
+
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+ struct tls_random *keys)
+{
+ WOLFSSL *ssl;
+
+ if (!conn || !keys)
+ return -1;
+ ssl = conn->ssl;
+ if (!ssl)
+ return -1;
+
+ os_memset(keys, 0, sizeof(*keys));
+ keys->client_random = conn->client_random;
+ keys->client_random_len = wolfSSL_get_client_random(
+ ssl, conn->client_random, sizeof(conn->client_random));
+ keys->server_random = conn->server_random;
+ keys->server_random_len = wolfSSL_get_server_random(
+ ssl, conn->server_random, sizeof(conn->server_random));
+
+ return 0;
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+ const char *label, u8 *out, size_t out_len)
+{
+ if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
+ return -1;
+ return 0;
+}
+
+
+#define SEED_LEN (RAN_LEN + RAN_LEN)
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+ u8 *out, size_t out_len)
+{
+ byte seed[SEED_LEN];
+ int ret = -1;
+ WOLFSSL *ssl;
+ byte *tmp_out;
+ byte *_out;
+ int skip = 0;
+ byte *master_key;
+ unsigned int master_key_len;
+ byte *server_random;
+ unsigned int server_len;
+ byte *client_random;
+ unsigned int client_len;
+
+ if (!conn || !conn->ssl)
+ return -1;
+ ssl = conn->ssl;
+
+ skip = 2 * (wolfSSL_GetKeySize(ssl) + wolfSSL_GetHmacSize(ssl) +
+ wolfSSL_GetIVSize(ssl));
+
+ tmp_out = os_malloc(skip + out_len);
+ if (!tmp_out)
+ return -1;
+ _out = tmp_out;
+
+ wolfSSL_get_keys(ssl, &master_key, &master_key_len, &server_random,
+ &server_len, &client_random, &client_len);
+ os_memcpy(seed, server_random, RAN_LEN);
+ os_memcpy(seed + RAN_LEN, client_random, RAN_LEN);
+
+ if (wolfSSL_GetVersion(ssl) == WOLFSSL_TLSV1_2) {
+ tls_prf_sha256(master_key, master_key_len,
+ "key expansion", seed, sizeof(seed),
+ _out, skip + out_len);
+ ret = 0;
+ } else {
+ ret = tls_prf_sha1_md5(master_key, master_key_len,
+ "key expansion", seed, sizeof(seed),
+ _out, skip + out_len);
+ }
+
+ os_memset(master_key, 0, master_key_len);
+ if (ret == 0)
+ os_memcpy(out, _out + skip, out_len);
+ bin_clear_free(tmp_out, skip + out_len);
+
+ return ret;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ (void) ssl_ctx;
+
+ if (!conn || !conn->ssl || ext_type != 35)
+ return -1;
+
+ if (wolfSSL_set_SessionTicket(conn->ssl, data,
+ (unsigned int) data_len) != 1)
+ return -1;
+
+ return 0;
+}
+
+
+static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg)
+{
+ struct tls_connection *conn = arg;
+ int ret;
+ unsigned char client_random[RAN_LEN];
+ unsigned char server_random[RAN_LEN];
+ word32 ticket_len = sizeof(conn->session_ticket);
+
+ if (!conn || !conn->session_ticket_cb)
+ return 1;
+
+ if (wolfSSL_get_client_random(s, client_random,
+ sizeof(client_random)) == 0 ||
+ wolfSSL_get_server_random(s, server_random,
+ sizeof(server_random)) == 0 ||
+ wolfSSL_get_SessionTicket(s, conn->session_ticket,
+ &ticket_len) != 1)
+ return 1;
+
+ if (ticket_len == 0)
+ return 0;
+
+ ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+ conn->session_ticket, ticket_len,
+ client_random, server_random, secret);
+ if (ret <= 0)
+ return 1;
+
+ *secret_len = SECRET_LEN;
+ return 0;
+}
+
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+ struct tls_connection *conn,
+ tls_session_ticket_cb cb,
+ void *ctx)
+{
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+ conn->session_ticket_cb = cb;
+ conn->session_ticket_cb_ctx = ctx;
+
+ if (cb) {
+ if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+ conn) != 1)
+ return -1;
+ } else {
+ if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+ return -1;
+ }
+
+ return 0;
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+ return -1;
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Success data accepted for resumed session");
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+ WOLFSSL_SESSION *sess;
+
+ sess = wolfSSL_get_session(conn->ssl);
+ if (!sess)
+ return;
+
+ wolfSSL_SSL_SESSION_set_timeout(sess, 0);
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: Removed cached session to disable session resumption");
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data)
+{
+ WOLFSSL_SESSION *sess;
+ struct wpabuf *old;
+
+ wpa_printf(MSG_DEBUG, "wolfSSL: Set success data");
+
+ sess = wolfSSL_get_session(conn->ssl);
+ if (!sess) {
+ wpa_printf(MSG_DEBUG,
+ "wolfSSL: No session found for success data");
+ goto fail;
+ }
+
+ old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+ if (old) {
+ wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p",
+ old);
+ wpabuf_free(old);
+ }
+ if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data);
+ conn->success_data = 1;
+ return;
+
+fail:
+ wpa_printf(MSG_INFO, "wolfSSL: Failed to store success data");
+ wpabuf_free(data);
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+ WOLFSSL_SESSION *sess;
+
+ wpa_printf(MSG_DEBUG, "wolfSSL: Get success data");
+
+ sess = wolfSSL_get_session(conn->ssl);
+ if (!sess)
+ return NULL;
+ return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index a6307e3..9922962 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1563,6 +1563,8 @@
* functionality but can support only OCE STA-CFON functionality.
*/
#define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL
+/** Driver supports MFP-optional in the connect command */
+#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL
u64 flags;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -1692,6 +1694,7 @@
#define STA_DRV_DATA_RX_VHT_NSS BIT(5)
#define STA_DRV_DATA_TX_SHORT_GI BIT(6)
#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
+#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
struct hostap_sta_driver_data {
unsigned long rx_packets, tx_packets;
@@ -1704,8 +1707,7 @@
unsigned long num_ps_buf_frames;
unsigned long tx_retry_failed;
unsigned long tx_retry_count;
- int last_rssi;
- int last_ack_rssi;
+ s8 last_ack_rssi;
s8 signal;
u8 rx_vhtmcs;
u8 tx_vhtmcs;
@@ -1875,6 +1877,17 @@
WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */
};
+/* enum smps_mode - SMPS mode definitions */
+enum smps_mode {
+ SMPS_AUTOMATIC,
+ SMPS_OFF,
+ SMPS_DYNAMIC,
+ SMPS_STATIC,
+
+ /* Keep last */
+ SMPS_INVALID,
+};
+
/* enum chan_width - Channel width definitions */
enum chan_width {
CHAN_WIDTH_20_NOHT,
@@ -2052,6 +2065,36 @@
};
/**
+ * struct external_auth - External authentication trigger parameters
+ *
+ * These are used across the external authentication request and event
+ * interfaces.
+ * @action: Action type / trigger for external authentication. Only significant
+ * for the event interface.
+ * @bssid: BSSID of the peer with which the authentication has to happen. Used
+ * by both the request and event interface.
+ * @ssid: SSID of the AP. Used by both the request and event interface.
+ * @ssid_len: SSID length in octets.
+ * @key_mgmt_suite: AKM suite of the respective authentication. Optional for
+ * the request interface.
+ * @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication,
+ * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give
+ * the real status code for failures. Used only for the request interface
+ * from user space to the driver.
+ */
+struct external_auth {
+ enum {
+ EXT_AUTH_START,
+ EXT_AUTH_ABORT,
+ } action;
+ u8 bssid[ETH_ALEN];
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ unsigned int key_mgmt_suite;
+ u16 status;
+};
+
+/**
* struct wpa_driver_ops - Driver interface API definition
*
* This structure defines the API that each driver interface needs to implement
@@ -3611,6 +3654,17 @@
int (*roaming)(void *priv, int allowed, const u8 *bssid);
/**
+ * disable_fils - Enable/disable FILS feature
+ * @priv: Private driver interface data
+ * @disable: 0-enable and 1-disable FILS feature
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback can be used to configure driver and below layers to
+ * enable/disable all FILS features.
+ */
+ int (*disable_fils)(void *priv, int disable);
+
+ /**
* set_mac_addr - Set MAC address
* @priv: Private driver interface data
* @addr: MAC address to use or %NULL for setting back to permanent
@@ -4001,6 +4055,16 @@
int (*update_connect_params)(
void *priv, struct wpa_driver_associate_params *params,
enum wpa_drv_update_connect_params_mask mask);
+
+ /**
+ * send_external_auth_status - Indicate the status of external
+ * authentication processing to the host driver.
+ * @priv: Private driver interface data
+ * @params: Status of authentication processing.
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_external_auth_status)(void *priv,
+ struct external_auth *params);
};
/**
@@ -4496,6 +4560,47 @@
* performed before start operating on this channel.
*/
EVENT_DFS_PRE_CAC_EXPIRED,
+
+ /**
+ * EVENT_EXTERNAL_AUTH - This event interface is used by host drivers
+ * that do not define separate commands for authentication and
+ * association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11
+ * authentication to wpa_supplicant. This event carries all the
+ * necessary information from the host driver for the authentication to
+ * happen.
+ */
+ EVENT_EXTERNAL_AUTH,
+
+ /**
+ * EVENT_PORT_AUTHORIZED - Notification that a connection is authorized
+ *
+ * This event should be indicated when the driver completes the 4-way
+ * handshake. This event should be preceded by an EVENT_ASSOC that
+ * indicates the completion of IEEE 802.11 association.
+ */
+ EVENT_PORT_AUTHORIZED,
+
+ /**
+ * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode
+ * change event.
+ */
+ EVENT_STATION_OPMODE_CHANGED,
+
+ /**
+ * EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed
+ *
+ * This event is emitted when the MAC changes while the interface is
+ * enabled. When an interface was disabled and becomes enabled, it
+ * must be always assumed that the MAC possibly changed.
+ */
+ EVENT_INTERFACE_MAC_CHANGED,
+
+ /**
+ * EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status
+ *
+ * This event is emitted when an interface is added/removed for WDS STA.
+ */
+ EVENT_WDS_STA_INTERFACE_STATUS,
};
@@ -5298,6 +5403,37 @@
P2P_LO_STOPPED_REASON_NOT_SUPPORTED,
} reason_code;
} p2p_lo_stop;
+
+ /* For EVENT_EXTERNAL_AUTH */
+ struct external_auth external_auth;
+
+ /**
+ * struct sta_opmode - Station's operation mode change event
+ * @addr: The station MAC address
+ * @smps_mode: SMPS mode of the station
+ * @chan_width: Channel width of the station
+ * @rx_nss: RX_NSS of the station
+ *
+ * This is used as data with EVENT_STATION_OPMODE_CHANGED.
+ */
+ struct sta_opmode {
+ const u8 *addr;
+ enum smps_mode smps_mode;
+ enum chan_width chan_width;
+ u8 rx_nss;
+ } sta_opmode;
+
+ /**
+ * struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS.
+ */
+ struct wds_sta_interface {
+ const u8 *sta_addr;
+ const char *ifname;
+ enum {
+ INTERFACE_ADDED,
+ INTERFACE_REMOVED
+ } istatus;
+ } wds_sta_interface;
};
/**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index d2b355c..fff23f0 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -931,6 +931,12 @@
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
break;
os_memset(&event, 0, sizeof(event));
+ if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) {
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+ break;
+ }
os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
@@ -1352,6 +1358,40 @@
}
}
+
+static void send_action_cb_event(struct atheros_driver_data *drv,
+ char *data, size_t data_len)
+{
+ union wpa_event_data event;
+ struct ieee80211_send_action_cb *sa;
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ if (data_len < sizeof(*sa) + 24) {
+ wpa_printf(MSG_DEBUG,
+ "athr: Too short event message (data_len=%d sizeof(*sa)=%d)",
+ (int) data_len, (int) sizeof(*sa));
+ wpa_hexdump(MSG_DEBUG, "athr: Short event message",
+ data, data_len);
+ return;
+ }
+
+ sa = (struct ieee80211_send_action_cb *) data;
+
+ hdr = (const struct ieee80211_hdr *) (sa + 1);
+ fc = le_to_host16(hdr->frame_control);
+
+ os_memset(&event, 0, sizeof(event));
+ event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+ event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+ event.tx_status.dst = sa->dst_addr;
+ event.tx_status.data = (const u8 *) hdr;
+ event.tx_status.data_len = data_len - sizeof(*sa);
+ event.tx_status.ack = sa->ack;
+ wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
+}
+
+
/*
* Handle size of data problem. WEXT only allows data of 256 bytes for custom
* events, and p2p data can be much bigger. So the athr driver sends a small
@@ -1416,6 +1456,11 @@
&event);
continue;
}
+ } else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) {
+ wpa_printf(MSG_DEBUG,
+ "%s: ACTION_CB frame_type=%u len=%zu",
+ __func__, frame_type, data_len);
+ send_action_cb_event(drv, (void *) mgmt, data_len);
} else {
wpa_printf(MSG_DEBUG, "athr: %s unknown type %d",
__func__, frame_type);
@@ -1429,6 +1474,10 @@
int opcode, char *buf, int len)
{
switch (opcode) {
+ case IEEE80211_EV_P2P_SEND_ACTION_CB:
+ wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB");
+ fetch_pending_big_events(drv);
+ break;
case IEEE80211_EV_RX_MGMT:
wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT");
fetch_pending_big_events(drv);
@@ -1812,7 +1861,7 @@
os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
iwr.u.essid.flags = 1; /* SSID active */
iwr.u.essid.pointer = (caddr_t) buf;
- iwr.u.essid.length = len + 1;
+ iwr.u.essid.length = len;
if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 0464304..ac0916e 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -82,6 +82,11 @@
E2S(P2P_LO_STOP);
E2S(BEACON_LOSS);
E2S(DFS_PRE_CAC_EXPIRED);
+ E2S(EXTERNAL_AUTH);
+ E2S(PORT_AUTHORIZED);
+ E2S(STATION_OPMODE_CHANGED);
+ E2S(INTERFACE_MAC_CHANGED);
+ E2S(WDS_STA_INTERFACE_STATUS);
}
return "UNKNOWN";
@@ -268,6 +273,19 @@
DF2S(OFFCHANNEL_SIMULTANEOUS);
DF2S(FULL_AP_CLIENT_STATE);
DF2S(P2P_LISTEN_OFFLOAD);
+ DF2S(SUPPORT_FILS);
+ DF2S(BEACON_RATE_LEGACY);
+ DF2S(BEACON_RATE_HT);
+ DF2S(BEACON_RATE_VHT);
+ DF2S(MGMT_TX_RANDOM_TA);
+ DF2S(MGMT_TX_RANDOM_TA_CONNECTED);
+ DF2S(SCHED_SCAN_RELATIVE_RSSI);
+ DF2S(HE_CAPABILITIES);
+ DF2S(FILS_SK_OFFLOAD);
+ DF2S(OCE_STA);
+ DF2S(OCE_AP);
+ DF2S(OCE_STA_CFON);
+ DF2S(MFP_OPTIONAL);
}
return "UNKNOWN";
#undef DF2S
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
index d3be19c..6766a62 100644
--- a/src/drivers/driver_macsec_qca.c
+++ b/src/drivers/driver_macsec_qca.c
@@ -592,6 +592,7 @@
fal_tx_class_lut_t entry;
u8 psci[ETH_ALEN + 2];
u32 channel;
+ u16 sci_port = be_to_host16(sc->sci.port);
ret = macsec_qca_get_available_transmit_sc(priv, &channel);
if (ret != 0)
@@ -607,8 +608,8 @@
entry.channel = channel;
os_memcpy(psci, sc->sci.addr, ETH_ALEN);
- psci[6] = (sc->sci.port >> 8) & 0xf;
- psci[7] = sc->sci.port & 0xf;
+ psci[6] = (sci_port >> 8) & 0xf;
+ psci[7] = sci_port & 0xf;
ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 2ce03ed..5cff47f 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -130,7 +130,7 @@
static void nl80211_register_eloop_read(struct nl_handle **handle,
eloop_sock_handler handler,
- void *eloop_data)
+ void *eloop_data, int persist)
{
#ifdef CONFIG_LIBNL20
/*
@@ -151,13 +151,17 @@
nl_socket_set_nonblocking(*handle);
eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
eloop_data, *handle);
- *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+ if (!persist)
+ *handle = (void *) (((intptr_t) *handle) ^
+ ELOOP_SOCKET_INVALID);
}
-static void nl80211_destroy_eloop_handle(struct nl_handle **handle)
+static void nl80211_destroy_eloop_handle(struct nl_handle **handle, int persist)
{
- *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+ if (!persist)
+ *handle = (void *) (((intptr_t) *handle) ^
+ ELOOP_SOCKET_INVALID);
eloop_unregister_read_sock(nl_socket_get_fd(*handle));
nl_destroy_handles(handle);
}
@@ -723,7 +727,7 @@
}
nl80211_register_eloop_read(&w->nl_beacons,
- nl80211_recv_beacons, w);
+ nl80211_recv_beacons, w, 0);
}
dl_list_add(&nl80211_wiphys, &w->list);
@@ -772,7 +776,7 @@
return;
if (w->nl_beacons)
- nl80211_destroy_eloop_handle(&w->nl_beacons);
+ nl80211_destroy_eloop_handle(&w->nl_beacons, 0);
nl_cb_put(w->nl_cb);
dl_list_del(&w->list);
@@ -947,7 +951,7 @@
static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv,
- int ifindex)
+ int ifindex, int notify)
{
struct i802_bss *bss;
u8 addr[ETH_ALEN];
@@ -966,6 +970,9 @@
ifindex, bss->ifname,
MAC2STR(bss->addr), MAC2STR(addr));
os_memcpy(bss->addr, addr, ETH_ALEN);
+ if (notify)
+ wpa_supplicant_event(drv->ctx,
+ EVENT_INTERFACE_MAC_CHANGED, NULL);
}
}
@@ -1037,11 +1044,11 @@
namebuf[0] = '\0';
if (if_indextoname(ifi->ifi_index, namebuf) &&
linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
- /* Re-read MAC address as it may have changed */
- nl80211_refresh_mac(drv, ifi->ifi_index);
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event since interface %s is up", namebuf);
drv->ignore_if_down_event = 0;
+ /* Re-read MAC address as it may have changed */
+ nl80211_refresh_mac(drv, ifi->ifi_index, 1);
return;
}
wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
@@ -1087,7 +1094,7 @@
"removed", drv->first_bss->ifname);
} else {
/* Re-read MAC address as it may have changed */
- nl80211_refresh_mac(drv, ifi->ifi_index);
+ nl80211_refresh_mac(drv, ifi->ifi_index, 0);
wpa_printf(MSG_DEBUG, "nl80211: Interface up");
drv->if_disabled = 0;
@@ -1631,7 +1638,7 @@
nl80211_register_eloop_read(&global->nl_event,
wpa_driver_nl80211_event_receive,
- global->nl_cb);
+ global->nl_cb, 0);
return 0;
@@ -1997,7 +2004,7 @@
{
nl80211_register_eloop_read(&bss->nl_mgmt,
wpa_driver_nl80211_event_receive,
- bss->nl_cb);
+ bss->nl_cb, 0);
}
@@ -2010,6 +2017,25 @@
}
+static int nl80211_init_connect_handle(struct i802_bss *bss)
+{
+ if (bss->nl_connect) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Connect handle already created (nl_connect=%p)",
+ bss->nl_connect);
+ return -1;
+ }
+
+ bss->nl_connect = nl_create_handle(bss->nl_cb, "connect");
+ if (!bss->nl_connect)
+ return -1;
+ nl80211_register_eloop_read(&bss->nl_connect,
+ wpa_driver_nl80211_event_receive,
+ bss->nl_cb, 1);
+ return 0;
+}
+
+
static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -2020,7 +2046,9 @@
wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
"handle %p", bss->nl_mgmt);
- if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+ if (drv->nlmode == NL80211_IFTYPE_ADHOC ||
+ ((drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_SME))) {
u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
/* register for any AUTH message */
@@ -2311,7 +2339,7 @@
return;
wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
"(%s)", bss->nl_mgmt, reason);
- nl80211_destroy_eloop_handle(&bss->nl_mgmt);
+ nl80211_destroy_eloop_handle(&bss->nl_mgmt, 0);
nl80211_put_wiphy_data_ap(bss);
}
@@ -2527,6 +2555,8 @@
if (drv->vendor_cmd_test_avail)
qca_vendor_test(drv);
+ nl80211_init_connect_handle(bss);
+
return 0;
}
@@ -2640,6 +2670,9 @@
nl80211_del_p2pdev(bss);
}
+ if (bss->nl_connect)
+ nl80211_destroy_eloop_handle(&bss->nl_connect, 1);
+
nl80211_destroy_bss(drv->first_bss);
os_free(drv->filter_ssids);
@@ -2782,6 +2815,44 @@
#endif /* CONFIG_DRIVER_NL80211_QCA */
+static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
+ const u8 *key, size_t key_len,
+ const u8 *addr)
+{
+ struct nl_msg *msg = NULL;
+ int ret;
+
+ /*
+ * If the authenticator address is not set, assume it is
+ * the current BSSID.
+ */
+ if (!addr && drv->associated)
+ addr = drv->bssid;
+ else if (!addr)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR,
+ MAC2STR(addr));
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK);
+ if (!msg ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put(msg, NL80211_ATTR_PMK, key_len, key)) {
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
enum wpa_alg alg, const u8 *addr,
int key_idx, int set_tx,
@@ -2820,6 +2891,10 @@
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
+ if (alg == WPA_ALG_PMK &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+ return nl80211_set_pmk(drv, key, key_len, addr);
+
if (alg == WPA_ALG_NONE) {
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
if (!msg)
@@ -3273,11 +3348,11 @@
msg = NULL;
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
- "nl80211: MLME command failed (auth): ret=%d (%s)",
- ret, strerror(-ret));
+ "nl80211: MLME command failed (auth): count=%d ret=%d (%s)",
+ count, ret, strerror(-ret));
count++;
- if (ret == -EALREADY && count == 1 && params->bssid &&
- !params->local_state_change) {
+ if ((ret == -EALREADY || ret == -EEXIST) && count == 1 &&
+ params->bssid && !params->local_state_change) {
/*
* mac80211 does not currently accept new
* authentication if we are already authenticated. As a
@@ -3957,7 +4032,7 @@
smps_mode = NL80211_SMPS_OFF;
break;
}
- if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
+ if (nla_put_u8(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
goto fail;
}
@@ -5256,7 +5331,9 @@
params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
- params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384) {
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
switch (params->key_mgmt_suite) {
@@ -5299,6 +5376,12 @@
case WPA_KEY_MGMT_FT_FILS_SHA384:
mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
break;
+ case WPA_KEY_MGMT_OWE:
+ mgmt = RSN_AUTH_KEY_MGMT_OWE;
+ break;
+ case WPA_KEY_MGMT_DPP:
+ mgmt = RSN_AUTH_KEY_MGMT_DPP;
+ break;
case WPA_KEY_MGMT_PSK:
default:
mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
@@ -5309,6 +5392,14 @@
return -1;
}
+ /* Add PSK in case of 4-way handshake offload */
+ if (params->psk &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) {
+ wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32);
+ if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk))
+ return -1;
+ }
+
if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
return -1;
@@ -5320,10 +5411,6 @@
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
return -1;
- if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
- nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
- return -1;
-
if (params->rrm_used) {
u32 drv_rrm_flags = drv->capa.rrm_flags;
if ((!((drv_rrm_flags &
@@ -5360,13 +5447,19 @@
nl80211_put_fils_connect_params(drv, params, msg) != 0)
return -1;
+ if ((params->auth_alg & WPA_AUTH_ALG_SAE) &&
+ (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
+ nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+ return -1;
+
return 0;
}
static int wpa_driver_nl80211_try_connect(
struct wpa_driver_nl80211_data *drv,
- struct wpa_driver_associate_params *params)
+ struct wpa_driver_associate_params *params,
+ struct nl_handle *nl_connect)
{
struct nl_msg *msg;
enum nl80211_auth_type type;
@@ -5394,6 +5487,15 @@
if (ret)
goto fail;
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+ goto fail;
+
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_MFP_OPTIONAL) &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL))
+ goto fail;
+
algs = 0;
if (params->auth_alg & WPA_AUTH_ALG_OPEN)
algs++;
@@ -5403,6 +5505,8 @@
algs++;
if (params->auth_alg & WPA_AUTH_ALG_FILS)
algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_FT)
+ algs++;
if (algs > 1) {
wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
"selection");
@@ -5420,7 +5524,11 @@
if (ret)
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (nl_connect)
+ ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL);
+ else
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
@@ -5439,7 +5547,8 @@
static int wpa_driver_nl80211_connect(
struct wpa_driver_nl80211_data *drv,
- struct wpa_driver_associate_params *params)
+ struct wpa_driver_associate_params *params,
+ struct nl_handle *nl_connect)
{
int ret;
@@ -5449,7 +5558,7 @@
else
os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
- ret = wpa_driver_nl80211_try_connect(drv, params);
+ ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect);
if (ret == -EALREADY) {
/*
* cfg80211 does not currently accept new connections if
@@ -5462,7 +5571,7 @@
if (wpa_driver_nl80211_disconnect(
drv, WLAN_REASON_PREV_AUTH_NOT_VALID))
return -1;
- ret = wpa_driver_nl80211_try_connect(drv, params);
+ ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect);
}
return ret;
}
@@ -5487,10 +5596,13 @@
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
enum nl80211_iftype nlmode = params->p2p ?
NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+ struct nl_handle *nl_connect = NULL;
if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
return -1;
- return wpa_driver_nl80211_connect(drv, params);
+ if (params->auth_alg & WPA_AUTH_ALG_SAE)
+ nl_connect = bss->nl_connect;
+ return wpa_driver_nl80211_connect(drv, params, nl_connect);
}
nl80211_mark_disconnected(drv);
@@ -5505,6 +5617,10 @@
if (ret)
goto fail;
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+ goto fail;
+
if (params->fils_kek) {
wpa_printf(MSG_DEBUG, " * FILS KEK (len=%u)",
(unsigned int) params->fils_kek_len);
@@ -5938,6 +6054,7 @@
[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
};
struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -6000,6 +6117,11 @@
nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
if (stats[NL80211_STA_INFO_SIGNAL])
data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
+ if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
+ data->last_ack_rssi =
+ nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
+ data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
+ }
if (stats[NL80211_STA_INFO_TX_BITRATE] &&
nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
@@ -6389,6 +6511,7 @@
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
char name[IFNAMSIZ + 1];
+ union wpa_event_data event;
os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
if (ifname_wds)
@@ -6407,6 +6530,14 @@
linux_br_add_if(drv->global->ioctl_sock,
bridge_ifname, name) < 0)
return -1;
+
+ os_memset(&event, 0, sizeof(event));
+ event.wds_sta_interface.sta_addr = addr;
+ event.wds_sta_interface.ifname = name;
+ event.wds_sta_interface.istatus = INTERFACE_ADDED;
+ wpa_supplicant_event(drv->ctx,
+ EVENT_WDS_STA_INTERFACE_STATUS,
+ &event);
}
if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
@@ -6420,6 +6551,12 @@
i802_set_sta_vlan(priv, addr, bss->ifname, 0);
nl80211_remove_iface(drv, if_nametoindex(name));
+ os_memset(&event, 0, sizeof(event));
+ event.wds_sta_interface.sta_addr = addr;
+ event.wds_sta_interface.ifname = name;
+ event.wds_sta_interface.istatus = INTERFACE_REMOVED;
+ wpa_supplicant_event(drv->ctx, EVENT_WDS_STA_INTERFACE_STATUS,
+ &event);
return 0;
}
}
@@ -7200,7 +7337,7 @@
} else if (bss->nl_preq) {
wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
"reporting nl_preq=%p", bss->nl_preq);
- nl80211_destroy_eloop_handle(&bss->nl_preq);
+ nl80211_destroy_eloop_handle(&bss->nl_preq, 0);
}
return 0;
}
@@ -7225,7 +7362,7 @@
nl80211_register_eloop_read(&bss->nl_preq,
wpa_driver_nl80211_event_receive,
- bss->nl_cb);
+ bss->nl_cb, 0);
return 0;
@@ -7540,7 +7677,7 @@
nl_destroy_handles(&global->nl);
if (global->nl_event)
- nl80211_destroy_eloop_handle(&global->nl_event);
+ nl80211_destroy_eloop_handle(&global->nl_event, 0);
nl_cb_put(global->nl_cb);
@@ -7563,6 +7700,7 @@
struct wpa_pmkid_params *params)
{
struct nl_msg *msg;
+ const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */
if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
(params->pmkid &&
@@ -7574,7 +7712,7 @@
(params->fils_cache_id &&
nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
params->fils_cache_id)) ||
- (params->pmk_len &&
+ (params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
nlmsg_free(msg);
return -ENOBUFS;
@@ -7587,6 +7725,7 @@
static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
{
struct i802_bss *bss = priv;
+ int ret;
if (params->bssid)
wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR,
@@ -7598,13 +7737,21 @@
wpa_ssid_txt(params->ssid, params->ssid_len));
}
- return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
+ ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
}
static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params)
{
struct i802_bss *bss = priv;
+ int ret;
if (params->bssid)
wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
@@ -7616,7 +7763,14 @@
wpa_ssid_txt(params->ssid, params->ssid_len));
}
- return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
+ ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
}
@@ -7912,6 +8066,7 @@
static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
{
struct nl_msg *msg;
+ int ret;
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
nla_put_u32(msg, NL80211_ATTR_PS_STATE,
@@ -7919,7 +8074,15 @@
nlmsg_free(msg);
return -ENOBUFS;
}
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Setting PS state %s failed: %d (%s)",
+ enabled ? "enabled" : "disabled",
+ ret, strerror(-ret));
+ }
+ return ret;
}
@@ -8521,8 +8684,9 @@
return -EOPNOTSUPP;
}
- if ((drv->nlmode != NL80211_IFTYPE_AP) &&
- (drv->nlmode != NL80211_IFTYPE_P2P_GO))
+ if (drv->nlmode != NL80211_IFTYPE_AP &&
+ drv->nlmode != NL80211_IFTYPE_P2P_GO &&
+ drv->nlmode != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
/*
@@ -8885,6 +9049,34 @@
}
+static int nl80211_disable_fils(void *priv, int disable)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Disable FILS=%d", disable);
+
+ if (!drv->set_wifi_conf_vendor_cmd_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS,
+ disable)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
#define WPA_SUPPLICANT_CLIENT_ID 1
@@ -9026,8 +9218,8 @@
return -1;
if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
- nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
- params->auto_plinks)) ||
+ nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ params->auto_plinks)) ||
((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
params->max_peer_links)) ||
@@ -10341,6 +10533,38 @@
}
+static int nl80211_send_external_auth_status(void *priv,
+ struct external_auth *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg = NULL;
+ int ret = -1;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: External auth status: %u", params->status);
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
+ if (!msg ||
+ nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
+ nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid) ||
+ nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))
+ goto fail;
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: External Auth status update failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -10450,6 +10674,7 @@
.get_ifindex = nl80211_get_ifindex,
#ifdef CONFIG_DRIVER_NL80211_QCA
.roaming = nl80211_roaming,
+ .disable_fils = nl80211_disable_fils,
.do_acs = wpa_driver_do_acs,
.set_band = nl80211_set_band,
.get_pref_freq_list = nl80211_get_pref_freq_list,
@@ -10467,4 +10692,5 @@
.configure_data_frame_filters = nl80211_configure_data_frame_filters,
.get_ext_capab = nl80211_get_ext_capab,
.update_connect_params = nl80211_update_connection_params,
+ .send_external_auth_status = nl80211_send_external_auth_status,
};
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 23cf9db..4bdeaa0 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -73,7 +73,7 @@
int if_dynamic;
void *ctx;
- struct nl_handle *nl_preq, *nl_mgmt;
+ struct nl_handle *nl_preq, *nl_mgmt, *nl_connect;
struct nl_cb *nl_cb;
struct nl80211_wiphy_data *wiphy_data;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index f11a1d7..ba053b4 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -401,6 +401,20 @@
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_FILS_SK_OFFLOAD))
capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
+ capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL))
+ capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_DFS_OFFLOAD))
+ capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index e6bc254..5b8efbc 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -131,6 +131,11 @@
C2S(NL80211_CMD_SET_QOS_MAP)
C2S(NL80211_CMD_ADD_TX_TS)
C2S(NL80211_CMD_DEL_TX_TS)
+ C2S(NL80211_CMD_WIPHY_REG_CHANGE)
+ C2S(NL80211_CMD_PORT_AUTHORIZED)
+ C2S(NL80211_CMD_EXTERNAL_AUTH)
+ C2S(NL80211_CMD_STA_OPMODE_CHANGED)
+ C2S(NL80211_CMD_CONTROL_PORT_FRAME)
default:
return "NL80211_CMD_UNKNOWN";
}
@@ -673,7 +678,7 @@
cookie_val = nla_get_u64(cookie);
wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
- " cookie=0%llx%s (ack=%d)",
+ " cookie=0x%llx%s (ack=%d)",
(long long unsigned int) cookie_val,
cookie_val == drv->send_action_cookie ?
" (match)" : " (unknown)", ack != NULL);
@@ -1590,6 +1595,9 @@
wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
&data);
break;
+ case NL80211_RADAR_CAC_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
"received", event_type);
@@ -2175,6 +2183,146 @@
}
+static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data event;
+ enum nl80211_external_auth_action act;
+
+ if (!tb[NL80211_ATTR_AKM_SUITES] ||
+ !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] ||
+ !tb[NL80211_ATTR_BSSID] ||
+ !tb[NL80211_ATTR_SSID])
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]);
+ switch (act) {
+ case NL80211_EXTERNAL_AUTH_START:
+ event.external_auth.action = EXT_AUTH_START;
+ break;
+ case NL80211_EXTERNAL_AUTH_ABORT:
+ event.external_auth.action = EXT_AUTH_ABORT;
+ break;
+ default:
+ return;
+ }
+
+ event.external_auth.key_mgmt_suite =
+ nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]);
+
+ event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]);
+ if (event.external_auth.ssid_len > SSID_MAX_LEN)
+ return;
+ os_memcpy(event.external_auth.ssid, nla_data(tb[NL80211_ATTR_SSID]),
+ event.external_auth.ssid_len);
+
+ os_memcpy(event.external_auth.bssid, nla_data(tb[NL80211_ATTR_BSSID]),
+ ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: External auth action: %u, AKM: 0x%x",
+ event.external_auth.action,
+ event.external_auth.key_mgmt_suite);
+ wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event);
+}
+
+
+static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ const u8 *addr;
+
+ if (!tb[NL80211_ATTR_MAC] ||
+ nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore port authorized event without BSSID");
+ return;
+ }
+
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore port authorized event for " MACSTR
+ " (not the currently connected BSSID " MACSTR ")",
+ MAC2STR(addr), MAC2STR(drv->bssid));
+ return;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, NULL);
+}
+
+
+static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data ed;
+ u8 smps_mode, max_bw;
+
+ if (!tb[NL80211_ATTR_MAC] ||
+ (!tb[NL80211_ATTR_CHANNEL_WIDTH] &&
+ !tb[NL80211_ATTR_SMPS_MODE] &&
+ !tb[NL80211_ATTR_NSS]))
+ return;
+
+ ed.sta_opmode.smps_mode = SMPS_INVALID;
+ ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
+ ed.sta_opmode.rx_nss = 0xff;
+ ed.sta_opmode.addr = nla_data(tb[NL80211_ATTR_MAC]);
+
+ if (tb[NL80211_ATTR_SMPS_MODE]) {
+ smps_mode = nla_get_u32(tb[NL80211_ATTR_SMPS_MODE]);
+ switch (smps_mode) {
+ case NL80211_SMPS_OFF:
+ ed.sta_opmode.smps_mode = SMPS_OFF;
+ break;
+ case NL80211_SMPS_STATIC:
+ ed.sta_opmode.smps_mode = SMPS_STATIC;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ ed.sta_opmode.smps_mode = SMPS_DYNAMIC;
+ break;
+ default:
+ ed.sta_opmode.smps_mode = SMPS_INVALID;
+ break;
+ }
+ }
+
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+ max_bw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]);
+ switch (max_bw) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_20_NOHT;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_40;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_80;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_80P80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_160;
+ break;
+ default:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
+ break;
+
+ }
+ }
+
+ if (tb[NL80211_ATTR_NSS])
+ ed.sta_opmode.rx_nss = nla_get_u8(tb[NL80211_ATTR_NSS]);
+
+ wpa_supplicant_event(drv->ctx, EVENT_STATION_OPMODE_CHANGED, &ed);
+}
+
+
static void do_process_drv_event(struct i802_bss *bss, int cmd,
struct nlattr **tb)
{
@@ -2328,6 +2476,7 @@
nl80211_cqm_event(drv, tb);
break;
case NL80211_CMD_REG_CHANGE:
+ case NL80211_CMD_WIPHY_REG_CHANGE:
nl80211_reg_change_event(drv, tb);
break;
case NL80211_CMD_REG_BEACON_HINT:
@@ -2373,6 +2522,12 @@
case NL80211_CMD_NEW_PEER_CANDIDATE:
nl80211_new_peer_candidate(drv, tb);
break;
+ case NL80211_CMD_PORT_AUTHORIZED:
+ nl80211_port_authorized(drv, tb);
+ break;
+ case NL80211_CMD_STA_OPMODE_CHANGED:
+ nl80211_sta_opmode_change_event(drv, tb);
+ break;
default:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
@@ -2387,10 +2542,11 @@
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct wpa_driver_nl80211_data *drv, *tmp;
- int ifidx = -1;
+ int ifidx = -1, wiphy_idx = -1, wiphy_idx_rx = -1;
struct i802_bss *bss;
u64 wdev_id = 0;
int wdev_id_set = 0;
+ int wiphy_idx_set = 0;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
@@ -2400,13 +2556,19 @@
else if (tb[NL80211_ATTR_WDEV]) {
wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
wdev_id_set = 1;
+ } else if (tb[NL80211_ATTR_WIPHY]) {
+ wiphy_idx_rx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+ wiphy_idx_set = 1;
}
dl_list_for_each_safe(drv, tmp, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
for (bss = drv->first_bss; bss; bss = bss->next) {
- if ((ifidx == -1 && !wdev_id_set) ||
+ if (wiphy_idx_set)
+ wiphy_idx = nl80211_get_wiphy_index(bss);
+ if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) ||
ifidx == bss->ifindex ||
+ (wiphy_idx_set && wiphy_idx == wiphy_idx_rx) ||
(wdev_id_set && bss->wdev_id_set &&
wdev_id == bss->wdev_id)) {
do_process_drv_event(bss, gnlh->cmd, tb);
@@ -2451,6 +2613,9 @@
case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
nl80211_spurious_frame(bss, tb, 1);
break;
+ case NL80211_CMD_EXTERNAL_AUTH:
+ nl80211_external_auth(bss->drv, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 659eda2..933b8d9 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -922,7 +922,7 @@
static void wext_check_hostap(struct wpa_driver_wext_data *drv)
{
- char buf[200], *pos;
+ char path[200], buf[200], *pos;
ssize_t res;
/*
@@ -937,9 +937,9 @@
*/
/* First, try to see if driver information is available from sysfs */
- snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/driver",
+ snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver",
drv->ifname);
- res = readlink(buf, buf, sizeof(buf) - 1);
+ res = readlink(path, buf, sizeof(buf) - 1);
if (res > 0) {
buf[res] = '\0';
pos = strrchr(buf, '/');
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 6095a6c..15daf5e 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -173,6 +173,29 @@
*/
/**
+ * DOC: WPA/WPA2 EAPOL handshake offload
+ *
+ * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK flag drivers
+ * can indicate they support offloading EAPOL handshakes for WPA/WPA2
+ * preshared key authentication. In %NL80211_CMD_CONNECT the preshared
+ * key should be specified using %NL80211_ATTR_PMK. Drivers supporting
+ * this offload may reject the %NL80211_CMD_CONNECT when no preshared
+ * key material is provided, for example when that driver does not
+ * support setting the temporal keys through %CMD_NEW_KEY.
+ *
+ * Similarly @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X flag can be
+ * set by drivers indicating offload support of the PTK/GTK EAPOL
+ * handshakes during 802.1X authentication. In order to use the offload
+ * the %NL80211_CMD_CONNECT should have %NL80211_ATTR_WANT_1X_4WAY_HS
+ * attribute flag. Drivers supporting this offload may reject the
+ * %NL80211_CMD_CONNECT when the attribute flag is not present.
+ *
+ * For 802.1X the PMK or PMK-R0 are set by providing %NL80211_ATTR_PMK
+ * using %NL80211_CMD_SET_PMK. For offloaded FT support also
+ * %NL80211_ATTR_PMKR0_NAME must be provided.
+ */
+
+/**
* DOC: FILS shared key authentication offload
*
* FILS shared key authentication offload can be advertized by drivers by
@@ -387,7 +410,9 @@
* are used. Extra IEs can also be passed from the userspace by
* using the %NL80211_ATTR_IE attribute. The first cycle of the
* scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
- * is supplied.
+ * is supplied. If the device supports multiple concurrent scheduled
+ * scans, it will allow such when the caller provides the flag attribute
+ * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it.
* @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
* scheduled scan is not running. The caller may assume that as soon
* as the call returns, it is safe to start a new scheduled scan again.
@@ -517,7 +542,8 @@
* IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
* %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
* %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
- * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+ * %NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and
* %NL80211_ATTR_WIPHY_FREQ_HINT.
* If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
* restrictions on BSS selection, i.e., they effectively prevent roaming
@@ -544,8 +570,14 @@
* authentication/association or not receiving a response from the AP.
* Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
* well to remain backwards compatible.
- * @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
- * sent as an event when the card/driver roamed by itself.
+ * When establishing a security association, drivers that support 4 way
+ * handshake offload should send %NL80211_CMD_PORT_AUTHORIZED event when
+ * the 4 way handshake is completed successfully.
+ * @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself.
+ * When a security association was established with the new AP (e.g. if
+ * the FT protocol was used for roaming or the driver completed the 4 way
+ * handshake), this event should be followed by an
+ * %NL80211_CMD_PORT_AUTHORIZED event.
* @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
* userspace that a connection was dropped by the AP or due to other
* reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
@@ -945,6 +977,59 @@
* does not result in a change for the current association. Currently,
* only the %NL80211_ATTR_IE data is used and updated with this command.
*
+ * @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0
+ * for the given authenticator address (specified with &NL80211_ATTR_MAC).
+ * When &NL80211_ATTR_PMKR0_NAME is set, &NL80211_ATTR_PMK specifies the
+ * PMK-R0, otherwise it specifies the PMK.
+ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
+ * configured PMK for the authenticator address identified by
+ * &NL80211_ATTR_MAC.
+ * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates that the 4 way
+ * handshake was completed successfully by the driver. The BSSID is
+ * specified with &NL80211_ATTR_MAC. Drivers that support 4 way handshake
+ * offload should send this event after indicating 802.11 association with
+ * &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed
+ * &NL80211_CMD_DISCONNECT should be indicated instead.
+ *
+ * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
+ * and RX notification. This command is used both as a request to transmit
+ * a control port frame and as a notification that a control port frame
+ * has been received. %NL80211_ATTR_FRAME is used to specify the
+ * frame contents. The frame is the raw EAPoL data, without ethernet or
+ * 802.11 headers.
+ * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
+ * indicating the protocol type of the received frame; whether the frame
+ * was received unencrypted and the MAC address of the peer respectively.
+ *
+ * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded.
+ *
+ * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host
+ * drivers that do not define separate commands for authentication and
+ * association, but rely on user space for the authentication to happen.
+ * This interface acts both as the event request (driver to user space)
+ * to trigger the authentication and command response (userspace to
+ * driver) to indicate the authentication status.
+ *
+ * User space uses the %NL80211_CMD_CONNECT command to the host driver to
+ * trigger a connection. The host driver selects a BSS and further uses
+ * this interface to offload only the authentication part to the user
+ * space. Authentication frames are passed between the driver and user
+ * space through the %NL80211_CMD_FRAME interface. Host driver proceeds
+ * further with the association after getting successful authentication
+ * status. User space indicates the authentication status through
+ * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH
+ * command interface.
+ *
+ * Host driver reports this status on an authentication failure to the
+ * user space through the connect result as the user space would have
+ * initiated the connection through the connect request.
+ *
+ * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's
+ * ht opmode or vht opmode changes using any of &NL80211_ATTR_SMPS_MODE,
+ * &NL80211_ATTR_CHANNEL_WIDTH,&NL80211_ATTR_NSS attributes with its
+ * address(specified in &NL80211_ATTR_MAC).
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1144,6 +1229,19 @@
NL80211_CMD_UPDATE_CONNECT_PARAMS,
+ NL80211_CMD_SET_PMK,
+ NL80211_CMD_DEL_PMK,
+
+ NL80211_CMD_PORT_AUTHORIZED,
+
+ NL80211_CMD_RELOAD_REGDB,
+
+ NL80211_CMD_EXTERNAL_AUTH,
+
+ NL80211_CMD_STA_OPMODE_CHANGED,
+
+ NL80211_CMD_CONTROL_PORT_FRAME,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1366,8 +1464,12 @@
*
* @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
* used for the association (&enum nl80211_mfp, represented as a u32);
- * this attribute can be used
- * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
+ * this attribute can be used with %NL80211_CMD_ASSOCIATE and
+ * %NL80211_CMD_CONNECT requests. %NL80211_MFP_OPTIONAL is not allowed for
+ * %NL80211_CMD_ASSOCIATE since user space SME is expected and hence, it
+ * must have decided whether to use management frame protection or not.
+ * Setting %NL80211_MFP_OPTIONAL with a %NL80211_CMD_CONNECT request will
+ * let the driver (or the firmware) decide whether to use MFP or not.
*
* @NL80211_ATTR_STA_FLAGS2: Attribute containing a
* &struct nl80211_sta_flag_update.
@@ -1387,6 +1489,15 @@
* @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with
* %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom
* ethertype frames used for key negotiation must not be encrypted.
+ * @NL80211_ATTR_CONTROL_PORT_OVER_NL80211: A flag indicating whether control
+ * port frames (e.g. of type given in %NL80211_ATTR_CONTROL_PORT_ETHERTYPE)
+ * will be sent directly to the network interface or sent via the NL80211
+ * socket. If this attribute is missing, then legacy behavior of sending
+ * control port frames directly to the network interface is used. If the
+ * flag is included, then control port frames are sent over NL80211 instead
+ * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is
+ * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER
+ * flag.
*
* @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
* We recommend using nested, driver-specific attributes within this.
@@ -1868,13 +1979,18 @@
* that configured the indoor setting, and the indoor operation would be
* cleared when the socket is closed.
* If set during NAN interface creation, the interface will be destroyed
- * if the socket is closed just like any other interface. Moreover, only
- * the netlink socket that created the interface will be allowed to add
- * and remove functions. NAN notifications will be sent in unicast to that
- * socket. Without this attribute, any socket can add functions and the
- * notifications will be sent to the %NL80211_MCGRP_NAN multicast group.
+ * if the socket is closed just like any other interface. Moreover, NAN
+ * notifications will be sent in unicast to that socket. Without this
+ * attribute, the notifications will be sent to the %NL80211_MCGRP_NAN
+ * multicast group.
* If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
* station will deauthenticate when the socket is closed.
+ * If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically
+ * torn down when the socket is closed.
+ * If set during %NL80211_CMD_JOIN_MESH the mesh setup will be
+ * automatically torn down when the socket is closed.
+ * If set during %NL80211_CMD_START_AP the AP will be automatically
+ * disabled when the socket is closed.
*
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
* the TDLS link initiator.
@@ -2078,8 +2194,36 @@
* identifying the scope of PMKSAs. This is used with
* @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
*
- * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID.
- * This is used with @NL80211_CMD_SET_PMKSA.
+ * @NL80211_ATTR_PMK: attribute for passing PMK key material. Used with
+ * %NL80211_CMD_SET_PMKSA for the PMKSA identified by %NL80211_ATTR_PMKID.
+ * For %NL80211_CMD_CONNECT it is used to provide PSK for offloading 4-way
+ * handshake for WPA/WPA2-PSK networks. For 802.1X authentication it is
+ * used with %NL80211_CMD_SET_PMK. For offloaded FT support this attribute
+ * specifies the PMK-R0 if NL80211_ATTR_PMKR0_NAME is included as well.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to
+ * indicate that it supports multiple active scheduled scan requests.
+ * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled
+ * scan request that may be active for the device (u32).
+ *
+ * @NL80211_ATTR_WANT_1X_4WAY_HS: flag attribute which user-space can include
+ * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it
+ * wants to use the supported offload of the 4-way handshake.
+ * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT.
+ * @NL80211_ATTR_PORT_AUTHORIZED: (reserved)
+ *
+ * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external
+ * authentication operation (u32 attribute with an
+ * &enum nl80211_external_auth_action value). This is used with the
+ * &NL80211_CMD_EXTERNAL_AUTH request event.
+ * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
+ * space supports external authentication. This attribute shall be used
+ * only with %NL80211_CMD_CONNECT request. The driver may offload
+ * authentication processing to user space if this capability is indicated
+ * in NL80211_CMD_CONNECT requests from the user space.
+ *
+ * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this
+ * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2500,6 +2644,21 @@
NL80211_ATTR_PMK,
+ NL80211_ATTR_SCHED_SCAN_MULTI,
+ NL80211_ATTR_SCHED_SCAN_MAX_REQS,
+
+ NL80211_ATTR_WANT_1X_4WAY_HS,
+ NL80211_ATTR_PMKR0_NAME,
+ NL80211_ATTR_PORT_AUTHORIZED,
+
+ NL80211_ATTR_EXTERNAL_AUTH_ACTION,
+ NL80211_ATTR_EXTERNAL_AUTH_SUPPORT,
+
+ NL80211_ATTR_NSS,
+ NL80211_ATTR_ACK_SIGNAL,
+
+ NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2820,6 +2979,7 @@
* @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames
* received from the station (u64, usec)
* @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm)
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
@@ -2858,6 +3018,7 @@
NL80211_STA_INFO_TID_STATS,
NL80211_STA_INFO_RX_DURATION,
NL80211_STA_INFO_PAD,
+ NL80211_STA_INFO_ACK_SIGNAL,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@@ -3184,6 +3345,7 @@
* @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
* only report BSS with matching SSID.
+ * (This cannot be used together with BSSID.)
* @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
* BSS in scan results. Filtering is turned off if not specified. Note that
* if this attribute is in a match set of its own, then it is treated as
@@ -3199,6 +3361,8 @@
* BSS-es in the specified band is to be adjusted before doing
* RSSI-based BSS selection. The attribute value is a packed structure
* value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
+ * (this cannot be used together with SSID).
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -3210,6 +3374,7 @@
NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
+ NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
/* keep last */
__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -3779,6 +3944,9 @@
* @NL80211_BSS_PARENT_BSSID. (u64).
* @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
* is set.
+ * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
+ * Contains a nested array of signal strength attributes (u8, dBm),
+ * using the nesting index as the antenna number.
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -3802,6 +3970,7 @@
NL80211_BSS_PAD,
NL80211_BSS_PARENT_TSF,
NL80211_BSS_PARENT_BSSID,
+ NL80211_BSS_CHAIN_SIGNAL,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -3878,10 +4047,12 @@
* enum nl80211_mfp - Management frame protection state
* @NL80211_MFP_NO: Management frame protection not used
* @NL80211_MFP_REQUIRED: Management frame protection required
+ * @NL80211_MFP_OPTIONAL: Management frame protection is optional
*/
enum nl80211_mfp {
NL80211_MFP_NO,
NL80211_MFP_REQUIRED,
+ NL80211_MFP_OPTIONAL,
};
enum nl80211_wpa_versions {
@@ -4838,6 +5009,35 @@
* RSSI threshold values to monitor rather than exactly one threshold.
* @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
* authentication with %NL80211_CMD_CONNECT.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way
+ * handshake with PSK in station mode (PSK is passed as part of the connect
+ * and associate commands), doing it in the host might not be supported.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way
+ * handshake with 802.1X in station mode (will pass EAP frames to the host
+ * and accept the set_pmk/del_pmk commands), doing it in the host might not
+ * be supported.
+ * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding
+ * the max channel attribute in the FILS request params IE with the
+ * actual dwell time.
+ * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe
+ * response
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending
+ * the first probe request in each channel at rate of at least 5.5Mbps.
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports
+ * probe request tx deferral and suppression
+ * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL
+ * value in %NL80211_ATTR_USE_MFP.
+ * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan.
+ * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan.
+ * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan.
+ * @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions.
+ * Device or driver will do all DFS-related actions by itself,
+ * informing user-space about CAC progress, radar detection event,
+ * channel change triggered by radar detection event.
+ * No need to start CAC from user-space, no need to react to
+ * "radar detected" event.
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and
+ * receiving control port frames over nl80211 instead of the netdevice.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4858,6 +5058,18 @@
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
NL80211_EXT_FEATURE_CQM_RSSI_LIST,
NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
+ NL80211_EXT_FEATURE_DFS_OFFLOAD,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -4918,6 +5130,10 @@
* of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
* requests.
*
+ * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and
+ * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only
+ * one of them can be used in the request.
+ *
* @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
* @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
* @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
@@ -4934,12 +5150,44 @@
* locally administered 1, multicast 0) is assumed.
* This flag must not be requested when the feature isn't supported, check
* the nl80211 feature flags for the device.
+ * @NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME: fill the dwell time in the FILS
+ * request parameters IE in the probe request
+ * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
+ * rate of at least 5.5M. In case non OCE AP is dicovered in the channel,
+ * only the first probe req in the channel will be sent in high rate.
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
+ * tx deferral (dot11FILSProbeDelay shall be set to 15ms)
+ * and suppression (if it has received a broadcast Probe Response frame,
+ * Beacon frame or FILS Discovery frame from an AP that the STA considers
+ * a suitable candidate for (re-)association - suitable in terms of
+ * SSID and/or RSSI.
+ * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to
+ * accomplish the scan. Thus, this flag intends the driver to perform the
+ * scan request with lesser span/duration. It is specific to the driver
+ * implementations on how this is accomplished. Scan accuracy may get
+ * impacted with this flag.
+ * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume
+ * optimal possible power. Drivers can resort to their specific means to
+ * optimize the power. Scan accuracy may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan
+ * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum
+ * possible scan results. This flag hints the driver to use the best
+ * possible scan configuration to improve the accuracy in scanning.
+ * Latency and power use may get impacted with this flag.
*/
enum nl80211_scan_flags {
- NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
- NL80211_SCAN_FLAG_FLUSH = 1<<1,
- NL80211_SCAN_FLAG_AP = 1<<2,
- NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+ NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
+ NL80211_SCAN_FLAG_FLUSH = 1<<1,
+ NL80211_SCAN_FLAG_AP = 1<<2,
+ NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+ NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = 1<<4,
+ NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 1<<5,
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 1<<6,
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 1<<7,
+ NL80211_SCAN_FLAG_LOW_SPAN = 1<<8,
+ NL80211_SCAN_FLAG_LOW_POWER = 1<<9,
+ NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10,
};
/**
@@ -4997,6 +5245,8 @@
* non-operating channel is expired and no longer valid. New CAC must
* be done on this channel before starting the operation. This is not
* applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
*/
enum nl80211_radar_event {
NL80211_RADAR_DETECTED,
@@ -5004,6 +5254,7 @@
NL80211_RADAR_CAC_ABORTED,
NL80211_RADAR_NOP_FINISHED,
NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
};
/**
@@ -5339,4 +5590,15 @@
NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1
};
+/**
+ * nl80211_external_auth_action - Action to perform with external
+ * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION.
+ * @NL80211_EXTERNAL_AUTH_START: Start the authentication.
+ * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication.
+ */
+enum nl80211_external_auth_action {
+ NL80211_EXTERNAL_AUTH_START,
+ NL80211_EXTERNAL_AUTH_ABORT,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index 6217468..bfe8811 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -161,7 +161,6 @@
int generator;
u8 gen;
const struct dh_group *dh;
- size_t pub_len, i;
generator = eap_eke_dh_generator(group);
dh = eap_eke_dh_group(group);
@@ -169,33 +168,11 @@
return -1;
gen = generator;
- /* x = random number 2 .. p-1 */
- if (random_get_bytes(ret_priv, dh->prime_len))
- return -1;
- if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) {
- /* Make sure private value is smaller than prime */
- ret_priv[0] = 0;
- }
- for (i = 0; i < dh->prime_len - 1; i++) {
- if (ret_priv[i])
- break;
- }
- if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1))
+ if (crypto_dh_init(gen, dh->prime, dh->prime_len, ret_priv,
+ ret_pub) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value",
ret_priv, dh->prime_len);
-
- /* y = g ^ x (mod p) */
- pub_len = dh->prime_len;
- if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len,
- dh->prime, dh->prime_len, ret_pub, &pub_len) < 0)
- return -1;
- if (pub_len < dh->prime_len) {
- size_t pad = dh->prime_len - pub_len;
- os_memmove(ret_pub + pad, ret_pub, pub_len);
- os_memset(ret_pub, 0, pad);
- }
-
wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value",
ret_pub, dh->prime_len);
@@ -421,8 +398,9 @@
/* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */
len = dh->prime_len;
- if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len,
- dh->prime, dh->prime_len, modexp, &len) < 0)
+ if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+ dhpriv, dh->prime_len, peer_pub,
+ dh->prime_len, modexp, &len) < 0)
return -1;
if (len < dh->prime_len) {
size_t pad = dh->prime_len - len;
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 67f8f70..88c6595 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -81,6 +81,27 @@
}
+EAP_PWD_group * get_eap_pwd_group(u16 num)
+{
+ EAP_PWD_group *grp;
+
+ grp = os_zalloc(sizeof(EAP_PWD_group));
+ if (!grp)
+ return NULL;
+ grp->group = crypto_ec_init(num);
+ if (!grp->group) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC group");
+ os_free(grp);
+ return NULL;
+ }
+
+ grp->group_num = num;
+ wpa_printf(MSG_INFO, "EAP-pwd: provisioned group %d", num);
+
+ return grp;
+}
+
+
/*
* compute a "random" secret point on an elliptic curve based
* on the password and identities.
@@ -91,105 +112,71 @@
const u8 *id_peer, size_t id_peer_len,
const u8 *token)
{
- BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
+ struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
struct crypto_hash *hash;
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
- int nid, is_odd, ret = 0;
+ int is_odd, ret = 0, check, found = 0;
size_t primebytelen, primebitlen;
+ struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ const struct crypto_bignum *prime;
- switch (num) { /* from IANA registry for IKE D-H groups */
- case 19:
- nid = NID_X9_62_prime256v1;
- break;
- case 20:
- nid = NID_secp384r1;
- break;
- case 21:
- nid = NID_secp521r1;
- break;
-#ifndef OPENSSL_IS_BORINGSSL
- case 25:
- nid = NID_X9_62_prime192v1;
- break;
-#endif /* OPENSSL_IS_BORINGSSL */
- case 26:
- nid = NID_secp224r1;
- break;
-#ifdef NID_brainpoolP224r1
- case 27:
- nid = NID_brainpoolP224r1;
- break;
-#endif /* NID_brainpoolP224r1 */
-#ifdef NID_brainpoolP256r1
- case 28:
- nid = NID_brainpoolP256r1;
- break;
-#endif /* NID_brainpoolP256r1 */
-#ifdef NID_brainpoolP384r1
- case 29:
- nid = NID_brainpoolP384r1;
- break;
-#endif /* NID_brainpoolP384r1 */
-#ifdef NID_brainpoolP512r1
- case 30:
- nid = NID_brainpoolP512r1;
- break;
-#endif /* NID_brainpoolP512r1 */
- default:
- wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
+ if (grp->pwe)
return -1;
- }
- grp->pwe = NULL;
- grp->order = NULL;
- grp->prime = NULL;
-
- if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
- goto fail;
- }
-
- if (((rnd = BN_new()) == NULL) ||
- ((cofactor = BN_new()) == NULL) ||
- ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
- ((grp->order = BN_new()) == NULL) ||
- ((grp->prime = BN_new()) == NULL) ||
- ((x_candidate = BN_new()) == NULL)) {
+ prime = crypto_ec_get_prime(grp->group);
+ cofactor = crypto_bignum_init();
+ grp->pwe = crypto_ec_point_init(grp->group);
+ tmp1 = crypto_bignum_init();
+ pm1 = crypto_bignum_init();
+ one = crypto_bignum_init_set((const u8 *) "\x01", 1);
+ if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
goto fail;
}
- if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
- {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
- "curve");
- goto fail;
- }
- if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
- goto fail;
- }
- if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
+ if (crypto_ec_cofactor(grp->group, cofactor) < 0) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
"curve");
goto fail;
}
- primebitlen = BN_num_bits(grp->prime);
- primebytelen = BN_num_bytes(grp->prime);
+ primebitlen = crypto_ec_prime_len_bits(grp->group);
+ primebytelen = crypto_ec_prime_len(grp->group);
if ((prfbuf = os_malloc(primebytelen)) == NULL) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
"buffer");
goto fail;
}
+ if (crypto_bignum_sub(prime, one, pm1) < 0)
+ goto fail;
+
+ /* get a random quadratic residue and nonresidue */
+ while (!qr || !qnr) {
+ int res;
+
+ if (crypto_bignum_rand(tmp1, prime) < 0)
+ goto fail;
+ res = crypto_bignum_legendre(tmp1, prime);
+ if (!qr && res == 1) {
+ qr = tmp1;
+ tmp1 = crypto_bignum_init();
+ } else if (!qnr && res == -1) {
+ qnr = tmp1;
+ tmp1 = crypto_bignum_init();
+ }
+ if (!tmp1)
+ goto fail;
+ }
+
os_memset(prfbuf, 0, primebytelen);
ctr = 0;
- while (1) {
- if (ctr > 30) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
- "point on curve for group %d, something's "
- "fishy", num);
- goto fail;
- }
+
+ /*
+ * Run through the hunting-and-pecking loop 40 times to mask the time
+ * necessary to find PWE. The odds of PWE not being found in 40 loops is
+ * roughly 1 in 1 trillion.
+ */
+ while (ctr < 40) {
ctr++;
/*
@@ -207,15 +194,25 @@
eap_pwd_h_update(hash, &ctr, sizeof(ctr));
eap_pwd_h_final(hash, pwe_digest);
- BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
-
+ crypto_bignum_deinit(rnd, 1);
+ rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN);
+ if (!rnd) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd");
+ goto fail;
+ }
if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
(u8 *) "EAP-pwd Hunting And Pecking",
os_strlen("EAP-pwd Hunting And Pecking"),
prfbuf, primebitlen) < 0)
goto fail;
- BN_bin2bn(prfbuf, primebytelen, x_candidate);
+ crypto_bignum_deinit(x_candidate, 1);
+ x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
+ if (!x_candidate) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: unable to create x_candidate");
+ goto fail;
+ }
/*
* eap_pwd_kdf() returns a string of bits 0..primebitlen but
@@ -224,97 +221,157 @@
* then excessive bits-- those _after_ primebitlen-- so now
* we have to shift right the amount we masked off.
*/
- if (primebitlen % 8)
- BN_rshift(x_candidate, x_candidate,
- (8 - (primebitlen % 8)));
+ if ((primebitlen % 8) &&
+ crypto_bignum_rshift(x_candidate,
+ (8 - (primebitlen % 8)),
+ x_candidate) < 0)
+ goto fail;
- if (BN_ucmp(x_candidate, grp->prime) >= 0)
+ if (crypto_bignum_cmp(x_candidate, prime) >= 0)
continue;
wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
prfbuf, primebytelen);
/*
- * need to unambiguously identify the solution, if there is
- * one...
+ * compute y^2 using the equation of the curve
+ *
+ * y^2 = x^3 + ax + b
*/
- if (BN_is_odd(rnd))
- is_odd = 1;
- else
- is_odd = 0;
+ tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
+ if (!tmp2)
+ goto fail;
/*
- * solve the quadratic equation, if it's not solvable then we
- * don't have a point
+ * mask tmp2 so doing legendre won't leak timing info
+ *
+ * tmp1 is a random number between 1 and p-1
*/
- if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
- grp->pwe,
- x_candidate,
- is_odd, NULL))
- continue;
+ if (crypto_bignum_rand(tmp1, pm1) < 0 ||
+ crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 ||
+ crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0)
+ goto fail;
+
/*
- * If there's a solution to the equation then the point must be
- * on the curve so why check again explicitly? OpenSSL code
- * says this is required by X9.62. We're not X9.62 but it can't
- * hurt just to be sure.
+ * Now tmp2 (y^2) is masked, all values between 1 and p-1
+ * are equally probable. Multiplying by r^2 does not change
+ * whether or not tmp2 is a quadratic residue, just masks it.
+ *
+ * Flip a coin, multiply by the random quadratic residue or the
+ * random quadratic nonresidue and record heads or tails.
*/
- if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
- wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
- continue;
+ if (crypto_bignum_is_odd(tmp1)) {
+ crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
+ check = 1;
+ } else {
+ crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
+ check = -1;
}
- if (BN_cmp(cofactor, BN_value_one())) {
- /* make sure the point is not in a small sub-group */
- if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
- cofactor, NULL)) {
- wpa_printf(MSG_INFO, "EAP-pwd: cannot "
- "multiply generator by order");
+ /*
+ * Now it's safe to do legendre, if check is 1 then it's
+ * a straightforward test (multiplying by qr does not
+ * change result), if check is -1 then it's the opposite test
+ * (multiplying a qr by qnr would make a qnr).
+ */
+ if (crypto_bignum_legendre(tmp2, prime) == check) {
+ if (found == 1)
+ continue;
+
+ /* need to unambiguously identify the solution */
+ is_odd = crypto_bignum_is_odd(rnd);
+
+ /*
+ * We know x_candidate is a quadratic residue so set
+ * it here.
+ */
+ if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
+ x_candidate,
+ is_odd) != 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Could not solve for y");
continue;
}
- if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
- wpa_printf(MSG_INFO, "EAP-pwd: point is at "
- "infinity");
+
+ /*
+ * If there's a solution to the equation then the point
+ * must be on the curve so why check again explicitly?
+ * OpenSSL code says this is required by X9.62. We're
+ * not X9.62 but it can't hurt just to be sure.
+ */
+ if (!crypto_ec_point_is_on_curve(grp->group,
+ grp->pwe)) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: point is not on curve");
continue;
}
+
+ if (!crypto_bignum_is_one(cofactor)) {
+ /* make sure the point is not in a small
+ * sub-group */
+ if (crypto_ec_point_mul(grp->group, grp->pwe,
+ cofactor,
+ grp->pwe) != 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: cannot multiply generator by order");
+ continue;
+ }
+ if (crypto_ec_point_is_at_infinity(grp->group,
+ grp->pwe)) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: point is at infinity");
+ continue;
+ }
+ }
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd: found a PWE in %d tries", ctr);
+ found = 1;
}
- /* if we got here then we have a new generator. */
- break;
}
- wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
- grp->group_num = num;
+ if (found == 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: unable to find random point on curve for group %d, something's fishy",
+ num);
+ goto fail;
+ }
if (0) {
fail:
- EC_GROUP_free(grp->group);
- grp->group = NULL;
- EC_POINT_clear_free(grp->pwe);
+ crypto_ec_point_deinit(grp->pwe, 1);
grp->pwe = NULL;
- BN_clear_free(grp->order);
- grp->order = NULL;
- BN_clear_free(grp->prime);
- grp->prime = NULL;
ret = 1;
}
/* cleanliness and order.... */
- BN_clear_free(cofactor);
- BN_clear_free(x_candidate);
- BN_clear_free(rnd);
+ crypto_bignum_deinit(cofactor, 1);
+ crypto_bignum_deinit(x_candidate, 1);
+ crypto_bignum_deinit(rnd, 1);
+ crypto_bignum_deinit(pm1, 0);
+ crypto_bignum_deinit(tmp1, 1);
+ crypto_bignum_deinit(tmp2, 1);
+ crypto_bignum_deinit(qr, 1);
+ crypto_bignum_deinit(qnr, 1);
+ crypto_bignum_deinit(one, 0);
os_free(prfbuf);
return ret;
}
-int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
- const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
+int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
+ const struct crypto_bignum *peer_scalar,
+ const struct crypto_bignum *server_scalar,
const u8 *confirm_peer, const u8 *confirm_server,
const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
{
struct crypto_hash *hash;
u8 mk[SHA256_MAC_LEN], *cruft;
u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
- int offset;
+ size_t prime_len, order_len;
- if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
+ prime_len = crypto_ec_prime_len(grp->group);
+ order_len = crypto_ec_order_len(grp->group);
+
+ cruft = os_malloc(prime_len);
+ if (!cruft)
return -1;
/*
@@ -328,14 +385,10 @@
return -1;
}
eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
- offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
- os_memset(cruft, 0, BN_num_bytes(grp->prime));
- BN_bn2bin(peer_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
- offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
- os_memset(cruft, 0, BN_num_bytes(grp->prime));
- BN_bn2bin(server_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
+ crypto_bignum_to_bin(peer_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
+ crypto_bignum_to_bin(server_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
eap_pwd_h_final(hash, &session_id[1]);
/* then compute MK = H(k | confirm-peer | confirm-server) */
@@ -344,10 +397,8 @@
os_free(cruft);
return -1;
}
- offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
- os_memset(cruft, 0, BN_num_bytes(grp->prime));
- BN_bn2bin(k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
+ crypto_bignum_to_bin(k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
os_free(cruft);
eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index a0d717e..6b07cf8 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -9,20 +9,14 @@
#ifndef EAP_PWD_COMMON_H
#define EAP_PWD_COMMON_H
-#include <openssl/bn.h>
-#include <openssl/ec.h>
-#include <openssl/evp.h>
-
/*
* definition of a finite cyclic group
* TODO: support one based on a prime field
*/
typedef struct group_definition_ {
u16 group_num;
- EC_GROUP *group;
- EC_POINT *pwe;
- BIGNUM *order;
- BIGNUM *prime;
+ struct crypto_ec *group;
+ struct crypto_ec_point *pwe;
} EAP_PWD_group;
/*
@@ -52,17 +46,22 @@
u8 prep;
#define EAP_PWD_PREP_NONE 0
#define EAP_PWD_PREP_MS 1
+#define EAP_PWD_PREP_SSHA1 3
+#define EAP_PWD_PREP_SSHA256 4
+#define EAP_PWD_PREP_SSHA512 5
u8 identity[0]; /* length inferred from payload */
} STRUCT_PACKED;
/* common routines */
+EAP_PWD_group * get_eap_pwd_group(u16 num);
int compute_password_element(EAP_PWD_group *grp, u16 num,
const u8 *password, size_t password_len,
const u8 *id_server, size_t id_server_len,
const u8 *id_peer, size_t id_peer_len,
const u8 *token);
-int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
- const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
+int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
+ const struct crypto_bignum *peer_scalar,
+ const struct crypto_bignum *server_scalar,
const u8 *confirm_peer, const u8 *confirm_server,
const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id);
struct crypto_hash * eap_pwd_h_init(void);
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 52b0491..e55e2d5 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -94,6 +94,7 @@
sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter);
}
+
static void eap_report_error(struct eap_sm *sm, int error_code)
{
wpa_printf(MSG_DEBUG, "EAP: Error notification: %d", error_code);
@@ -101,6 +102,7 @@
sm->eapol_cb->notify_eap_error(sm->eapol_ctx, error_code);
}
+
static void eap_sm_free_key(struct eap_sm *sm)
{
if (sm->eapKeyData) {
@@ -1940,7 +1942,6 @@
const struct eap_hdr *hdr;
size_t plen;
const u8 *pos;
- int error_code;
sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE;
sm->reqId = 0;
@@ -2028,6 +2029,8 @@
/* Get the error code from method */
if (sm->m && sm->m->get_error_code) {
+ int error_code;
+
error_code = sm->m->get_error_code(sm->eap_method_priv);
if (error_code != NO_EAP_METHOD_ERROR)
eap_report_error(sm, error_code);
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 679f101..a444141 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -1528,14 +1528,16 @@
return key;
}
+
static int eap_aka_get_error_code(void *priv)
{
struct eap_aka_data *data = priv;
+ int current_data_error;
if (!data)
return NO_EAP_METHOD_ERROR;
- int current_data_error = data->error_code;
+ current_data_error = data->error_code;
/* Now reset for next transaction */
data->error_code = NO_EAP_METHOD_ERROR;
@@ -1543,6 +1545,7 @@
return current_data_error;
}
+
int eap_peer_aka_register(void)
{
struct eap_method *eap;
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 5b38969..096f0f2 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -208,10 +208,10 @@
const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
/**
- * get_error_code - Get latest EAP method error code
+ * get_error_code - Get the latest EAP method error code
* @priv: Pointer to private EAP method data from eap_method::init()
- * Returns: An int for the EAP Method Error code if exists or
- * NO_EAP_METHOD_ERROR otherwise
+ * Returns: An int for the EAP method specific error code if exists or
+ * NO_EAP_METHOD_ERROR otherwise.
*
* This method is an optional handler that only EAP methods that need to
* report their error code need to implement.
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index ec277ac..90ac3cf 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -9,8 +9,11 @@
#include "includes.h"
#include "common.h"
+#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/sha512.h"
#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
#include "eap_peer/eap_i.h"
#include "eap_common/eap_pwd_common.h"
@@ -28,6 +31,8 @@
size_t password_len;
int password_hash;
u16 group_num;
+ u8 prep;
+ u8 token[4];
EAP_PWD_group *grp;
struct wpabuf *inbuf;
@@ -36,18 +41,16 @@
size_t out_frag_pos;
size_t mtu;
- BIGNUM *k;
- BIGNUM *private_value;
- BIGNUM *server_scalar;
- BIGNUM *my_scalar;
- EC_POINT *my_element;
- EC_POINT *server_element;
+ struct crypto_bignum *k;
+ struct crypto_bignum *private_value;
+ struct crypto_bignum *server_scalar;
+ struct crypto_bignum *my_scalar;
+ struct crypto_ec_point *my_element;
+ struct crypto_ec_point *server_element;
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 session_id[1 + SHA256_MAC_LEN];
-
- BN_CTX *bnctx;
};
@@ -107,15 +110,8 @@
return NULL;
}
- if ((data->bnctx = BN_CTX_new()) == NULL) {
- wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
- os_free(data);
- return NULL;
- }
-
if ((data->id_peer = os_malloc(identity_len)) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
- BN_CTX_free(data->bnctx);
os_free(data);
return NULL;
}
@@ -125,7 +121,6 @@
if ((data->password = os_malloc(password_len)) == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
- BN_CTX_free(data->bnctx);
bin_clear_free(data->id_peer, data->id_peer_len);
os_free(data);
return NULL;
@@ -152,21 +147,18 @@
{
struct eap_pwd_data *data = priv;
- BN_clear_free(data->private_value);
- BN_clear_free(data->server_scalar);
- BN_clear_free(data->my_scalar);
- BN_clear_free(data->k);
- BN_CTX_free(data->bnctx);
- EC_POINT_clear_free(data->my_element);
- EC_POINT_clear_free(data->server_element);
+ crypto_bignum_deinit(data->private_value, 1);
+ crypto_bignum_deinit(data->server_scalar, 1);
+ crypto_bignum_deinit(data->my_scalar, 1);
+ crypto_bignum_deinit(data->k, 1);
+ crypto_ec_point_deinit(data->my_element, 1);
+ crypto_ec_point_deinit(data->server_element, 1);
bin_clear_free(data->id_peer, data->id_peer_len);
bin_clear_free(data->id_server, data->id_server_len);
bin_clear_free(data->password, data->password_len);
if (data->grp) {
- EC_GROUP_free(data->grp->group);
- EC_POINT_clear_free(data->grp->pwe);
- BN_clear_free(data->grp->order);
- BN_clear_free(data->grp->prime);
+ crypto_ec_deinit(data->grp->group);
+ crypto_ec_point_deinit(data->grp->pwe, 1);
os_free(data->grp);
}
wpabuf_free(data->inbuf);
@@ -218,10 +210,6 @@
const u8 *payload, size_t payload_len)
{
struct eap_pwd_id *id;
- const u8 *password;
- size_t password_len;
- u8 pwhashhash[16];
- int res;
if (data->state != PWD_ID_Req) {
ret->ignore = TRUE;
@@ -248,7 +236,10 @@
}
if (id->prep != EAP_PWD_PREP_NONE &&
- id->prep != EAP_PWD_PREP_MS) {
+ id->prep != EAP_PWD_PREP_MS &&
+ id->prep != EAP_PWD_PREP_SSHA1 &&
+ id->prep != EAP_PWD_PREP_SSHA256 &&
+ id->prep != EAP_PWD_PREP_SSHA512) {
wpa_printf(MSG_DEBUG,
"EAP-PWD: Unsupported password pre-processing technique (Prep=%u)",
id->prep);
@@ -266,6 +257,15 @@
wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
data->group_num);
+ data->prep = id->prep;
+ os_memcpy(data->token, id->token, sizeof(id->token));
+
+ if (data->id_server || data->grp) {
+ wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
if (data->id_server == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
@@ -277,7 +277,7 @@
wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
data->id_server, data->id_server_len);
- data->grp = os_zalloc(sizeof(EAP_PWD_group));
+ data->grp = get_eap_pwd_group(data->group_num);
if (data->grp == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
"group");
@@ -285,54 +285,6 @@
return;
}
- if (id->prep == EAP_PWD_PREP_MS) {
-#ifdef CONFIG_FIPS
- wpa_printf(MSG_ERROR,
- "EAP-PWD (peer): MS password hash not supported in FIPS mode");
- eap_pwd_state(data, FAILURE);
- return;
-#else /* CONFIG_FIPS */
- if (data->password_hash) {
- res = hash_nt_password_hash(data->password, pwhashhash);
- } else {
- u8 pwhash[16];
-
- res = nt_password_hash(data->password,
- data->password_len, pwhash);
- if (res == 0)
- res = hash_nt_password_hash(pwhash, pwhashhash);
- os_memset(pwhash, 0, sizeof(pwhash));
- }
-
- if (res) {
- eap_pwd_state(data, FAILURE);
- return;
- }
-
- password = pwhashhash;
- password_len = sizeof(pwhashhash);
-#endif /* CONFIG_FIPS */
- } else {
- password = data->password;
- password_len = data->password_len;
- }
-
- /* compute PWE */
- res = compute_password_element(data->grp, data->group_num,
- password, password_len,
- data->id_server, data->id_server_len,
- data->id_peer, data->id_peer_len,
- id->token);
- os_memset(pwhashhash, 0, sizeof(pwhashhash));
- if (res) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
- eap_pwd_state(data, FAILURE);
- return;
- }
-
- wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
- BN_num_bits(data->grp->prime));
-
data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
data->id_peer_len);
if (data->outbuf == NULL) {
@@ -356,109 +308,301 @@
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
- EC_POINT *K = NULL, *point = NULL;
- BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
- u16 offset;
- u8 *ptr, *scalar = NULL, *element = NULL;
+ struct crypto_ec_point *K = NULL, *point = NULL;
+ struct crypto_bignum *mask = NULL, *cofactor = NULL;
+ const u8 *ptr = payload;
+ u8 *scalar = NULL, *element = NULL;
size_t prime_len, order_len;
+ const u8 *password;
+ size_t password_len;
+ u8 pwhashhash[16];
+ const u8 *salt_pwd[2];
+ size_t salt_pwd_len[2], exp_len;
+ u8 salt_len, salthashpwd[64]; /* 64 = SHA512_DIGEST_LENGTH */
+ int res;
if (data->state != PWD_Commit_Req) {
ret->ignore = TRUE;
goto fin;
}
- prime_len = BN_num_bytes(data->grp->prime);
- order_len = BN_num_bytes(data->grp->order);
-
- if (payload_len != 2 * prime_len + order_len) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
- (unsigned int) payload_len,
- (unsigned int) (2 * prime_len + order_len));
+ if (!data->grp) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-PWD (client): uninitialized EAP-pwd group");
+ ret->ignore = TRUE;
goto fin;
}
- if (((data->private_value = BN_new()) == NULL) ||
- ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
- ((cofactor = BN_new()) == NULL) ||
- ((data->my_scalar = BN_new()) == NULL) ||
- ((mask = BN_new()) == NULL)) {
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
+
+ switch (data->prep) {
+ case EAP_PWD_PREP_MS:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is MS");
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR,
+ "EAP-PWD (peer): MS password hash not supported in FIPS mode");
+ eap_pwd_state(data, FAILURE);
+ return;
+#else /* CONFIG_FIPS */
+ if (payload_len != 2 * prime_len + order_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) (2 * prime_len + order_len));
+ goto fin;
+ }
+ if (data->password_hash) {
+ res = hash_nt_password_hash(data->password, pwhashhash);
+ } else {
+ u8 pwhash[16];
+
+ res = nt_password_hash(data->password,
+ data->password_len, pwhash);
+ if (res == 0)
+ res = hash_nt_password_hash(pwhash, pwhashhash);
+ os_memset(pwhash, 0, sizeof(pwhash));
+ }
+
+ if (res) {
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ password = pwhashhash;
+ password_len = sizeof(pwhashhash);
+#endif /* CONFIG_FIPS */
+ break;
+ case EAP_PWD_PREP_SSHA1:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is salted sha1");
+ if (payload_len < 1 || *ptr == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len");
+ goto fin;
+ }
+ salt_len = *ptr++;
+ exp_len = 1 + salt_len + 2 * prime_len + order_len;
+ if (payload_len != exp_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) exp_len);
+ goto fin;
+ }
+
+ /* salted-password = Hash(password | salt) */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password",
+ data->password, data->password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len);
+ salt_pwd[0] = data->password;
+ salt_pwd[1] = ptr;
+ salt_pwd_len[0] = data->password_len;
+ salt_pwd_len[1] = salt_len;
+ if (sha1_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0)
+ goto fin;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd: sha1 hashed %d byte salt with password",
+ (int) salt_len);
+ ptr += salt_len;
+ password = salthashpwd;
+ password_len = SHA1_MAC_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password",
+ password, password_len);
+ break;
+ case EAP_PWD_PREP_SSHA256:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is salted sha256");
+ if (payload_len < 1 || *ptr == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len");
+ goto fin;
+ }
+ salt_len = *ptr++;
+ exp_len = 1 + salt_len + 2 * prime_len + order_len;
+ if (payload_len != exp_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) exp_len);
+ goto fin;
+ }
+
+ /* salted-password = Hash(password | salt) */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password",
+ data->password, data->password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len);
+ salt_pwd[0] = data->password;
+ salt_pwd[1] = ptr;
+ salt_pwd_len[0] = data->password_len;
+ salt_pwd_len[1] = salt_len;
+ if (sha256_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0)
+ goto fin;
+
+ ptr += salt_len;
+ password = salthashpwd;
+ password_len = SHA256_MAC_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password",
+ password, password_len);
+ break;
+#ifdef CONFIG_SHA512
+ case EAP_PWD_PREP_SSHA512:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is salted sha512");
+ if (payload_len < 1 || *ptr == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len");
+ goto fin;
+ }
+ salt_len = *ptr++;
+ exp_len = 1 + salt_len + 2 * prime_len + order_len;
+ if (payload_len != exp_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) exp_len);
+ goto fin;
+ }
+
+ /* salted-password = Hash(password | salt) */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password",
+ data->password, data->password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len);
+ salt_pwd[0] = data->password;
+ salt_pwd[1] = ptr;
+ salt_pwd_len[0] = data->password_len;
+ salt_pwd_len[1] = salt_len;
+ if (sha512_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0)
+ goto fin;
+
+ ptr += salt_len;
+ password = salthashpwd;
+ password_len = SHA512_MAC_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password",
+ password, password_len);
+ break;
+#endif /* CONFIG_SHA512 */
+ case EAP_PWD_PREP_NONE:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd commit request, password prep is NONE");
+ if (data->password_hash) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-PWD: Unhashed password not available");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ if (payload_len != 2 * prime_len + order_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+ (unsigned int) payload_len,
+ (unsigned int) (2 * prime_len + order_len));
+ goto fin;
+ }
+ password = data->password;
+ password_len = data->password_len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd: Unsupported password pre-processing technique (Prep=%u)",
+ data->prep);
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ /* compute PWE */
+ res = compute_password_element(data->grp, data->group_num,
+ password, password_len,
+ data->id_server, data->id_server_len,
+ data->id_peer, data->id_peer_len,
+ data->token);
+ os_memset(pwhashhash, 0, sizeof(pwhashhash));
+ os_memset(salthashpwd, 0, sizeof(salthashpwd));
+ if (res) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
+ (int) crypto_ec_prime_len_bits(data->grp->group));
+
+ data->private_value = crypto_bignum_init();
+ data->my_element = crypto_ec_point_init(data->grp->group);
+ cofactor = crypto_bignum_init();
+ data->my_scalar = crypto_bignum_init();
+ mask = crypto_bignum_init();
+ if (!data->private_value || !data->my_element || !cofactor ||
+ !data->my_scalar || !mask) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
goto fin;
}
- if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+ if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) {
wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
"for curve");
goto fin;
}
- if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
- BN_rand_range(mask, data->grp->order) != 1 ||
- BN_add(data->my_scalar, data->private_value, mask) != 1 ||
- BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
- data->bnctx) != 1) {
+ if (crypto_bignum_rand(data->private_value,
+ crypto_ec_get_order(data->grp->group)) < 0 ||
+ crypto_bignum_rand(mask,
+ crypto_ec_get_order(data->grp->group)) < 0 ||
+ crypto_bignum_add(data->private_value, mask,
+ data->my_scalar) < 0 ||
+ crypto_bignum_mod(data->my_scalar,
+ crypto_ec_get_order(data->grp->group),
+ data->my_scalar) < 0) {
wpa_printf(MSG_INFO,
"EAP-pwd (peer): unable to get randomness");
goto fin;
}
- if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
- data->grp->pwe, mask, data->bnctx)) {
+ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
+ data->my_element) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
"fail");
eap_pwd_state(data, FAILURE);
goto fin;
}
- if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
- {
+ if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
goto fin;
}
- if (((x = BN_new()) == NULL) ||
- ((y = BN_new()) == NULL)) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
- goto fin;
- }
-
/* process the request */
- if (((data->server_scalar = BN_new()) == NULL) ||
- ((data->k = BN_new()) == NULL) ||
- ((K = EC_POINT_new(data->grp->group)) == NULL) ||
- ((point = EC_POINT_new(data->grp->group)) == NULL) ||
- ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
- {
+ data->k = crypto_bignum_init();
+ K = crypto_ec_point_init(data->grp->group);
+ point = crypto_ec_point_init(data->grp->group);
+ if (!data->k || !K || !point) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
"fail");
goto fin;
}
/* element, x then y, followed by scalar */
- ptr = (u8 *) payload;
- BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
- ptr += BN_num_bytes(data->grp->prime);
- BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
- ptr += BN_num_bytes(data->grp->prime);
- BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
- if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
- data->server_element, x, y,
- data->bnctx)) {
+ data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+ if (!data->server_element) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
"fail");
goto fin;
}
+ ptr += prime_len * 2;
+ data->server_scalar = crypto_bignum_init_set(ptr, order_len);
+ if (!data->server_scalar) {
+ wpa_printf(MSG_INFO,
+ "EAP-PWD (peer): setting peer scalar fail");
+ goto fin;
+ }
/* check to ensure server's element is not in a small sub-group */
- if (BN_cmp(cofactor, BN_value_one())) {
- if (!EC_POINT_mul(data->grp->group, point, NULL,
- data->server_element, cofactor, NULL)) {
+ if (!crypto_bignum_is_one(cofactor)) {
+ if (crypto_ec_point_mul(data->grp->group, data->server_element,
+ cofactor, point) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
"server element by order!\n");
goto fin;
}
- if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+ if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
"is at infinity!\n");
goto fin;
@@ -466,21 +610,20 @@
}
/* compute the shared key, k */
- if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
- data->server_scalar, data->bnctx)) ||
- (!EC_POINT_add(data->grp->group, K, K, data->server_element,
- data->bnctx)) ||
- (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
- data->bnctx))) {
+ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe,
+ data->server_scalar, K) < 0 ||
+ crypto_ec_point_add(data->grp->group, K, data->server_element,
+ K) < 0 ||
+ crypto_ec_point_mul(data->grp->group, K, data->private_value,
+ K) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
"fail");
goto fin;
}
/* ensure that the shared key isn't in a small sub-group */
- if (BN_cmp(cofactor, BN_value_one())) {
- if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
- NULL)) {
+ if (!crypto_bignum_is_one(cofactor)) {
+ if (crypto_ec_point_mul(data->grp->group, K, cofactor, K) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
"shared key point by order");
goto fin;
@@ -493,30 +636,22 @@
* never going to happen it is a simple and safe check "just to be
* sure" so let's be safe.
*/
- if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+ if (crypto_ec_point_is_at_infinity(data->grp->group, K)) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
"infinity!\n");
goto fin;
}
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
- NULL, data->bnctx)) {
+ if (crypto_ec_point_x(data->grp->group, K, data->k) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
"shared secret from point");
goto fin;
}
/* now do the response */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
- goto fin;
- }
-
- if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
- ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
- NULL)) {
+ scalar = os_zalloc(order_len);
+ element = os_zalloc(prime_len * 2);
+ if (!scalar || !element) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
goto fin;
}
@@ -526,36 +661,28 @@
* sufficiently smaller than the prime or order might need pre-pending
* with zeros.
*/
- os_memset(scalar, 0, BN_num_bytes(data->grp->order));
- os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, scalar + offset);
+ crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
+ element + prime_len) != 0) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
+ goto fin;
+ }
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, element + offset);
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
-
- data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) +
- 2 * BN_num_bytes(data->grp->prime));
+ data->outbuf = wpabuf_alloc(order_len + 2 * prime_len);
if (data->outbuf == NULL)
goto fin;
/* we send the element as (x,y) follwed by the scalar */
- wpabuf_put_data(data->outbuf, element,
- 2 * BN_num_bytes(data->grp->prime));
- wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
+ wpabuf_put_data(data->outbuf, element, 2 * prime_len);
+ wpabuf_put_data(data->outbuf, scalar, order_len);
fin:
os_free(scalar);
os_free(element);
- BN_clear_free(x);
- BN_clear_free(y);
- BN_clear_free(mask);
- BN_clear_free(cofactor);
- EC_POINT_clear_free(K);
- EC_POINT_clear_free(point);
+ crypto_bignum_deinit(mask, 1);
+ crypto_bignum_deinit(cofactor, 1);
+ crypto_ec_point_deinit(K, 1);
+ crypto_ec_point_deinit(point, 1);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
else
@@ -569,12 +696,11 @@
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
- BIGNUM *x = NULL, *y = NULL;
struct crypto_hash *hash;
u32 cs;
u16 grp;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
- int offset;
+ size_t prime_len = 0, order_len = 0;
if (data->state != PWD_Confirm_Req) {
ret->ignore = TRUE;
@@ -588,6 +714,9 @@
goto fin;
}
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
+
/*
* first build up the ciphersuite which is group | random_function |
* prf
@@ -600,9 +729,9 @@
ptr += sizeof(u8);
*ptr = EAP_PWD_DEFAULT_PRF;
- /* each component of the cruft will be at most as big as the prime */
- if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
- ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ /* each component of the point will be at most as big as the prime */
+ cruft = os_malloc(prime_len * 2);
+ if (!cruft) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
"fail");
goto fin;
@@ -620,59 +749,34 @@
* zero the memory each time because this is mod prime math and some
* value may start with a few zeros and the previous one did not.
*/
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
- BN_bn2bin(data->k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
/* server element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->server_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->server_element,
+ cruft, cruft + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->server_scalar);
- BN_bn2bin(data->server_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* my element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+ cruft + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
-
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* my scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* the ciphersuite */
eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
@@ -698,58 +802,34 @@
goto fin;
/* k */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
- BN_bn2bin(data->k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
/* my element */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+ cruft + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
"assignment fail");
goto fin;
}
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* my scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* server element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->server_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->server_element,
+ cruft, cruft + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
"assignment fail");
goto fin;
}
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->server_scalar);
- BN_bn2bin(data->server_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* the ciphersuite */
eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
@@ -757,7 +837,7 @@
/* all done */
eap_pwd_h_final(hash, conf);
- if (compute_keys(data->grp, data->bnctx, data->k,
+ if (compute_keys(data->grp, data->k,
data->my_scalar, data->server_scalar, conf, ptr,
&cs, data->msk, data->emsk, data->session_id) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
@@ -772,10 +852,7 @@
wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
fin:
- if (data->grp)
- bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
- BN_clear_free(x);
- BN_clear_free(y);
+ bin_clear_free(cruft, prime_len * 2);
if (data->outbuf == NULL) {
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index c0896aa..ba5eea9 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -1248,14 +1248,16 @@
return key;
}
+
static int eap_sim_get_error_code(void *priv)
{
struct eap_sim_data *data = priv;
+ int current_data_error;
if (!data)
return NO_EAP_METHOD_ERROR;
- int current_data_error = data->error_code;
+ current_data_error = data->error_code;
/* Now reset for next transaction */
data->error_code = NO_EAP_METHOD_ERROR;
@@ -1263,6 +1265,7 @@
return current_data_error;
}
+
int eap_peer_sim_register(void)
{
struct eap_method *eap;
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index c1820a4..cb74702 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -173,14 +173,31 @@
static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
struct eap_method_ret *ret)
{
+ const char *label;
+
wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_UNCOND_SUCC;
+ if (data->ssl.tls_out) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Fragment(s) remaining");
+ return;
+ }
+
+ if (data->ssl.tls_v13) {
+ label = "EXPORTER_EAP_TLS_Key_Material";
+
+ /* A possible NewSessionTicket may be received before
+ * EAP-Success, so need to allow it to be received. */
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ label = "client EAP encryption";
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
eap_tls_free_key(data);
- data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
- "client EAP encryption",
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label,
EAP_TLS_KEY_LEN +
EAP_EMSK_LEN);
if (data->key_data) {
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index b3d4aba..0de1315 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -80,6 +80,10 @@
params->flags |= TLS_CONN_DISABLE_TLSv1_2;
if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
+ if (os_strstr(txt, "tls_disable_tlsv1_3=1"))
+ params->flags |= TLS_CONN_DISABLE_TLSv1_3;
+ if (os_strstr(txt, "tls_disable_tlsv1_3=0"))
+ params->flags &= ~TLS_CONN_DISABLE_TLSv1_3;
if (os_strstr(txt, "tls_ext_cert_check=1"))
params->flags |= TLS_CONN_EXT_CERT_CHECK;
if (os_strstr(txt, "tls_ext_cert_check=0"))
@@ -159,6 +163,23 @@
*/
params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
}
+ if (data->eap_type == EAP_TYPE_FAST ||
+ data->eap_type == EAP_TYPE_TTLS ||
+ data->eap_type == EAP_TYPE_PEAP) {
+ /* The current EAP peer implementation is not yet ready for the
+ * TLS v1.3 changes, so disable this by default for now. */
+ params->flags |= TLS_CONN_DISABLE_TLSv1_3;
+ }
+ if (data->eap_type == EAP_TYPE_TLS) {
+ /* While the current EAP-TLS implementation is more or less
+ * complete for TLS v1.3, there has been no interoperability
+ * testing with other implementations, so disable for by default
+ * for now until there has been chance to confirm that no
+ * significant interoperability issues show up with TLS version
+ * update.
+ */
+ params->flags |= TLS_CONN_DISABLE_TLSv1_3;
+ }
if (phase2) {
wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
eap_tls_params_from_conf2(params, config);
@@ -366,6 +387,13 @@
struct tls_random keys;
u8 *out;
+ if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
+ *len = 64;
+ return eap_peer_tls_derive_key(sm, data,
+ "EXPORTER_EAP_TLS_Session-Id",
+ 64);
+ }
+
if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) ||
keys.client_random == NULL || keys.server_random == NULL)
return NULL;
@@ -669,6 +697,8 @@
* the AS.
*/
int res = eap_tls_process_input(sm, data, in_data, out_data);
+ char buf[20];
+
if (res) {
/*
* Input processing failed (res = -1) or more data is
@@ -681,6 +711,12 @@
* The incoming message has been reassembled and processed. The
* response was allocated into data->tls_out buffer.
*/
+
+ if (tls_get_version(data->ssl_ctx, data->conn,
+ buf, sizeof(buf)) == 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf);
+ data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0;
+ }
}
if (data->tls_out == NULL) {
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index acd2b78..306e6a9 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -73,6 +73,11 @@
* eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
*/
u8 eap_type;
+
+ /**
+ * tls_v13 - Whether TLS v1.3 or newer is used
+ */
+ int tls_v13;
};
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index c67fa82..bb3641f 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -31,6 +31,8 @@
size_t password_len;
int password_hash; /* whether password is hashed with
* nt_password_hash() */
+ u8 *salt;
+ size_t salt_len;
int phase2;
int force_version;
unsigned int remediation:1;
@@ -38,6 +40,7 @@
int ttls_auth; /* bitfield of
* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
struct hostapd_radius_attr *accept_attr;
+ u32 t_c_timestamp;
};
struct eap_eapol_interface {
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 9706e25..c9da72e 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -326,6 +326,9 @@
if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
sm->eap_if.eapReq = TRUE;
}
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -634,6 +637,9 @@
SM_ENTRY(EAP, TIMEOUT_FAILURE);
sm->eap_if.eapTimeout = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1011,6 +1017,9 @@
if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
sm->eap_if.eapReq = TRUE;
}
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1101,6 +1110,9 @@
SM_ENTRY(EAP, TIMEOUT_FAILURE2);
sm->eap_if.eapTimeout = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1110,6 +1122,9 @@
eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
sm->eap_if.eapFail = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1136,6 +1151,9 @@
* started properly.
*/
sm->start_reauth = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR,
+ MAC2STR(sm->peer_addr));
}
@@ -1802,6 +1820,8 @@
return;
bin_clear_free(user->password, user->password_len);
user->password = NULL;
+ bin_clear_free(user->salt, user->salt_len);
+ user->salt = NULL;
os_free(user);
}
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index fa0342f..a63f820 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -1516,7 +1516,7 @@
if (eap_fast_process_phase1(sm, data))
break;
- /* fall through to PHASE2_START */
+ /* fall through */
case PHASE2_START:
eap_fast_process_phase2_start(sm, data);
break;
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 1833419..32e6872 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -223,7 +223,7 @@
}
data->out_used = 0;
}
- /* pass through */
+ /* fall through */
case WAIT_FRAG_ACK:
return eap_ikev2_build_msg(data, id);
case FRAG_ACK:
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 68f0af9..d0fa54a 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "crypto/sha256.h"
#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_pwd_common.h"
@@ -26,8 +27,11 @@
u8 *password;
size_t password_len;
int password_hash;
+ u8 *salt;
+ size_t salt_len;
u32 token;
u16 group_num;
+ u8 password_prep;
EAP_PWD_group *grp;
struct wpabuf *inbuf;
@@ -36,20 +40,18 @@
size_t out_frag_pos;
size_t mtu;
- BIGNUM *k;
- BIGNUM *private_value;
- BIGNUM *peer_scalar;
- BIGNUM *my_scalar;
- EC_POINT *my_element;
- EC_POINT *peer_element;
+ struct crypto_bignum *k;
+ struct crypto_bignum *private_value;
+ struct crypto_bignum *peer_scalar;
+ struct crypto_bignum *my_scalar;
+ struct crypto_ec_point *my_element;
+ struct crypto_ec_point *peer_element;
u8 my_confirm[SHA256_MAC_LEN];
u8 msk[EAP_MSK_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 session_id[1 + SHA256_MAC_LEN];
-
- BN_CTX *bnctx;
};
@@ -116,13 +118,17 @@
os_memcpy(data->password, sm->user->password, data->password_len);
data->password_hash = sm->user->password_hash;
- data->bnctx = BN_CTX_new();
- if (data->bnctx == NULL) {
- wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
- bin_clear_free(data->password, data->password_len);
- bin_clear_free(data->id_server, data->id_server_len);
- os_free(data);
- return NULL;
+ data->salt_len = sm->user->salt_len;
+ if (data->salt_len) {
+ data->salt = os_memdup(sm->user->salt, sm->user->salt_len);
+ if (!data->salt) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Memory allocation of salt failed");
+ bin_clear_free(data->id_server, data->id_server_len);
+ bin_clear_free(data->password, data->password_len);
+ os_free(data);
+ return NULL;
+ }
}
data->in_frag_pos = data->out_frag_pos = 0;
@@ -138,21 +144,19 @@
{
struct eap_pwd_data *data = priv;
- BN_clear_free(data->private_value);
- BN_clear_free(data->peer_scalar);
- BN_clear_free(data->my_scalar);
- BN_clear_free(data->k);
- BN_CTX_free(data->bnctx);
- EC_POINT_clear_free(data->my_element);
- EC_POINT_clear_free(data->peer_element);
+ crypto_bignum_deinit(data->private_value, 1);
+ crypto_bignum_deinit(data->peer_scalar, 1);
+ crypto_bignum_deinit(data->my_scalar, 1);
+ crypto_bignum_deinit(data->k, 1);
+ crypto_ec_point_deinit(data->my_element, 1);
+ crypto_ec_point_deinit(data->peer_element, 1);
bin_clear_free(data->id_peer, data->id_peer_len);
bin_clear_free(data->id_server, data->id_server_len);
bin_clear_free(data->password, data->password_len);
+ bin_clear_free(data->salt, data->salt_len);
if (data->grp) {
- EC_GROUP_free(data->grp->group);
- EC_POINT_clear_free(data->grp->pwe);
- BN_clear_free(data->grp->order);
- BN_clear_free(data->grp->prime);
+ crypto_ec_deinit(data->grp->group);
+ crypto_ec_point_deinit(data->grp->pwe, 1);
os_free(data->grp);
}
wpabuf_free(data->inbuf);
@@ -185,12 +189,45 @@
return;
}
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd (server): password",
+ data->password, data->password_len);
+ if (data->salt_len)
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd (server): salt",
+ data->salt, data->salt_len);
+
+ /*
+ * If this is a salted password then figure out how it was hashed
+ * based on the length.
+ */
+ if (data->salt_len) {
+ switch (data->password_len) {
+ case 20:
+ data->password_prep = EAP_PWD_PREP_SSHA1;
+ break;
+ case 32:
+ data->password_prep = EAP_PWD_PREP_SSHA256;
+ break;
+ case 64:
+ data->password_prep = EAP_PWD_PREP_SSHA512;
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "EAP-pwd (server): bad size %d for salted password",
+ (int) data->password_len);
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ } else {
+ /* Otherwise, figure out whether it's MS hashed or plain */
+ data->password_prep = data->password_hash ? EAP_PWD_PREP_MS :
+ EAP_PWD_PREP_NONE;
+ }
+
wpabuf_put_be16(data->outbuf, data->group_num);
wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token));
- wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS :
- EAP_PWD_PREP_NONE);
+ wpabuf_put_u8(data->outbuf, data->password_prep);
wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len);
}
@@ -198,9 +235,9 @@
static void eap_pwd_build_commit_req(struct eap_sm *sm,
struct eap_pwd_data *data, u8 id)
{
- BIGNUM *mask = NULL, *x = NULL, *y = NULL;
+ struct crypto_bignum *mask = NULL;
u8 *scalar = NULL, *element = NULL;
- u16 offset;
+ size_t prime_len, order_len;
wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
/*
@@ -210,93 +247,82 @@
if (data->out_frag_pos)
return;
- if (((data->private_value = BN_new()) == NULL) ||
- ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
- ((data->my_scalar = BN_new()) == NULL) ||
- ((mask = BN_new()) == NULL)) {
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
+
+ data->private_value = crypto_bignum_init();
+ data->my_element = crypto_ec_point_init(data->grp->group);
+ data->my_scalar = crypto_bignum_init();
+ mask = crypto_bignum_init();
+ if (!data->private_value || !data->my_element || !data->my_scalar ||
+ !mask) {
wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
"fail");
goto fin;
}
- if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
- BN_rand_range(mask, data->grp->order) != 1 ||
- BN_add(data->my_scalar, data->private_value, mask) != 1 ||
- BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
- data->bnctx) != 1) {
+ if (crypto_bignum_rand(data->private_value,
+ crypto_ec_get_order(data->grp->group)) < 0 ||
+ crypto_bignum_rand(mask,
+ crypto_ec_get_order(data->grp->group)) < 0 ||
+ crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 ||
+ crypto_bignum_mod(data->my_scalar,
+ crypto_ec_get_order(data->grp->group),
+ data->my_scalar) < 0) {
wpa_printf(MSG_INFO,
"EAP-pwd (server): unable to get randomness");
goto fin;
}
- if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
- data->grp->pwe, mask, data->bnctx)) {
+ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
+ data->my_element) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
"fail");
eap_pwd_state(data, FAILURE);
goto fin;
}
- if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
- {
+ if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
"fail");
goto fin;
}
- BN_clear_free(mask);
- if (((x = BN_new()) == NULL) ||
- ((y = BN_new()) == NULL)) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
- "fail");
+ scalar = os_malloc(order_len);
+ element = os_malloc(prime_len * 2);
+ if (!scalar || !element) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
goto fin;
}
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
+ element + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
"fail");
goto fin;
}
- if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
- ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
- NULL)) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
- goto fin;
- }
+ crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
- /*
- * bignums occupy as little memory as possible so one that is
- * sufficiently smaller than the prime or order might need pre-pending
- * with zeros.
- */
- os_memset(scalar, 0, BN_num_bytes(data->grp->order));
- os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, scalar + offset);
-
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, element + offset);
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
-
- data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) +
- BN_num_bytes(data->grp->order));
+ data->outbuf = wpabuf_alloc(2 * prime_len + order_len +
+ (data->salt ? 1 + data->salt_len : 0));
if (data->outbuf == NULL)
goto fin;
+ /* If we're doing salted password prep, add the salt */
+ if (data->salt_len) {
+ wpabuf_put_u8(data->outbuf, data->salt_len);
+ wpabuf_put_data(data->outbuf, data->salt, data->salt_len);
+ }
+
/* We send the element as (x,y) followed by the scalar */
- wpabuf_put_data(data->outbuf, element,
- 2 * BN_num_bytes(data->grp->prime));
- wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
+ wpabuf_put_data(data->outbuf, element, 2 * prime_len);
+ wpabuf_put_data(data->outbuf, scalar, order_len);
fin:
+ crypto_bignum_deinit(mask, 1);
os_free(scalar);
os_free(element);
- BN_clear_free(x);
- BN_clear_free(y);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
}
@@ -305,11 +331,10 @@
static void eap_pwd_build_confirm_req(struct eap_sm *sm,
struct eap_pwd_data *data, u8 id)
{
- BIGNUM *x = NULL, *y = NULL;
struct crypto_hash *hash;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
u16 grp;
- int offset;
+ size_t prime_len, order_len;
wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
/*
@@ -319,9 +344,12 @@
if (data->out_frag_pos)
return;
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
+
/* Each component of the cruft will be at most as big as the prime */
- if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
- ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ cruft = os_malloc(prime_len * 2);
+ if (!cruft) {
wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
"fail");
goto fin;
@@ -341,64 +369,38 @@
*
* First is k
*/
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
- BN_bn2bin(data->k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
/* server element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+ cruft + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
-
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* peer element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->peer_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft,
+ cruft + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
-
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* peer scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->peer_scalar);
- BN_bn2bin(data->peer_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* ciphersuite */
grp = htons(data->group_num);
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, prime_len);
ptr = cruft;
os_memcpy(ptr, &grp, sizeof(u16));
ptr += sizeof(u16);
@@ -419,9 +421,7 @@
wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
fin:
- bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
- BN_clear_free(x);
- BN_clear_free(y);
+ bin_clear_free(cruft, prime_len * 2);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
}
@@ -603,12 +603,15 @@
(id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
(os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
(id->prf != EAP_PWD_DEFAULT_PRF) ||
- id->prep !=
- data->password_hash ? EAP_PWD_PREP_MS : EAP_PWD_PREP_NONE) {
+ (id->prep != data->password_prep)) {
wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
eap_pwd_state(data, FAILURE);
return;
}
+ if (data->id_peer || data->grp) {
+ wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated");
+ return;
+ }
data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
if (data->id_peer == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
@@ -619,14 +622,19 @@
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
data->id_peer, data->id_peer_len);
- data->grp = os_zalloc(sizeof(EAP_PWD_group));
+ data->grp = get_eap_pwd_group(data->group_num);
if (data->grp == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
"group");
return;
}
- if (data->password_hash) {
+ /*
+ * If it's PREP_MS then hash the password again, otherwise regardless
+ * of the prep the client is doing, the password we have is the one to
+ * use to generate the password element.
+ */
+ if (data->password_prep == EAP_PWD_PREP_MS) {
res = hash_nt_password_hash(data->password, pwhashhash);
if (res)
return;
@@ -649,7 +657,7 @@
return;
}
wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
- BN_num_bits(data->grp->prime));
+ (int) crypto_ec_prime_len_bits(data->grp->group));
eap_pwd_state(data, PWD_Commit_Req);
}
@@ -659,16 +667,16 @@
eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
- u8 *ptr;
- BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
- EC_POINT *K = NULL, *point = NULL;
+ const u8 *ptr;
+ struct crypto_bignum *cofactor = NULL;
+ struct crypto_ec_point *K = NULL, *point = NULL;
int res = 0;
size_t prime_len, order_len;
wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
- prime_len = BN_num_bytes(data->grp->prime);
- order_len = BN_num_bytes(data->grp->order);
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
if (payload_len != 2 * prime_len + order_len) {
wpa_printf(MSG_INFO,
@@ -678,49 +686,47 @@
goto fin;
}
- if (((data->peer_scalar = BN_new()) == NULL) ||
- ((data->k = BN_new()) == NULL) ||
- ((cofactor = BN_new()) == NULL) ||
- ((x = BN_new()) == NULL) ||
- ((y = BN_new()) == NULL) ||
- ((point = EC_POINT_new(data->grp->group)) == NULL) ||
- ((K = EC_POINT_new(data->grp->group)) == NULL) ||
- ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) {
+ data->k = crypto_bignum_init();
+ cofactor = crypto_bignum_init();
+ point = crypto_ec_point_init(data->grp->group);
+ K = crypto_ec_point_init(data->grp->group);
+ if (!data->k || !cofactor || !point || !K) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
"fail");
goto fin;
}
- if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+ if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
"cofactor for curve");
goto fin;
}
/* element, x then y, followed by scalar */
- ptr = (u8 *) payload;
- BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
- ptr += BN_num_bytes(data->grp->prime);
- BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
- ptr += BN_num_bytes(data->grp->prime);
- BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar);
- if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
- data->peer_element, x, y,
- data->bnctx)) {
+ ptr = payload;
+ data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+ if (!data->peer_element) {
wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
"fail");
goto fin;
}
+ ptr += prime_len * 2;
+ data->peer_scalar = crypto_bignum_init_set(ptr, order_len);
+ if (!data->peer_scalar) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+ "fail");
+ goto fin;
+ }
/* check to ensure peer's element is not in a small sub-group */
- if (BN_cmp(cofactor, BN_value_one())) {
- if (!EC_POINT_mul(data->grp->group, point, NULL,
- data->peer_element, cofactor, NULL)) {
+ if (!crypto_bignum_is_one(cofactor)) {
+ if (crypto_ec_point_mul(data->grp->group, data->peer_element,
+ cofactor, point) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
"multiply peer element by order");
goto fin;
}
- if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+ if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
"is at infinity!\n");
goto fin;
@@ -728,21 +734,21 @@
}
/* compute the shared key, k */
- if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
- data->peer_scalar, data->bnctx)) ||
- (!EC_POINT_add(data->grp->group, K, K, data->peer_element,
- data->bnctx)) ||
- (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
- data->bnctx))) {
+ if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe,
+ data->peer_scalar, K) < 0) ||
+ (crypto_ec_point_add(data->grp->group, K, data->peer_element,
+ K) < 0) ||
+ (crypto_ec_point_mul(data->grp->group, K, data->private_value,
+ K) < 0)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
"fail");
goto fin;
}
/* ensure that the shared key isn't in a small sub-group */
- if (BN_cmp(cofactor, BN_value_one())) {
- if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
- NULL)) {
+ if (!crypto_bignum_is_one(cofactor)) {
+ if (crypto_ec_point_mul(data->grp->group, K, cofactor,
+ K) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
"multiply shared key point by order!\n");
goto fin;
@@ -755,13 +761,12 @@
* never going to happen it is a simple and safe check "just to be
* sure" so let's be safe.
*/
- if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+ if (crypto_ec_point_is_at_infinity(data->grp->group, K)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
"at infinity");
goto fin;
}
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
- NULL, data->bnctx)) {
+ if (crypto_ec_point_x(data->grp->group, K, data->k)) {
wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
"shared secret from secret point");
goto fin;
@@ -769,11 +774,9 @@
res = 1;
fin:
- EC_POINT_clear_free(K);
- EC_POINT_clear_free(point);
- BN_clear_free(cofactor);
- BN_clear_free(x);
- BN_clear_free(y);
+ crypto_ec_point_deinit(K, 1);
+ crypto_ec_point_deinit(point, 1);
+ crypto_bignum_deinit(cofactor, 1);
if (res)
eap_pwd_state(data, PWD_Confirm_Req);
@@ -786,12 +789,14 @@
eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
- BIGNUM *x = NULL, *y = NULL;
struct crypto_hash *hash;
u32 cs;
u16 grp;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
- int offset;
+ size_t prime_len, order_len;
+
+ prime_len = crypto_ec_prime_len(data->grp->group);
+ order_len = crypto_ec_order_len(data->grp->group);
if (payload_len != SHA256_MAC_LEN) {
wpa_printf(MSG_INFO,
@@ -810,8 +815,8 @@
*ptr = EAP_PWD_DEFAULT_PRF;
/* each component of the cruft will be at most as big as the prime */
- if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
- ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ cruft = os_malloc(prime_len * 2);
+ if (!cruft) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
goto fin;
}
@@ -825,62 +830,36 @@
goto fin;
/* k */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
- BN_bn2bin(data->k, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ eap_pwd_h_update(hash, cruft, prime_len);
/* peer element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->peer_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft,
+ cruft + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* peer scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->peer_scalar);
- BN_bn2bin(data->peer_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* server element: x, y */
- if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
- data->my_element, x, y,
- data->bnctx)) {
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft,
+ cruft + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
"assignment fail");
goto fin;
}
-
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
- BN_bn2bin(x, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
- BN_bn2bin(y, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
- offset = BN_num_bytes(data->grp->order) -
- BN_num_bytes(data->my_scalar);
- BN_bn2bin(data->my_scalar, cruft + offset);
- eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+ crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ eap_pwd_h_update(hash, cruft, order_len);
/* ciphersuite */
- os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
/* all done */
@@ -894,7 +873,7 @@
}
wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
- if (compute_keys(data->grp, data->bnctx, data->k,
+ if (compute_keys(data->grp, data->k,
data->peer_scalar, data->my_scalar, conf,
data->my_confirm, &cs, data->msk, data->emsk,
data->session_id) < 0)
@@ -903,9 +882,7 @@
eap_pwd_state(data, SUCCESS);
fin:
- bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
- BN_clear_free(x);
- BN_clear_free(y);
+ bin_clear_free(cruft, prime_len * 2);
}
@@ -1126,4 +1103,3 @@
return eap_server_method_register(eap);
}
-
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 7249858..8b9e53c 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -302,17 +302,22 @@
{
struct eap_tls_data *data = priv;
u8 *eapKeyData;
+ const char *label;
if (data->state != SUCCESS)
return NULL;
- eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "client EAP encryption",
- EAP_TLS_KEY_LEN);
+ if (data->ssl.tls_v13)
+ label = "EXPORTER_EAP_TLS_Key_Material";
+ else
+ label = "client EAP encryption";
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
+ EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
*len = EAP_TLS_KEY_LEN;
wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
eapKeyData, EAP_TLS_KEY_LEN);
+ os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN);
} else {
wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
}
@@ -325,12 +330,16 @@
{
struct eap_tls_data *data = priv;
u8 *eapKeyData, *emsk;
+ const char *label;
if (data->state != SUCCESS)
return NULL;
- eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "client EAP encryption",
+ if (data->ssl.tls_v13)
+ label = "EXPORTER_EAP_TLS_Key_Material";
+ else
+ label = "client EAP encryption";
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
emsk = os_malloc(EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 3c9027b..4f9cb08 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -107,7 +107,7 @@
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
- char *label, size_t len)
+ const char *label, size_t len)
{
u8 *out;
@@ -145,6 +145,13 @@
struct tls_random keys;
u8 *out;
+ if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
+ *len = 64;
+ return eap_server_tls_derive_key(sm, data,
+ "EXPORTER_EAP_TLS_Session-Id",
+ 64);
+ }
+
if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
return NULL;
@@ -305,6 +312,8 @@
int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data)
{
+ char buf[20];
+
if (data->tls_out) {
/* This should not happen.. */
wpa_printf(MSG_INFO, "SSL: pending tls_out data when "
@@ -327,6 +336,11 @@
return -1;
}
+ if (tls_get_version(sm->ssl_ctx, data->conn, buf, sizeof(buf)) == 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf);
+ data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0;
+ }
+
return 0;
}
@@ -373,7 +387,7 @@
if (data->tls_in &&
eap_server_tls_process_cont(data, *pos, end - *pos) < 0)
return -1;
-
+
if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) {
if (eap_server_tls_process_fragment(data, flags, tls_msg_len,
*pos, end - *pos) < 0)
diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c
index 7d9d285..4a5cb98 100644
--- a/src/eap_server/eap_server_wsc.c
+++ b/src/eap_server/eap_server_wsc.c
@@ -257,7 +257,7 @@
}
data->out_used = 0;
}
- /* pass through */
+ /* fall through */
case WAIT_FRAG_ACK:
return eap_wsc_build_msg(data, id);
case FRAG_ACK:
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index dc943eb..31f6e72 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -50,6 +50,11 @@
enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state;
struct wpabuf tmpbuf;
+
+ /**
+ * tls_v13 - Whether TLS v1.3 or newer is used
+ */
+ int tls_v13;
};
@@ -73,7 +78,7 @@
int verify_peer, int eap_type);
void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
- char *label, size_t len);
+ const char *label, size_t len);
u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
struct eap_ssl_data *data, u8 eap_type,
size_t *len);
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index cb463ef..bfbc995 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -2014,6 +2014,7 @@
sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
}
+
static void eapol_sm_notify_eap_error(void *ctx, int error_code)
{
struct eapol_sm *sm = ctx;
@@ -2022,6 +2023,7 @@
sm->ctx->eap_error_cb(sm->ctx->ctx, error_code);
}
+
#ifdef CONFIG_EAP_PROXY
static void eapol_sm_eap_proxy_cb(void *ctx)
diff --git a/src/fst/fst_ctrl_aux.h b/src/fst/fst_ctrl_aux.h
index e2133f5..0aff5d0 100644
--- a/src/fst/fst_ctrl_aux.h
+++ b/src/fst/fst_ctrl_aux.h
@@ -35,6 +35,17 @@
* more info */
};
+enum fst_reason {
+ REASON_TEARDOWN,
+ REASON_SETUP,
+ REASON_SWITCH,
+ REASON_STT,
+ REASON_REJECT,
+ REASON_ERROR_PARAMS,
+ REASON_RESET,
+ REASON_DETACH_IFACE,
+};
+
enum fst_initiator {
FST_INITIATOR_UNDEFINED,
FST_INITIATOR_LOCAL,
@@ -57,16 +68,7 @@
enum fst_session_state new_state;
union fst_session_state_switch_extra {
struct {
- enum fst_reason {
- REASON_TEARDOWN,
- REASON_SETUP,
- REASON_SWITCH,
- REASON_STT,
- REASON_REJECT,
- REASON_ERROR_PARAMS,
- REASON_RESET,
- REASON_DETACH_IFACE,
- } reason;
+ enum fst_reason reason;
u8 reject_code; /* REASON_REJECT */
/* REASON_SWITCH,
* REASON_TEARDOWN,
diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c
index 7820e58..7df3362 100644
--- a/src/fst/fst_ctrl_iface.c
+++ b/src/fst/fst_ctrl_iface.c
@@ -49,7 +49,7 @@
if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
os_snprintf(reject_str, sizeof(reject_str), "%u",
ss->extra.to_initial.reject_code);
- /* no break */
+ /* fall through */
case REASON_TEARDOWN:
case REASON_SWITCH:
switch (ss->extra.to_initial.initiator) {
diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h
index 2a45245..5387177 100644
--- a/src/l2_packet/l2_packet.h
+++ b/src/l2_packet/l2_packet.h
@@ -42,6 +42,7 @@
enum l2_packet_filter_type {
L2_PACKET_FILTER_DHCP,
L2_PACKET_FILTER_NDISC,
+ L2_PACKET_FILTER_PKTTYPE,
};
/**
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 65b4906..291c9dd 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -84,6 +84,26 @@
.filter = ndisc_sock_filter_insns,
};
+/* drop packet if skb->pkt_type is PACKET_OTHERHOST (0x03). Generated by:
+ * $ bpfc - <<EOF
+ * > ldb #type
+ * > jeq #0x03, drop
+ * > pass: ret #-1
+ * > drop: ret #0
+ * > EOF
+ */
+static struct sock_filter pkt_type_filter_insns[] = {
+ { 0x30, 0, 0, 0xfffff004 },
+ { 0x15, 1, 0, 0x00000003 },
+ { 0x6, 0, 0, 0xffffffff },
+ { 0x6, 0, 0, 0x00000000 },
+};
+
+static const struct sock_fprog pkt_type_sock_filter = {
+ .len = ARRAY_SIZE(pkt_type_filter_insns),
+ .filter = pkt_type_filter_insns,
+};
+
int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
{
@@ -471,6 +491,9 @@
case L2_PACKET_FILTER_NDISC:
sock_filter = &ndisc_sock_filter;
break;
+ case L2_PACKET_FILTER_PKTTYPE:
+ sock_filter = &pkt_type_sock_filter;
+ break;
default:
return -1;
}
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 1a31ef2..b4660c4 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -3889,6 +3889,19 @@
if (p2p->in_listen)
return 0; /* Internal timeout will trigger the next step */
+ if (p2p->state == P2P_WAIT_PEER_CONNECT && p2p->go_neg_peer &&
+ p2p->pending_listen_freq) {
+ /*
+ * Better wait a bit if the driver is unable to start
+ * offchannel operation for some reason to continue with
+ * P2P_WAIT_PEER_(IDLE/CONNECT) state transitions.
+ */
+ p2p_dbg(p2p,
+ "Listen operation did not seem to start - delay idle phase to avoid busy loop");
+ p2p_set_timeout(p2p, 0, 100000);
+ return 1;
+ }
+
if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
if (p2p->go_neg_peer->connect_reqs >= 120) {
p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index cad0292..f2dd193 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -245,13 +245,15 @@
* ieee802_1x_kay_get_participant -
*/
static struct ieee802_1x_mka_participant *
-ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn)
+ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn,
+ size_t len)
{
struct ieee802_1x_mka_participant *participant;
dl_list_for_each(participant, &kay->participant_list,
struct ieee802_1x_mka_participant, list) {
- if (os_memcmp(participant->ckn.name, ckn,
+ if (participant->ckn.len == len &&
+ os_memcmp(participant->ckn.name, ckn,
participant->ckn.len) == 0)
return participant;
}
@@ -748,6 +750,8 @@
struct ieee802_1x_mka_participant *participant;
const struct ieee802_1x_mka_basic_body *body;
struct ieee802_1x_kay_peer *peer;
+ size_t ckn_len;
+ size_t body_len;
body = (const struct ieee802_1x_mka_basic_body *) mka_msg;
@@ -761,7 +765,15 @@
return NULL;
}
- participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+ body_len = get_mka_param_body_len(body);
+ if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
+ wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
+ body_len);
+ return NULL;
+ }
+ ckn_len = body_len -
+ (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
+ participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
if (!participant) {
wpa_printf(MSG_DEBUG, "Peer is not included in my CA");
return NULL;
@@ -1331,8 +1343,8 @@
}
}
- /* check old key is valid */
- if (body->otx || body->orx) {
+ /* check old key is valid (but only if we remember our old key) */
+ if (participant->oki.kn != 0 && (body->otx || body->orx)) {
if (os_memcmp(participant->oki.mi, body->osrv_mi,
sizeof(participant->oki.mi)) != 0 ||
be_to_host32(body->okn) != participant->oki.kn ||
@@ -1614,7 +1626,8 @@
os_free(unwrap_sak);
return -1;
}
- wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len);
+ wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK:",
+ unwrap_sak, sak_len);
sa_key = os_zalloc(sizeof(*sa_key));
if (!sa_key) {
@@ -2005,7 +2018,7 @@
wpa_printf(MSG_ERROR, "KaY: SAK Length not support");
goto fail;
}
- wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
+ wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
os_free(context);
context = NULL;
@@ -2081,6 +2094,7 @@
struct ieee802_1x_kay_peer *key_server = NULL;
struct ieee802_1x_kay *kay = participant->kay;
Boolean i_is_key_server;
+ int priority_comparison;
if (participant->is_obliged_key_server) {
participant->new_sak = TRUE;
@@ -2111,8 +2125,14 @@
tmp.key_server_priority = kay->actor_priority;
os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci));
- if (compare_priorities(&tmp, key_server) < 0)
+ priority_comparison = compare_priorities(&tmp, key_server);
+ if (priority_comparison < 0) {
i_is_key_server = TRUE;
+ } else if (priority_comparison == 0) {
+ wpa_printf(MSG_WARNING,
+ "KaY: Cannot elect key server between me and peer, duplicate MAC detected");
+ key_server = NULL;
+ }
} else if (participant->can_be_key_server) {
i_is_key_server = TRUE;
}
@@ -2387,7 +2407,7 @@
participant->orx = FALSE;
participant->is_key_server = FALSE;
participant->is_elected = FALSE;
- kay->authenticated = TRUE;
+ kay->authenticated = FALSE;
kay->secured = FALSE;
kay->failed = FALSE;
kay->ltx_kn = 0;
@@ -2404,7 +2424,7 @@
ieee802_1x_delete_transmit_sa(kay, txsa);
}
- ieee802_1x_cp_connect_authenticated(kay->cp);
+ ieee802_1x_cp_connect_pending(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
} else {
ieee802_1x_kay_elect_key_server(participant);
@@ -2856,6 +2876,7 @@
size_t mka_msg_len;
struct ieee802_1x_mka_participant *participant;
size_t body_len;
+ size_t ckn_len;
u8 icv[MAX_ICV_LEN];
u8 *msg_icv;
@@ -2895,8 +2916,22 @@
return -1;
}
+ if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
+ wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
+ body_len);
+ return -1;
+ }
+ ckn_len = body_len -
+ (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
+ if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) {
+ wpa_printf(MSG_ERROR,
+ "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)",
+ ckn_len, MAX_CKN_LEN);
+ return -1;
+ }
+
/* CKN should be owned by I */
- participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+ participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
if (!participant) {
wpa_printf(MSG_DEBUG, "CKN is not included in my CA");
return -1;
@@ -3234,8 +3269,9 @@
* ieee802_1x_kay_create_mka -
*/
struct ieee802_1x_mka_participant *
-ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn,
- struct mka_key *cak, u32 life,
+ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
+ const struct mka_key_name *ckn,
+ const struct mka_key *cak, u32 life,
enum mka_created_mode mode, Boolean is_authenticator)
{
struct ieee802_1x_mka_participant *participant;
@@ -3403,7 +3439,7 @@
wpa_printf(MSG_DEBUG, "KaY: participant removed");
/* get the participant */
- participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+ participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
if (!participant) {
wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
ckn->name, ckn->len);
@@ -3462,7 +3498,7 @@
if (!kay || !ckn)
return;
- participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+ participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
if (!participant)
return;
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index 8f394fd..b265059 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -241,7 +241,8 @@
struct ieee802_1x_mka_participant *
ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
- struct mka_key_name *ckn, struct mka_key *cak,
+ const struct mka_key_name *ckn,
+ const struct mka_key *cak,
u32 life, enum mka_created_mode mode,
Boolean is_authenticator);
void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay,
diff --git a/src/radius/radius.c b/src/radius/radius.c
index fc98ad6..07240ea 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -250,6 +250,8 @@
{ RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id",
RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_WLAN_REASON_CODE, "WLAN-Reason-Code",
+ RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher",
RADIUS_ATTR_HEXDUMP },
{ RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher",
diff --git a/src/radius/radius.h b/src/radius/radius.h
index cd510d2..630c0f9 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -102,8 +102,13 @@
RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130,
RADIUS_ATTR_LOCATION_CAPABLE = 131,
RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132,
+ RADIUS_ATTR_GSS_ACCEPTOR_SERVICE_NAME = 164,
+ RADIUS_ATTR_GSS_ACCEPTOR_HOST_NAME = 165,
+ RADIUS_ATTR_GSS_ACCEPTOR_SERVICE_SPECIFICS = 166,
+ RADIUS_ATTR_GSS_ACCEPTOR_REALM_NAME = 167,
RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177,
RADIUS_ATTR_WLAN_HESSID = 181,
+ RADIUS_ATTR_WLAN_REASON_CODE = 185,
RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186,
RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
RADIUS_ATTR_WLAN_AKM_SUITE = 188,
@@ -193,6 +198,11 @@
RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3,
RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4,
RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5,
+ RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM = 6,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME = 7,
+ RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP = 8,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING = 9,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL = 10,
};
#ifdef _MSC_VER
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 06c804d..a87ee74 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -904,13 +904,13 @@
switch (res) {
case RADIUS_RX_PROCESSED:
radius_msg_free(msg);
- /* continue */
+ /* fall through */
case RADIUS_RX_QUEUED:
radius_client_msg_free(req);
return;
case RADIUS_RX_INVALID_AUTHENTICATOR:
invalid_authenticator++;
- /* continue */
+ /* fall through */
case RADIUS_RX_UNKNOWN:
/* continue with next handler */
break;
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index ed24c19..aaa3fc2 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -27,6 +27,7 @@
void *ctx;
enum radius_das_res (*disconnect)(void *ctx,
struct radius_das_attrs *attr);
+ enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr);
};
@@ -161,6 +162,10 @@
abuf, from_port);
error = 508;
break;
+ case RADIUS_DAS_COA_FAILED:
+ /* not used with Disconnect-Request */
+ error = 405;
+ break;
case RADIUS_DAS_SUCCESS:
error = 0;
break;
@@ -184,6 +189,195 @@
}
+static struct radius_msg * radius_das_coa(struct radius_das_data *das,
+ struct radius_msg *msg,
+ const char *abuf, int from_port)
+{
+ struct radius_hdr *hdr;
+ struct radius_msg *reply;
+ u8 allowed[] = {
+ RADIUS_ATTR_USER_NAME,
+ RADIUS_ATTR_NAS_IP_ADDRESS,
+ RADIUS_ATTR_CALLING_STATION_ID,
+ RADIUS_ATTR_NAS_IDENTIFIER,
+ RADIUS_ATTR_ACCT_SESSION_ID,
+ RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+ RADIUS_ATTR_EVENT_TIMESTAMP,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+#ifdef CONFIG_HS20
+ RADIUS_ATTR_VENDOR_SPECIFIC,
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_IPV6
+ RADIUS_ATTR_NAS_IPV6_ADDRESS,
+#endif /* CONFIG_IPV6 */
+ 0
+ };
+ int error = 405;
+ u8 attr;
+ enum radius_das_res res;
+ struct radius_das_attrs attrs;
+ u8 *buf;
+ size_t len;
+ char tmp[100];
+ u8 sta_addr[ETH_ALEN];
+
+ hdr = radius_msg_get_hdr(msg);
+
+ if (!das->coa) {
+ wpa_printf(MSG_INFO, "DAS: CoA not supported");
+ goto fail;
+ }
+
+ attr = radius_msg_find_unlisted_attr(msg, allowed);
+ if (attr) {
+ wpa_printf(MSG_INFO,
+ "DAS: Unsupported attribute %u in CoA-Request from %s:%d",
+ attr, abuf, from_port);
+ error = 401;
+ goto fail;
+ }
+
+ os_memset(&attrs, 0, sizeof(attrs));
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ &buf, &len, NULL) == 0) {
+ if (len != 4) {
+ wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
+ abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.nas_ip_addr = buf;
+ }
+
+#ifdef CONFIG_IPV6
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+ &buf, &len, NULL) == 0) {
+ if (len != 16) {
+ wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
+ abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.nas_ipv6_addr = buf;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ &buf, &len, NULL) == 0) {
+ attrs.nas_identifier = buf;
+ attrs.nas_identifier_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ &buf, &len, NULL) == 0) {
+ if (len >= sizeof(tmp))
+ len = sizeof(tmp) - 1;
+ os_memcpy(tmp, buf, len);
+ tmp[len] = '\0';
+ if (hwaddr_aton2(tmp, sta_addr) < 0) {
+ wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
+ "'%s' from %s:%d", tmp, abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.sta_addr = sta_addr;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+ &buf, &len, NULL) == 0) {
+ attrs.user_name = buf;
+ attrs.user_name_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ &buf, &len, NULL) == 0) {
+ attrs.acct_session_id = buf;
+ attrs.acct_session_id_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+ &buf, &len, NULL) == 0) {
+ attrs.acct_multi_session_id = buf;
+ attrs.acct_multi_session_id_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ &buf, &len, NULL) == 0) {
+ attrs.cui = buf;
+ attrs.cui_len = len;
+ }
+
+#ifdef CONFIG_HS20
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ &buf, &len, NULL) == 0) {
+ if (len < 10 || WPA_GET_BE32(buf) != RADIUS_VENDOR_ID_WFA ||
+ buf[4] != RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING ||
+ buf[5] < 6) {
+ wpa_printf(MSG_INFO,
+ "DAS: Unsupported attribute %u in CoA-Request from %s:%d",
+ attr, abuf, from_port);
+ error = 401;
+ goto fail;
+ }
+ attrs.hs20_t_c_filtering = &buf[6];
+ }
+
+ if (!attrs.hs20_t_c_filtering) {
+ wpa_printf(MSG_INFO,
+ "DAS: No supported authorization change attribute in CoA-Request from %s:%d",
+ abuf, from_port);
+ error = 402;
+ goto fail;
+ }
+#endif /* CONFIG_HS20 */
+
+ res = das->coa(das->ctx, &attrs);
+ switch (res) {
+ case RADIUS_DAS_NAS_MISMATCH:
+ wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
+ abuf, from_port);
+ error = 403;
+ break;
+ case RADIUS_DAS_SESSION_NOT_FOUND:
+ wpa_printf(MSG_INFO,
+ "DAS: Session not found for request from %s:%d",
+ abuf, from_port);
+ error = 503;
+ break;
+ case RADIUS_DAS_MULTI_SESSION_MATCH:
+ wpa_printf(MSG_INFO,
+ "DAS: Multiple sessions match for request from %s:%d",
+ abuf, from_port);
+ error = 508;
+ break;
+ case RADIUS_DAS_COA_FAILED:
+ wpa_printf(MSG_INFO, "DAS: CoA failed for request from %s:%d",
+ abuf, from_port);
+ error = 407;
+ break;
+ case RADIUS_DAS_SUCCESS:
+ error = 0;
+ break;
+ }
+
+fail:
+ reply = radius_msg_new(error ? RADIUS_CODE_COA_NAK :
+ RADIUS_CODE_COA_ACK, hdr->identifier);
+ if (!reply)
+ return NULL;
+
+ if (error &&
+ !radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) {
+ radius_msg_free(reply);
+ return NULL;
+ }
+
+ return reply;
+}
+
+
static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct radius_das_data *das = eloop_ctx;
@@ -219,7 +413,8 @@
wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
len, abuf, from_port);
- if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+ if (das->client_addr.u.v4.s_addr &&
+ das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
return;
}
@@ -270,19 +465,7 @@
reply = radius_das_disconnect(das, msg, abuf, from_port);
break;
case RADIUS_CODE_COA_REQUEST:
- /* TODO */
- reply = radius_msg_new(RADIUS_CODE_COA_NAK,
- hdr->identifier);
- if (reply == NULL)
- break;
-
- /* Unsupported Service */
- if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
- 405)) {
- radius_msg_free(reply);
- reply = NULL;
- break;
- }
+ reply = radius_das_coa(das, msg, abuf, from_port);
break;
default:
wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
@@ -369,6 +552,7 @@
conf->require_message_authenticator;
das->ctx = conf->ctx;
das->disconnect = conf->disconnect;
+ das->coa = conf->coa;
os_memcpy(&das->client_addr, conf->client_addr,
sizeof(das->client_addr));
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
index 9863fdc..233d662 100644
--- a/src/radius/radius_das.h
+++ b/src/radius/radius_das.h
@@ -16,6 +16,7 @@
RADIUS_DAS_NAS_MISMATCH,
RADIUS_DAS_SESSION_NOT_FOUND,
RADIUS_DAS_MULTI_SESSION_MATCH,
+ RADIUS_DAS_COA_FAILED,
};
struct radius_das_attrs {
@@ -35,6 +36,9 @@
size_t acct_multi_session_id_len;
const u8 *cui;
size_t cui_len;
+
+ /* Authorization changes */
+ const u8 *hs20_t_c_filtering;
};
struct radius_das_conf {
@@ -48,6 +52,7 @@
void *ctx;
enum radius_das_res (*disconnect)(void *ctx,
struct radius_das_attrs *attr);
+ enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr);
};
struct radius_das_data *
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index c76bb22..e2801ae 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -80,6 +80,7 @@
struct eap_eapol_interface *eap_if;
char *username; /* from User-Name attribute */
char *nas_ip;
+ u8 mac_addr[ETH_ALEN]; /* from Calling-Station-Id attribute */
struct radius_msg *last_msg;
char *last_from_addr;
@@ -92,8 +93,11 @@
unsigned int remediation:1;
unsigned int macacl:1;
+ unsigned int t_c_filtering:1;
struct hostapd_radius_attr *accept_attr;
+
+ u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
};
/**
@@ -111,6 +115,14 @@
int shared_secret_len;
struct radius_session *sessions;
struct radius_server_counters counters;
+
+ u8 next_dac_identifier;
+ struct radius_msg *pending_dac_coa_req;
+ u8 pending_dac_coa_id;
+ u8 pending_dac_coa_addr[ETH_ALEN];
+ struct radius_msg *pending_dac_disconnect_req;
+ u8 pending_dac_disconnect_id;
+ u8 pending_dac_disconnect_addr[ETH_ALEN];
};
/**
@@ -346,6 +358,8 @@
char *subscr_remediation_url;
u8 subscr_remediation_method;
+ char *t_c_server_url;
+
#ifdef CONFIG_SQLITE
sqlite3 *db;
#endif /* CONFIG_SQLITE */
@@ -628,8 +642,8 @@
struct radius_client *client,
struct radius_msg *msg, const char *from_addr)
{
- u8 *user;
- size_t user_len;
+ u8 *user, *id;
+ size_t user_len, id_len;
int res;
struct radius_session *sess;
struct eap_config eap_conf;
@@ -675,6 +689,21 @@
return NULL;
}
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &id,
+ &id_len, NULL) == 0) {
+ char buf[3 * ETH_ALEN];
+
+ os_memset(buf, 0, sizeof(buf));
+ if (id_len >= sizeof(buf))
+ id_len = sizeof(buf) - 1;
+ os_memcpy(buf, id, id_len);
+ if (hwaddr_aton2(buf, sess->mac_addr) < 0)
+ os_memset(sess->mac_addr, 0, ETH_ALEN);
+ else
+ RADIUS_DEBUG("Calling-Station-Id: " MACSTR,
+ MAC2STR(sess->mac_addr));
+ }
+
srv_log(sess, "New session created");
os_memset(&eap_conf, 0, sizeof(eap_conf));
@@ -718,6 +747,77 @@
}
+#ifdef CONFIG_HS20
+static void radius_srv_hs20_t_c_pending(struct radius_session *sess)
+{
+#ifdef CONFIG_SQLITE
+ char *sql;
+ char addr[3 * ETH_ALEN], *id_str;
+ const u8 *id;
+ size_t id_len;
+
+ if (!sess->server->db || !sess->eap ||
+ is_zero_ether_addr(sess->mac_addr))
+ return;
+
+ os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sess->mac_addr));
+
+ id = eap_get_identity(sess->eap, &id_len);
+ if (!id)
+ return;
+ id_str = os_malloc(id_len + 1);
+ if (!id_str)
+ return;
+ os_memcpy(id_str, id, id_len);
+ id_str[id_len] = '\0';
+
+ sql = sqlite3_mprintf("INSERT OR REPLACE INTO pending_tc (mac_addr,identity) VALUES (%Q,%Q)",
+ addr, id_str);
+ os_free(id_str);
+ if (!sql)
+ return;
+
+ if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ RADIUS_ERROR("Failed to add pending_tc entry into sqlite database: %s",
+ sqlite3_errmsg(sess->server->db));
+ }
+ sqlite3_free(sql);
+#endif /* CONFIG_SQLITE */
+}
+#endif /* CONFIG_HS20 */
+
+
+static void radius_server_add_session(struct radius_session *sess)
+{
+#ifdef CONFIG_SQLITE
+ char *sql;
+ char addr_txt[ETH_ALEN * 3];
+ struct os_time now;
+
+ if (!sess->server->db)
+ return;
+
+
+ os_snprintf(addr_txt, sizeof(addr_txt), MACSTR,
+ MAC2STR(sess->mac_addr));
+
+ os_get_time(&now);
+ sql = sqlite3_mprintf("INSERT OR REPLACE INTO current_sessions(mac_addr,identity,start_time,nas,hs20_t_c_filtering) VALUES (%Q,%Q,%d,%Q,%u)",
+ addr_txt, sess->username, now.sec,
+ sess->nas_ip, sess->t_c_filtering);
+ if (sql) {
+ if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
+ NULL) != SQLITE_OK) {
+ RADIUS_ERROR("Failed to add current_sessions entry into sqlite database: %s",
+ sqlite3_errmsg(sess->server->db));
+ }
+ sqlite3_free(sql);
+ }
+#endif /* CONFIG_SQLITE */
+}
+
+
static struct radius_msg *
radius_server_encapsulate_eap(struct radius_server_data *data,
struct radius_client *client,
@@ -728,6 +828,7 @@
int code;
unsigned int sess_id;
struct radius_hdr *hdr = radius_msg_get_hdr(request);
+ u16 reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
if (sess->eap_if->eapFail) {
sess->eap_if->eapFail = FALSE;
@@ -820,6 +921,61 @@
RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
}
}
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
+ u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */
+ const char *url = data->t_c_server_url, *pos;
+ char *url2, *end2, *pos2;
+ size_t url_len;
+
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
+ buf, sizeof(buf))) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
+ radius_msg_free(msg);
+ return NULL;
+ }
+
+ if (!url) {
+ RADIUS_DEBUG("No t_c_server_url configured");
+ radius_msg_free(msg);
+ return NULL;
+ }
+
+ pos = os_strstr(url, "@1@");
+ if (!pos) {
+ RADIUS_DEBUG("No @1@ macro in t_c_server_url");
+ radius_msg_free(msg);
+ return NULL;
+ }
+
+ url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3;
+ url2 = os_malloc(url_len);
+ if (!url2) {
+ RADIUS_DEBUG("Failed to allocate room for T&C Server URL");
+ os_free(url2);
+ radius_msg_free(msg);
+ return NULL;
+ }
+ pos2 = url2;
+ end2 = url2 + url_len;
+ os_memcpy(pos2, url, pos - url);
+ pos2 += pos - url;
+ os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr));
+ pos2 += ETH_ALEN * 3 - 1;
+ os_memcpy(pos2, pos + 3, os_strlen(pos + 3));
+ if (!radius_msg_add_wfa(msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL,
+ (const u8 *) url2, url_len)) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL");
+ os_free(url2);
+ radius_msg_free(msg);
+ return NULL;
+ }
+ os_free(url2);
+
+ radius_srv_hs20_t_c_pending(sess);
+ }
#endif /* CONFIG_HS20 */
if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
@@ -841,12 +997,24 @@
}
}
+ if (code == RADIUS_CODE_ACCESS_REJECT) {
+ if (radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
+ reason) < 0) {
+ RADIUS_DEBUG("Failed to add WLAN-Reason-Code attribute");
+ radius_msg_free(msg);
+ return NULL;
+ }
+ }
+
if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
client->shared_secret_len,
hdr->authenticator) < 0) {
RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
}
+ if (code == RADIUS_CODE_ACCESS_ACCEPT)
+ radius_server_add_session(sess);
+
return msg;
}
@@ -993,6 +1161,51 @@
}
+static void radius_server_hs20_t_c_check(struct radius_session *sess,
+ struct radius_msg *msg)
+{
+#ifdef CONFIG_HS20
+ u8 *buf, *pos, *end, type, sublen, *timestamp = NULL;
+ size_t len;
+
+ buf = NULL;
+ for (;;) {
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ &buf, &len, buf) < 0)
+ break;
+ if (len < 6)
+ continue;
+ pos = buf;
+ end = buf + len;
+ if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+ continue;
+ pos += 4;
+
+ type = *pos++;
+ sublen = *pos++;
+ if (sublen < 2)
+ continue; /* invalid length */
+ sublen -= 2; /* skip header */
+ if (pos + sublen > end)
+ continue; /* invalid WFA VSA */
+
+ if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) {
+ timestamp = pos;
+ break;
+ }
+ }
+
+ if (!timestamp)
+ return;
+ RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp));
+ if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) {
+ RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering");
+ sess->t_c_filtering = 1;
+ }
+#endif /* CONFIG_HS20 */
+}
+
+
static int radius_server_request(struct radius_server_data *data,
struct radius_msg *msg,
struct sockaddr *from, socklen_t fromlen,
@@ -1128,6 +1341,9 @@
else if (sess->eap_if->eapSuccess)
srv_log(sess, "EAP authentication succeeded");
+ if (sess->eap_if->eapSuccess)
+ radius_server_hs20_t_c_check(sess, msg);
+
reply = radius_server_encapsulate_eap(data, client, sess, msg);
send_reply:
@@ -1189,6 +1405,116 @@
}
+static void
+radius_server_receive_disconnect_resp(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_msg *msg, int ack)
+{
+ struct radius_hdr *hdr;
+
+ if (!client->pending_dac_disconnect_req) {
+ RADIUS_DEBUG("Ignore unexpected Disconnect response");
+ radius_msg_free(msg);
+ return;
+ }
+
+ hdr = radius_msg_get_hdr(msg);
+ if (hdr->identifier != client->pending_dac_disconnect_id) {
+ RADIUS_DEBUG("Ignore unexpected Disconnect response with unexpected identifier %u (expected %u)",
+ hdr->identifier,
+ client->pending_dac_disconnect_id);
+ radius_msg_free(msg);
+ return;
+ }
+
+ if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
+ client->shared_secret_len,
+ client->pending_dac_disconnect_req, 0)) {
+ RADIUS_DEBUG("Ignore Disconnect response with invalid authenticator");
+ radius_msg_free(msg);
+ return;
+ }
+
+ RADIUS_DEBUG("Disconnect-%s received for " MACSTR,
+ ack ? "ACK" : "NAK",
+ MAC2STR(client->pending_dac_disconnect_addr));
+
+ radius_msg_free(msg);
+ radius_msg_free(client->pending_dac_disconnect_req);
+ client->pending_dac_disconnect_req = NULL;
+}
+
+
+static void radius_server_receive_coa_resp(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_msg *msg, int ack)
+{
+ struct radius_hdr *hdr;
+#ifdef CONFIG_SQLITE
+ char addrtxt[3 * ETH_ALEN];
+ char *sql;
+ int res;
+#endif /* CONFIG_SQLITE */
+
+ if (!client->pending_dac_coa_req) {
+ RADIUS_DEBUG("Ignore unexpected CoA response");
+ radius_msg_free(msg);
+ return;
+ }
+
+ hdr = radius_msg_get_hdr(msg);
+ if (hdr->identifier != client->pending_dac_coa_id) {
+ RADIUS_DEBUG("Ignore unexpected CoA response with unexpected identifier %u (expected %u)",
+ hdr->identifier,
+ client->pending_dac_coa_id);
+ radius_msg_free(msg);
+ return;
+ }
+
+ if (radius_msg_verify(msg, (const u8 *) client->shared_secret,
+ client->shared_secret_len,
+ client->pending_dac_coa_req, 0)) {
+ RADIUS_DEBUG("Ignore CoA response with invalid authenticator");
+ radius_msg_free(msg);
+ return;
+ }
+
+ RADIUS_DEBUG("CoA-%s received for " MACSTR,
+ ack ? "ACK" : "NAK",
+ MAC2STR(client->pending_dac_coa_addr));
+
+ radius_msg_free(msg);
+ radius_msg_free(client->pending_dac_coa_req);
+ client->pending_dac_coa_req = NULL;
+
+#ifdef CONFIG_SQLITE
+ if (!data->db)
+ return;
+
+ os_snprintf(addrtxt, sizeof(addrtxt), MACSTR,
+ MAC2STR(client->pending_dac_coa_addr));
+
+ if (ack) {
+ sql = sqlite3_mprintf("UPDATE current_sessions SET hs20_t_c_filtering=0, waiting_coa_ack=0, coa_ack_received=1 WHERE mac_addr=%Q",
+ addrtxt);
+ } else {
+ sql = sqlite3_mprintf("UPDATE current_sessions SET waiting_coa_ack=0 WHERE mac_addr=%Q",
+ addrtxt);
+ }
+ if (!sql)
+ return;
+
+ res = sqlite3_exec(data->db, sql, NULL, NULL, NULL);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ RADIUS_ERROR("Failed to update current_sessions entry: %s",
+ sqlite3_errmsg(data->db));
+ return;
+ }
+#endif /* CONFIG_SQLITE */
+}
+
+
static void radius_server_receive_auth(int sock, void *eloop_ctx,
void *sock_ctx)
{
@@ -1269,6 +1595,26 @@
radius_msg_dump(msg);
}
+ if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_ACK) {
+ radius_server_receive_disconnect_resp(data, client, msg, 1);
+ return;
+ }
+
+ if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_NAK) {
+ radius_server_receive_disconnect_resp(data, client, msg, 0);
+ return;
+ }
+
+ if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_ACK) {
+ radius_server_receive_coa_resp(data, client, msg, 1);
+ return;
+ }
+
+ if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_NAK) {
+ radius_server_receive_coa_resp(data, client, msg, 0);
+ return;
+ }
+
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) {
RADIUS_DEBUG("Unexpected RADIUS code %d",
radius_msg_get_hdr(msg)->code);
@@ -1529,6 +1875,8 @@
radius_server_free_sessions(data, prev->sessions);
os_free(prev->shared_secret);
+ radius_msg_free(prev->pending_dac_coa_req);
+ radius_msg_free(prev->pending_dac_disconnect_req);
os_free(prev);
}
}
@@ -1765,6 +2113,9 @@
}
data->subscr_remediation_method = conf->subscr_remediation_method;
+ if (conf->t_c_server_url)
+ data->t_c_server_url = os_strdup(conf->t_c_server_url);
+
#ifdef CONFIG_SQLITE
if (conf->sqlite_file) {
if (sqlite3_open(conf->sqlite_file, &data->db)) {
@@ -1881,6 +2232,7 @@
os_free(data->dump_msk_file);
#endif /* CONFIG_RADIUS_TEST */
os_free(data->subscr_remediation_url);
+ os_free(data->t_c_server_url);
#ifdef CONFIG_SQLITE
if (data->db)
@@ -2049,6 +2401,7 @@
sess->accept_attr = user->accept_attr;
sess->remediation = user->remediation;
sess->macacl = user->macacl;
+ sess->t_c_timestamp = user->t_c_timestamp;
}
if (ret) {
@@ -2175,3 +2528,212 @@
radius_msg_free(msg);
}
+
+
+#ifdef CONFIG_SQLITE
+
+struct db_session_fields {
+ char *identity;
+ char *nas;
+ int hs20_t_c_filtering;
+ int waiting_coa_ack;
+ int coa_ack_received;
+};
+
+
+static int get_db_session_fields(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct db_session_fields *fields = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (!argv[i])
+ continue;
+
+ RADIUS_DEBUG("Session DB: %s=%s", col[i], argv[i]);
+
+ if (os_strcmp(col[i], "identity") == 0) {
+ os_free(fields->identity);
+ fields->identity = os_strdup(argv[i]);
+ } else if (os_strcmp(col[i], "nas") == 0) {
+ os_free(fields->nas);
+ fields->nas = os_strdup(argv[i]);
+ } else if (os_strcmp(col[i], "hs20_t_c_filtering") == 0) {
+ fields->hs20_t_c_filtering = atoi(argv[i]);
+ } else if (os_strcmp(col[i], "waiting_coa_ack") == 0) {
+ fields->waiting_coa_ack = atoi(argv[i]);
+ } else if (os_strcmp(col[i], "coa_ack_received") == 0) {
+ fields->coa_ack_received = atoi(argv[i]);
+ }
+ }
+
+ return 0;
+}
+
+
+static void free_db_session_fields(struct db_session_fields *fields)
+{
+ os_free(fields->identity);
+ fields->identity = NULL;
+ os_free(fields->nas);
+ fields->nas = NULL;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
+int radius_server_dac_request(struct radius_server_data *data, const char *req)
+{
+#ifdef CONFIG_SQLITE
+ char *sql;
+ int res;
+ int disconnect;
+ const char *pos = req;
+ u8 addr[ETH_ALEN];
+ char addrtxt[3 * ETH_ALEN];
+ int t_c_clear = 0;
+ struct db_session_fields fields;
+ struct sockaddr_in das;
+ struct radius_client *client;
+ struct radius_msg *msg;
+ struct wpabuf *buf;
+ u8 identifier;
+ struct os_time now;
+
+ if (!data)
+ return -1;
+
+ /* req: <disconnect|coa> <MAC Address> [t_c_clear] */
+
+ if (os_strncmp(pos, "disconnect ", 11) == 0) {
+ disconnect = 1;
+ pos += 11;
+ } else if (os_strncmp(req, "coa ", 4) == 0) {
+ disconnect = 0;
+ pos += 4;
+ } else {
+ return -1;
+ }
+
+ if (hwaddr_aton(pos, addr))
+ return -1;
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ if (os_strstr(pos, "t_c_clear"))
+ t_c_clear = 1;
+ }
+
+ if (!disconnect && !t_c_clear) {
+ RADIUS_ERROR("DAC request for CoA without any authorization change");
+ return -1;
+ }
+
+ if (!data->db) {
+ RADIUS_ERROR("SQLite database not in use");
+ return -1;
+ }
+
+ os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(addr));
+
+ sql = sqlite3_mprintf("SELECT * FROM current_sessions WHERE mac_addr=%Q",
+ addrtxt);
+ if (!sql)
+ return -1;
+
+ os_memset(&fields, 0, sizeof(fields));
+ res = sqlite3_exec(data->db, sql, get_db_session_fields, &fields, NULL);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ RADIUS_ERROR("Failed to find matching current_sessions entry from sqlite database: %s",
+ sqlite3_errmsg(data->db));
+ free_db_session_fields(&fields);
+ return -1;
+ }
+
+ if (!fields.nas) {
+ RADIUS_ERROR("No NAS information found from current_sessions");
+ free_db_session_fields(&fields);
+ return -1;
+ }
+
+ os_memset(&das, 0, sizeof(das));
+ das.sin_family = AF_INET;
+ das.sin_addr.s_addr = inet_addr(fields.nas);
+ das.sin_port = htons(3799);
+
+ free_db_session_fields(&fields);
+
+ client = radius_server_get_client(data, &das.sin_addr, 0);
+ if (!client) {
+ RADIUS_ERROR("No NAS information available to protect the packet");
+ return -1;
+ }
+
+ identifier = client->next_dac_identifier++;
+
+ msg = radius_msg_new(disconnect ? RADIUS_CODE_DISCONNECT_REQUEST :
+ RADIUS_CODE_COA_REQUEST, identifier);
+ if (!msg)
+ return -1;
+
+ os_snprintf(addrtxt, sizeof(addrtxt), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) addrtxt, os_strlen(addrtxt))) {
+ RADIUS_ERROR("Could not add Calling-Station-Id");
+ radius_msg_free(msg);
+ return -1;
+ }
+
+ if (!disconnect && t_c_clear) {
+ u8 val[4] = { 0x00, 0x00, 0x00, 0x00 }; /* E=0 */
+
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
+ val, sizeof(val))) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering");
+ radius_msg_free(msg);
+ return -1;
+ }
+ }
+
+ os_get_time(&now);
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+ now.sec)) {
+ RADIUS_ERROR("Failed to add Event-Timestamp attribute");
+ radius_msg_free(msg);
+ return -1;
+ }
+
+ radius_msg_finish_acct(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len);
+
+ if (wpa_debug_level <= MSG_MSGDUMP)
+ radius_msg_dump(msg);
+
+ buf = radius_msg_get_buf(msg);
+ if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
+ (struct sockaddr *) &das, sizeof(das)) < 0) {
+ RADIUS_ERROR("Failed to send packet - sendto: %s",
+ strerror(errno));
+ radius_msg_free(msg);
+ return -1;
+ }
+
+ if (disconnect) {
+ radius_msg_free(client->pending_dac_disconnect_req);
+ client->pending_dac_disconnect_req = msg;
+ client->pending_dac_disconnect_id = identifier;
+ os_memcpy(client->pending_dac_disconnect_addr, addr, ETH_ALEN);
+ } else {
+ radius_msg_free(client->pending_dac_coa_req);
+ client->pending_dac_coa_req = msg;
+ client->pending_dac_coa_id = identifier;
+ os_memcpy(client->pending_dac_coa_addr, addr, ETH_ALEN);
+ }
+
+ return 0;
+#else /* CONFIG_SQLITE */
+ return -1;
+#endif /* CONFIG_SQLITE */
+}
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 996f00e..167bbf5 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -233,6 +233,8 @@
char *subscr_remediation_url;
u8 subscr_remediation_method;
+
+ char *t_c_server_url;
};
@@ -246,5 +248,6 @@
size_t buflen);
void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx);
+int radius_server_dac_request(struct radius_server_data *data, const char *req);
#endif /* RADIUS_SERVER_H */
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index f5024f2..fdd5220 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -96,7 +96,7 @@
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
- pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL);
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0);
if (entry) {
sec = pmksa->pmksa->reauth_time - now.sec;
if (sec < 0)
@@ -341,17 +341,20 @@
* @aa: Authenticator address or %NULL to match any
* @pmkid: PMKID or %NULL to match any
* @network_ctx: Network context or %NULL to match any
+ * @akmp: Specific AKMP to search for or 0 for any
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
*/
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *pmkid,
- const void *network_ctx)
+ const void *network_ctx,
+ int akmp)
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
while (entry) {
if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
(pmkid == NULL ||
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
+ (!akmp || akmp == entry->akmp) &&
(network_ctx == NULL || network_ctx == entry->network_ctx))
return entry;
entry = entry->next;
@@ -390,6 +393,7 @@
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @network_ctx: Network configuration context
* @aa: Authenticator address for the new AP
+ * @akmp: Specific AKMP to search for or 0 for any
* Returns: Pointer to a new PMKSA cache entry or %NULL if not available
*
* Try to create a new PMKSA cache entry opportunistically by guessing that the
@@ -398,7 +402,7 @@
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
- const u8 *aa)
+ const u8 *aa, int akmp)
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
@@ -406,7 +410,8 @@
if (network_ctx == NULL)
return NULL;
while (entry) {
- if (entry->network_ctx == network_ctx) {
+ if (entry->network_ctx == network_ctx &&
+ (!akmp || entry->akmp == akmp)) {
entry = pmksa_cache_clone_entry(pmksa, entry, aa);
if (entry) {
wpa_printf(MSG_DEBUG, "RSN: added "
@@ -476,11 +481,13 @@
*/
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid, void *network_ctx,
- int try_opportunistic, const u8 *fils_cache_id)
+ int try_opportunistic, const u8 *fils_cache_id,
+ int akmp)
{
struct rsn_pmksa_cache *pmksa = sm->pmksa;
wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
- "try_opportunistic=%d", network_ctx, try_opportunistic);
+ "try_opportunistic=%d akmp=0x%x",
+ network_ctx, try_opportunistic, akmp);
if (pmkid)
wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
pmkid, PMKID_LEN);
@@ -495,14 +502,14 @@
sm->cur_pmksa = NULL;
if (pmkid)
sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
- network_ctx);
+ network_ctx, akmp);
if (sm->cur_pmksa == NULL && bssid)
sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
- network_ctx);
+ network_ctx, akmp);
if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
network_ctx,
- bssid);
+ bssid, akmp);
if (sm->cur_pmksa == NULL && fils_cache_id)
sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa,
network_ctx,
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index f9a72a6..6c49fa9 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -61,7 +61,8 @@
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *pmkid,
- const void *network_ctx);
+ const void *network_ctx,
+ int akmp);
int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry *
@@ -76,10 +77,11 @@
void pmksa_cache_clear_current(struct wpa_sm *sm);
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid, void *network_ctx,
- int try_opportunistic, const u8 *fils_cache_id);
+ int try_opportunistic, const u8 *fils_cache_id,
+ int akmp);
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
- void *network_ctx, const u8 *aa);
+ void *network_ctx, const u8 *aa, int akmp);
void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *pmk, size_t pmk_len);
@@ -99,7 +101,7 @@
static inline struct rsn_pmksa_cache_entry *
pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid,
- const void *network_ctx)
+ const void *network_ctx, int akmp)
{
return NULL;
}
@@ -146,7 +148,8 @@
const u8 *bssid,
void *network_ctx,
int try_opportunistic,
- const u8 *fils_cache_id)
+ const u8 *fils_cache_id,
+ int akmp)
{
return -1;
}
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index d4276b9..d0c43f4 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -323,7 +323,7 @@
dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
struct rsn_pmksa_candidate, list) {
struct rsn_pmksa_cache_entry *p = NULL;
- p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL);
+ p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL, 0);
if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
(p == NULL || p->opportunistic)) {
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
@@ -372,7 +372,7 @@
if (sm->network_ctx && sm->proactive_key_caching)
pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx,
- bssid);
+ bssid, 0);
if (!preauth) {
wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
@@ -483,7 +483,7 @@
if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
return;
- pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL);
+ pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL, 0);
if (pmksa && (!pmksa->opportunistic ||
!(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
return;
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 5e350ed..14b346a 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -35,6 +35,7 @@
#define TDLS_TESTING_DECLINE_RESP BIT(9)
#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
#define TDLS_TESTING_WRONG_MIC BIT(11)
+#define TDLS_TESTING_DOUBLE_TPK_M2 BIT(12)
unsigned int tdls_testing = 0;
#endif /* CONFIG_TDLS_TESTING */
@@ -2124,6 +2125,13 @@
goto error;
}
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_DOUBLE_TPK_M2) {
+ wpa_printf(MSG_INFO, "TDLS: Testing - Send another TPK M2");
+ wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer);
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
return 0;
error:
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index ff4a6ed..eab2921 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - WPA state machine and EAPOL-Key processing
- * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
* Copyright(c) 2015 Intel Deutschland GmbH
*
* This software may be distributed under the terms of the BSD license.
@@ -181,8 +181,7 @@
int key_info, ver;
u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
- if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->key_mgmt))
+ if (wpa_use_akm_defined(sm->key_mgmt))
ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
wpa_key_mgmt_sha256(sm->key_mgmt))
@@ -269,7 +268,7 @@
* event before receiving this 1/4 message, so try to find a
* matching PMKSA cache entry here. */
sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
- NULL);
+ NULL, 0);
if (sm->cur_pmksa) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: found matching PMKID from PMKSA cache");
@@ -289,6 +288,18 @@
eapol_sm_notify_cached(sm->eapol);
#ifdef CONFIG_IEEE80211R
sm->xxkey_len = 0;
+#ifdef CONFIG_SAE
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE &&
+ sm->pmk_len == PMK_LEN) {
+ /* Need to allow FT key derivation to proceed with
+ * PMK from SAE being used as the XXKey in cases where
+ * the PMKID in msg 1/4 matches the PMKSA entry that was
+ * just added based on SAE authentication for the
+ * initial mobility domain association. */
+ os_memcpy(sm->xxkey, sm->pmk, sm->pmk_len);
+ sm->xxkey_len = sm->pmk_len;
+ }
+#endif /* CONFIG_SAE */
#endif /* CONFIG_IEEE80211R */
} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
int res, pmk_len;
@@ -312,8 +323,15 @@
u8 buf[2 * PMK_LEN];
if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
{
- os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
- sm->xxkey_len = PMK_LEN;
+ if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+ os_memcpy(sm->xxkey, buf,
+ SHA384_MAC_LEN);
+ sm->xxkey_len = SHA384_MAC_LEN;
+ } else {
+ os_memcpy(sm->xxkey, buf + PMK_LEN,
+ PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ }
os_memset(buf, 0, sizeof(buf));
}
#endif /* CONFIG_IEEE80211R */
@@ -343,8 +361,8 @@
fils_cache_id);
}
if (!sm->cur_pmksa && pmkid &&
- pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
- {
+ pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL,
+ 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: the new PMK matches with the "
"PMKID");
@@ -1739,7 +1757,7 @@
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
/*
* This assures the same TPTK in sm->tptk can never be
- * copied twice to sm->pkt as the new PTK. In
+ * copied twice to sm->ptk as the new PTK. In
* combination with the installed flag in the wpa_ptk
* struct, this assures the same PTK is only installed
* once.
@@ -1814,10 +1832,7 @@
#endif /* CONFIG_NO_RC4 */
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
- sm->key_mgmt == WPA_KEY_MGMT_OWE ||
- sm->key_mgmt == WPA_KEY_MGMT_DPP ||
- sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ wpa_use_aes_key_wrap(sm->key_mgmt)) {
u8 *buf;
wpa_printf(MSG_DEBUG,
@@ -2094,29 +2109,14 @@
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
- !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
- !wpa_key_mgmt_fils(sm->key_mgmt) &&
- sm->key_mgmt != WPA_KEY_MGMT_OWE &&
- sm->key_mgmt != WPA_KEY_MGMT_DPP &&
- sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
+ !wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Unsupported EAPOL-Key descriptor version %d",
ver);
goto out;
}
- if (sm->key_mgmt == WPA_KEY_MGMT_OSEN &&
- ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "OSEN: Unsupported EAPOL-Key descriptor version %d",
- ver);
- goto out;
- }
-
- if ((wpa_key_mgmt_suite_b(sm->key_mgmt) ||
- wpa_key_mgmt_fils(sm->key_mgmt) ||
- sm->key_mgmt == WPA_KEY_MGMT_DPP ||
- sm->key_mgmt == WPA_KEY_MGMT_OWE) &&
+ if (wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
@@ -2127,7 +2127,8 @@
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
/* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
- if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+ !wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"FT: AP did not use AES-128-CMAC");
goto out;
@@ -2137,9 +2138,7 @@
#ifdef CONFIG_IEEE80211W
if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
- sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
- !wpa_key_mgmt_fils(sm->key_mgmt) &&
- !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ !wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: AP did not use the "
"negotiated AES-128-CMAC");
@@ -2148,10 +2147,7 @@
} else
#endif /* CONFIG_IEEE80211W */
if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
- !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
- !wpa_key_mgmt_fils(sm->key_mgmt) &&
- sm->key_mgmt != WPA_KEY_MGMT_OWE &&
- sm->key_mgmt != WPA_KEY_MGMT_DPP &&
+ !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 "
@@ -2171,7 +2167,7 @@
} else
goto out;
} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
- !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ !wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: GCMP is used, but EAPOL-Key "
@@ -2427,6 +2423,7 @@
}
if (deauth) {
+ sm->pmk_len = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
}
@@ -3140,7 +3137,7 @@
int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
const void *network_ctx)
{
- return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx) != NULL;
+ return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL;
}
@@ -3149,6 +3146,7 @@
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
sm->ptk_set = 0;
sm->tptk_set = 0;
+ sm->pmk_len = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
os_memset(&sm->ptk, 0, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
@@ -3160,8 +3158,11 @@
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+ sm->xxkey_len = 0;
os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
+ sm->pmk_r0_len = 0;
os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+ sm->pmk_r1_len = 0;
#endif /* CONFIG_IEEE80211R */
}
@@ -3306,6 +3307,12 @@
#endif /* CONFIG_TESTING_OPTIONS */
+unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm)
+{
+ return sm->key_mgmt;
+}
+
+
#ifdef CONFIG_FILS
struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
@@ -3531,6 +3538,7 @@
os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
+#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
struct wpa_ft_ies parse;
@@ -3539,7 +3547,8 @@
goto fail;
}
- if (wpa_ft_parse_ies(pos, end - pos, &parse) < 0) {
+ if (wpa_ft_parse_ies(pos, end - pos, &parse,
+ wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) {
wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
goto fail;
}
@@ -3578,6 +3587,7 @@
wpabuf_free(sm->fils_ft_ies);
sm->fils_ft_ies = NULL;
}
+#endif /* CONFIG_IEEE80211R */
/* PMKID List */
if (rsn.pmkid && rsn.num_pmkid > 0) {
@@ -3738,6 +3748,7 @@
struct rsn_ie_hdr *rsnie;
u16 capab;
u8 *pos;
+ int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
/* RSNIE[PMKR0Name/PMKR1Name] */
rsnie = wpabuf_put(buf, sizeof(*rsnie));
@@ -3805,18 +3816,20 @@
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) < 0) {
+ sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) {
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
return -1;
}
- wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+ sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
+ sm->pmk_r0, sm->pmk_r0_len);
wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
sm->pmk_r0_name, WPA_PMK_NAME_LEN);
wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
MAC2STR(sm->r1kh_id));
pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
- pos) < 0) {
+ pos, use_sha384) < 0) {
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
return -1;
}
@@ -4378,8 +4391,10 @@
res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
os_strlen(info), sm->pmk, hash_len);
os_memset(prk, 0, SHA512_MAC_LEN);
- if (res < 0)
+ if (res < 0) {
+ sm->pmk_len = 0;
return -1;
+ }
sm->pmk_len = hash_len;
wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len);
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 81c0171..21f4b17 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -353,6 +353,9 @@
int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len);
int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie);
+int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len,
+ const u8 *mdie);
+const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm);
int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
int ft_action, const u8 *target_ap,
const u8 *ric_ies, size_t ric_ies_len);
@@ -377,6 +380,12 @@
return 0;
}
+static inline int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len,
+ const u8 *mdie)
+{
+ return 0;
+}
+
static inline int
wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
int ft_action, const u8 *target_ap)
@@ -429,6 +438,7 @@
int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
const u8 * wpa_sm_get_anonce(struct wpa_sm *sm);
+unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm);
struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md);
int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 1ff7afe..b8d60e3 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +10,7 @@
#include "common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/sha384.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
@@ -23,6 +24,7 @@
{
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *anonce = key->key_nonce;
+ int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
if (sm->xxkey_len == 0) {
wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -30,21 +32,26 @@
return -1;
}
- wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
- sm->ssid_len, sm->mobility_domain,
- sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
- sm->pmk_r0, sm->pmk_r0_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+ sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+ if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
+ sm->ssid_len, sm->mobility_domain,
+ sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+ sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, sm->pmk_r0_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
sm->pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
- sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ sm->pmk_r1_len = sm->pmk_r0_len;
+ if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
+ sm->r1kh_id, sm->own_addr, sm->pmk_r1,
+ sm->pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
WPA_PMK_NAME_LEN);
- return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
- sm->bssid, sm->pmk_r1_name, ptk, ptk_name,
- sm->key_mgmt, sm->pairwise_cipher);
+ return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
+ sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
+ ptk_name, sm->key_mgmt, sm->pairwise_cipher);
}
@@ -58,11 +65,13 @@
int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
{
struct wpa_ft_ies ft;
+ int use_sha384;
if (sm == NULL)
return 0;
- if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0)
+ use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+ if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0)
return -1;
if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
@@ -146,16 +155,17 @@
const u8 *ap_mdie)
{
size_t buf_len;
- u8 *buf, *pos, *ftie_len, *ftie_pos;
+ u8 *buf, *pos, *ftie_len, *ftie_pos, *fte_mic, *elem_count;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
struct rsn_ie_hdr *rsnie;
u16 capab;
+ int mdie_len;
sm->ft_completed = 0;
sm->ft_reassoc_completed = 0;
- buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+ buf_len = 2 + sizeof(struct rsn_mdie) + 2 +
+ sizeof(struct rsn_ftie_sha384) +
2 + sm->r0kh_id_len + ric_ies_len + 100;
buf = os_zalloc(buf_len);
if (buf == NULL)
@@ -201,6 +211,10 @@
/* Authenticated Key Management Suite List */
if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+#ifdef CONFIG_SHA384
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+#endif /* CONFIG_SHA384 */
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
@@ -222,7 +236,10 @@
/* RSN Capabilities */
capab = 0;
#ifdef CONFIG_IEEE80211W
- if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ||
+ sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 ||
+ sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256 ||
+ sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256)
capab |= WPA_CAPABILITY_MFPC;
#endif /* CONFIG_IEEE80211W */
WPA_PUT_LE16(pos, capab);
@@ -237,34 +254,63 @@
pos += WPA_PMK_NAME_LEN;
#ifdef CONFIG_IEEE80211W
- if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
- /* Management Group Cipher Suite */
+ /* Management Group Cipher Suite */
+ switch (sm->mgmt_group_cipher) {
+ case WPA_CIPHER_AES_128_CMAC:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
pos += RSN_SELECTOR_LEN;
+ break;
+ case WPA_CIPHER_BIP_GMAC_128:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
+ pos += RSN_SELECTOR_LEN;
+ break;
+ case WPA_CIPHER_BIP_GMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
+ pos += RSN_SELECTOR_LEN;
+ break;
+ case WPA_CIPHER_BIP_CMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
+ pos += RSN_SELECTOR_LEN;
+ break;
}
#endif /* CONFIG_IEEE80211W */
rsnie->len = (pos - (u8 *) rsnie) - 2;
/* MDIE */
- *pos++ = WLAN_EID_MOBILITY_DOMAIN;
- *pos++ = sizeof(*mdie);
- mdie = (struct rsn_mdie *) pos;
- pos += sizeof(*mdie);
- os_memcpy(mdie->mobility_domain, sm->mobility_domain,
- MOBILITY_DOMAIN_ID_LEN);
- mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
- sm->mdie_ft_capab;
+ mdie_len = wpa_ft_add_mdie(sm, pos, buf_len - (pos - buf), ap_mdie);
+ if (mdie_len <= 0) {
+ os_free(buf);
+ return NULL;
+ }
+ mdie = (struct rsn_mdie *) (pos + 2);
+ pos += mdie_len;
/* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
ftie_pos = pos;
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ftie_len = pos++;
- ftie = (struct rsn_ftie *) pos;
- pos += sizeof(*ftie);
- os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
- if (anonce)
- os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) pos;
+ fte_mic = ftie->mic;
+ elem_count = &ftie->mic_control[1];
+ pos += sizeof(*ftie);
+ os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+ if (anonce)
+ os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) pos;
+ fte_mic = ftie->mic;
+ elem_count = &ftie->mic_control[1];
+ pos += sizeof(*ftie);
+ os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+ if (anonce)
+ os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ }
if (kck) {
/* R1KH-ID sub-element in third FT message */
*pos++ = FTIE_SUBELEM_R1KH_ID;
@@ -298,13 +344,12 @@
* RIC-Request (if present)
*/
/* Information element count */
- ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
- ric_ies_len);
+ *elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len);
if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
((u8 *) mdie) - 2, 2 + sizeof(*mdie),
ftie_pos, 2 + *ftie_len,
(u8 *) rsnie, 2 + rsnie->len, ric_ies,
- ric_ies_len, ftie->mic) < 0) {
+ ric_ies_len, fte_mic) < 0) {
wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
os_free(buf);
return NULL;
@@ -373,6 +418,37 @@
}
+int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *buf, size_t buf_len,
+ const u8 *ap_mdie)
+{
+ u8 *pos = buf;
+ struct rsn_mdie *mdie;
+
+ if (buf_len < 2 + sizeof(*mdie)) {
+ wpa_printf(MSG_INFO,
+ "FT: Failed to add MDIE: short buffer, length=%zu",
+ buf_len);
+ return 0;
+ }
+
+ *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+ *pos++ = sizeof(*mdie);
+ mdie = (struct rsn_mdie *) pos;
+ os_memcpy(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+ mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
+ sm->mdie_ft_capab;
+
+ return 2 + sizeof(*mdie);
+}
+
+
+const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm)
+{
+ return sm->mobility_domain;
+}
+
+
int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
int ft_action, const u8 *target_ap,
const u8 *ric_ies, size_t ric_ies_len)
@@ -381,10 +457,13 @@
size_t ft_ies_len;
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
u8 ptk_name[WPA_PMK_NAME_LEN];
int ret;
const u8 *bssid;
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+ const u8 *anonce, *snonce;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
@@ -410,7 +489,7 @@
return -1;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
@@ -423,16 +502,34 @@
return -1;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return -1;
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
}
- if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- ftie->snonce, WPA_NONCE_LEN);
+ snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
return -1;
@@ -471,23 +568,34 @@
os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN);
- os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN);
- wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
- sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
+ os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN);
+ if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
+ sm->r1kh_id, sm->own_addr, sm->pmk_r1,
+ sm->pmk_r1_name) < 0)
+ return -1;
+ sm->pmk_r1_len = sm->pmk_r0_len;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
bssid = target_ap;
- if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce,
- sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk,
- ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0)
+ if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
+ anonce, sm->own_addr, bssid,
+ sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
+ sm->pairwise_cipher) < 0)
return -1;
- ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
+ if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+ kck = sm->ptk.kck2;
+ kck_len = sm->ptk.kck2_len;
+ } else {
+ kck = sm->ptk.kck;
+ kck_len = sm->ptk.kck_len;
+ }
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce,
sm->pmk_r1_name,
- sm->ptk.kck, sm->ptk.kck_len, bssid,
+ kck, kck_len, bssid,
ric_ies, ric_ies_len,
parse.mdie ? parse.mdie - 2 : NULL);
if (ft_ies) {
@@ -550,6 +658,16 @@
int keyidx;
enum wpa_alg alg;
size_t gtk_len, keylen, rsc_len;
+ const u8 *kek;
+ size_t kek_len;
+
+ if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+ kek = sm->ptk.kek2;
+ kek_len = sm->ptk.kek2_len;
+ } else {
+ kek = sm->ptk.kek;
+ kek_len = sm->ptk.kek_len;
+ }
if (gtk_elem == NULL) {
wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
@@ -566,8 +684,7 @@
return -1;
}
gtk_len = gtk_elem_len - 19;
- if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11,
- gtk)) {
+ if (aes_unwrap(kek, kek_len, gtk_len / 8, gtk_elem + 11, gtk)) {
wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
"decrypt GTK");
return -1;
@@ -621,10 +738,24 @@
static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
size_t igtk_elem_len)
{
- u8 igtk[WPA_IGTK_LEN];
+ u8 igtk[WPA_IGTK_MAX_LEN];
+ size_t igtk_len;
u16 keyidx;
+ const u8 *kek;
+ size_t kek_len;
- if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+ if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+ kek = sm->ptk.kek2;
+ kek_len = sm->ptk.kek2_len;
+ } else {
+ kek = sm->ptk.kek;
+ kek_len = sm->ptk.kek_len;
+ }
+
+ if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
+ sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
+ sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
+ sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256)
return 0;
if (igtk_elem == NULL) {
@@ -635,19 +766,19 @@
wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
igtk_elem, igtk_elem_len);
- if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) {
+ igtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ if (igtk_elem_len != 2 + 6 + 1 + igtk_len + 8) {
wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem "
"length %lu", (unsigned long) igtk_elem_len);
return -1;
}
- if (igtk_elem[8] != WPA_IGTK_LEN) {
+ if (igtk_elem[8] != igtk_len) {
wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length "
"%d", igtk_elem[8]);
return -1;
}
- if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8,
- igtk_elem + 9, igtk)) {
+ if (aes_unwrap(kek, kek_len, igtk_len / 8, igtk_elem + 9, igtk)) {
wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
"decrypt IGTK");
return -1;
@@ -658,13 +789,16 @@
keyidx = WPA_GET_LE16(igtk_elem);
wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
- WPA_IGTK_LEN);
- if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0,
- igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) {
+ igtk_len);
+ if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr, keyidx, 0,
+ igtk_elem + 2, 6, igtk, igtk_len) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
"driver.");
+ os_memset(igtk, 0, sizeof(igtk));
return -1;
}
+ os_memset(igtk, 0, sizeof(igtk));
return 0;
}
@@ -676,9 +810,13 @@
{
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
unsigned int count;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+ const u8 *anonce, *snonce, *fte_mic;
+ u8 fte_elem_count;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
@@ -693,7 +831,7 @@
return 0;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
@@ -706,25 +844,47 @@
return -1;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return -1;
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
}
- if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- ftie->snonce, WPA_NONCE_LEN);
+ snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
return -1;
}
- if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- ftie->anonce, WPA_NONCE_LEN);
+ anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->anonce, WPA_NONCE_LEN);
return -1;
@@ -769,14 +929,22 @@
count = 3;
if (parse.ric)
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
- if (ftie->mic_control[1] != count) {
+ if (fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- ftie->mic_control[1], count);
+ fte_elem_count, count);
return -1;
}
- if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6,
+ if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+ kck = sm->ptk.kck2;
+ kck_len = sm->ptk.kck2_len;
+ } else {
+ kck = sm->ptk.kck;
+ kck_len = sm->ptk.kck_len;
+ }
+
+ if (wpa_ft_mic(kck, kck_len, sm->own_addr, src_addr, 6,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
@@ -786,9 +954,9 @@
return -1;
}
- if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
+ if (os_memcmp_const(mic, fte_mic, 16) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
- wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
return -1;
}
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index e8da194..b94b17a 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -1,6 +1,6 @@
/*
* Internal WPA/RSN supplicant state machine definitions
- * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -112,11 +112,14 @@
#endif /* CONFIG_TDLS */
#ifdef CONFIG_IEEE80211R
- u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+ u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
+ * first 384 bits of MSK */
size_t xxkey_len;
- u8 pmk_r0[PMK_LEN];
+ u8 pmk_r0[PMK_LEN_MAX];
+ size_t pmk_r0_len;
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 pmk_r1[PMK_LEN];
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index d649058..a3410d1 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -1,6 +1,6 @@
/*
* wpa_supplicant - WPA/RSN IE and KDE processing
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -161,6 +161,10 @@
#ifdef CONFIG_IEEE80211R
} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+#ifdef CONFIG_SHA384
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+#endif /* CONFIG_SHA384 */
} else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
#endif /* CONFIG_IEEE80211R */
@@ -200,6 +204,10 @@
} else if (key_mgmt & WPA_KEY_MGMT_DPP) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ } else if (key_mgmt & WPA_KEY_MGMT_OSEN) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+#endif /* CONFIG_HS20 */
} else {
wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
key_mgmt);
diff --git a/src/utils/common.h b/src/utils/common.h
index 46e96a6..f824d00 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -150,6 +150,7 @@
#define host_to_le32(n) (n)
#define be_to_host32(n) wpa_swap_32(n)
#define host_to_be32(n) wpa_swap_32(n)
+#define host_to_le64(n) (n)
#define WPA_BYTE_SWAP_DEFINED
@@ -435,6 +436,7 @@
#define __bitwise __attribute__((bitwise))
#else
#define __force
+#undef __bitwise
#define __bitwise
#endif
diff --git a/src/utils/json.c b/src/utils/json.c
index eae627d..b9130d3 100644
--- a/src/utils/json.c
+++ b/src/utils/json.c
@@ -230,6 +230,8 @@
token = json_alloc_token(&tokens);
if (!token)
goto fail;
+ if (!root)
+ root = token;
} else if (curr_token->state == JSON_WAITING_VALUE) {
token = curr_token;
} else if (curr_token->parent &&