--- contrib/wpa/CONTRIBUTIONS.orig +++ contrib/wpa/CONTRIBUTIONS @@ -29,6 +29,34 @@ unfortunately be accepted. +The preferred method of submitting the contribution to the project is by +email to the hostap mailing list: +hostap@lists.infradead.org +Note that the list may require subscription before accepting message +without moderation. You can subscribe to the list at this address: +http://lists.infradead.org/mailman/listinfo/hostap + +The message should contain an inlined patch against the current +development branch (i.e., the master branch of +git://w1.fi/hostap.git). Please make sure the software you use for +sending the patch does not corrupt whitespace. If that cannot be fixed +for some reason, it is better to include an attached version of the +patch file than just send a whitespace damaged version in the message +body. + +The patches should be separate logical changes rather than doing +everything in a single patch. In other words, please keep cleanup, new +features, and bug fixes all in their own patches. Each patch needs a +commit log that describes the changes (what the changes fix, what +functionality is added, why the changes are useful, etc.). + +Please try to follow the coding style used in the project. + +In general, the best way of generating a suitable formatted patch file +is by committing the changes to a cloned git repository and using git +format-patch. The patch can then be sent, e.g., with git send-email. + + History of license and contributions terms ------------------------------------------ @@ -112,7 +140,7 @@ Modified BSD license (no advertisement clause): -Copyright (c) 2002-2015, Jouni Malinen and contributors +Copyright (c) 2002-2019, Jouni Malinen and contributors All Rights Reserved. Redistribution and use in source and binary forms, with or without --- contrib/wpa/COPYING.orig +++ contrib/wpa/COPYING @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2015, Jouni Malinen and contributors +Copyright (c) 2002-2019, Jouni Malinen and contributors All Rights Reserved. --- contrib/wpa/README.orig +++ contrib/wpa/README @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2015, Jouni Malinen and contributors +Copyright (c) 2002-2019, Jouni Malinen and contributors All Rights Reserved. These programs are licensed under the BSD license (the one with --- contrib/wpa/hostapd/ChangeLog.orig +++ contrib/wpa/hostapd/ChangeLog @@ -1,5 +1,188 @@ ChangeLog for hostapd +2019-04-21 - v2.8 + * SAE changes + - added support for SAE Password Identifier + - changed default configuration to enable only group 19 + (i.e., disable groups 20, 21, 25, 26 from default configuration) and + disable all unsuitable groups completely based on REVmd changes + - improved anti-clogging token mechanism and SAE authentication + frame processing during heavy CPU load; this mitigates some issues + with potential DoS attacks trying to flood an AP with large number + of SAE messages + - added Finite Cyclic Group field in status code 77 responses + - reject use of unsuitable groups based on new implementation guidance + in REVmd (allow only FFC groups with prime >= 3072 bits and ECC + groups with prime >= 256) + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-1/] (CVE-2019-9494) + - fixed confirm message validation in error cases + [https://w1.fi/security/2019-3/] (CVE-2019-9496) + * EAP-pwd changes + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-2/] (CVE-2019-9495) + - verify peer scalar/element + [https://w1.fi/security/2019-4/] (CVE-2019-9497 and CVE-2019-9498) + - fix message reassembly issue with unexpected fragment + [https://w1.fi/security/2019-5/] + - enforce rand,mask generation rules more strictly + - fix a memory leak in PWE derivation + - disallow ECC groups with a prime under 256 bits (groups 25, 26, and + 27) + * Hotspot 2.0 changes + - added support for release number 3 + - reject release 2 or newer association without PMF + * added support for RSN operating channel validation + (CONFIG_OCV=y and configuration parameter ocv=1) + * added Multi-AP protocol support + * added FTM responder configuration + * fixed build with LibreSSL + * added FT/RRB workaround for short Ethernet frame padding + * fixed KEK2 derivation for FILS+FT + * added RSSI-based association rejection from OCE + * extended beacon reporting functionality + * VLAN changes + - allow local VLAN management with remote RADIUS authentication + - add WPA/WPA2 passphrase/PSK -based VLAN assignment + * OpenSSL: allow systemwide policies to be overridden + * extended PEAP to derive EMSK to enable use with ERP/FILS + * extended WPS to allow SAE configuration to be added automatically + for PSK (wps_cred_add_sae=1) + * fixed FT and SA Query Action frame with AP-MLME-in-driver cases + * OWE: allow Diffie-Hellman Parameter element to be included with DPP + in preparation for DPP protocol extension + * RADIUS server: started to accept ERP keyName-NAI as user identity + automatically without matching EAP database entry + * fixed PTK rekeying with FILS and FT + +2018-12-02 - v2.7 + * fixed WPA packet number reuse with replayed messages and key + reinstallation + [http://w1.fi/security/2017-1/] (CVE-2017-13082) + * added support for FILS (IEEE 802.11ai) shared key authentication + * added support for OWE (Opportunistic Wireless Encryption, RFC 8110; + and transition mode defined by WFA) + * added support for DPP (Wi-Fi Device Provisioning Protocol) + * FT: + - added local generation of PMK-R0/PMK-R1 for FT-PSK + (ft_psk_generate_local=1) + - replaced inter-AP protocol with a cleaner design that is more + easily extensible; this breaks backward compatibility and requires + all APs in the ESS to be updated at the same time to maintain FT + functionality + - added support for wildcard R0KH/R1KH + - replaced r0_key_lifetime (minutes) parameter with + ft_r0_key_lifetime (seconds) + - fixed wpa_psk_file use for FT-PSK + - fixed FT-SAE PMKID matching + - added expiration to PMK-R0 and PMK-R1 cache + - added IEEE VLAN support (including tagged VLANs) + - added support for SHA384 based AKM + * SAE + - fixed some PMKSA caching cases with SAE + - added support for configuring SAE password separately of the + WPA2 PSK/passphrase + - added option to require MFP for SAE associations + (sae_require_pmf=1) + - fixed PTK and EAPOL-Key integrity and key-wrap algorithm selection + for SAE; + note: this is not backwards compatible, i.e., both the AP and + station side implementations will need to be update at the same + time to maintain interoperability + - added support for Password Identifier + * hostapd_cli: added support for command history and completion + * added support for requesting beacon report + * large number of other fixes, cleanup, and extensions + * added option to configure EAPOL-Key retry limits + (wpa_group_update_count and wpa_pairwise_update_count) + * removed all PeerKey functionality + * fixed nl80211 AP mode configuration regression with Linux 4.15 and + newer + * added support for using wolfSSL cryptographic library + * fixed some 20/40 MHz coexistence cases where the BSS could drop to + 20 MHz even when 40 MHz would be allowed + * Hotspot 2.0 + - added support for setting Venue URL ANQP-element (venue_url) + - added support for advertising Hotspot 2.0 operator icons + - added support for Roaming Consortium Selection element + - added support for Terms and Conditions + - added support for OSEN connection in a shared RSN BSS + * added support for using OpenSSL 1.1.1 + * added EAP-pwd server support for salted passwords + +2016-10-02 - v2.6 + * fixed EAP-pwd last fragment validation + [http://w1.fi/security/2015-7/] (CVE-2015-5314) + * fixed WPS configuration update vulnerability with malformed passphrase + [http://w1.fi/security/2016-1/] (CVE-2016-4476) + * extended channel switch support for VHT bandwidth changes + * added support for configuring new ANQP-elements with + anqp_elem=: + * fixed Suite B 192-bit AKM to use proper PMK length + (note: this makes old releases incompatible with the fixed behavior) + * added no_probe_resp_if_max_sta=1 parameter to disable Probe Response + frame sending for not-associated STAs if max_num_sta limit has been + reached + * added option (-S as command line argument) to request all interfaces + to be started at the same time + * modified rts_threshold and fragm_threshold configuration parameters + to allow -1 to be used to disable RTS/fragmentation + * EAP-pwd: added support for Brainpool Elliptic Curves + (with OpenSSL 1.0.2 and newer) + * fixed EAPOL reauthentication after FT protocol run + * fixed FTIE generation for 4-way handshake after FT protocol run + * fixed and improved various FST operations + * TLS server + - support SHA384 and SHA512 hashes + - support TLS v1.2 signature algorithm with SHA384 and SHA512 + - support PKCS #5 v2.0 PBES2 + - support PKCS #5 with PKCS #12 style key decryption + - minimal support for PKCS #12 + - support OCSP stapling (including ocsp_multi) + * added support for OpenSSL 1.1 API changes + - drop support for OpenSSL 0.9.8 + - drop support for OpenSSL 1.0.0 + * EAP-PEAP: support fast-connect crypto binding + * RADIUS + - fix Called-Station-Id to not escape SSID + - add Event-Timestamp to all Accounting-Request packets + - add Acct-Session-Id to Accounting-On/Off + - add Acct-Multi-Session-Id ton Access-Request packets + - add Service-Type (= Frames) + - allow server to provide PSK instead of passphrase for WPA-PSK + Tunnel_password case + - update full message for interim accounting updates + - add Acct-Delay-Time into Accounting messages + - add require_message_authenticator configuration option to require + CoA/Disconnect-Request packets to be authenticated + * started to postpone WNM-Notification frame sending by 100 ms so that + the STA has some more time to configure the key before this frame is + received after the 4-way handshake + * VHT: added interoperability workaround for 80+80 and 160 MHz channels + * extended VLAN support (per-STA vif, etc.) + * fixed PMKID derivation with SAE + * nl80211 + - added support for full station state operations + - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use + unencrypted EAPOL frames + * added initial MBO support; number of extensions to WNM BSS Transition + Management + * added initial functionality for location related operations + * added assocresp_elements parameter to allow vendor specific elements + to be added into (Re)Association Response frames + * improved Public Action frame addressing + - use Address 3 = wildcard BSSID in GAS response if a query from an + unassociated STA used that address + - fix TX status processing for Address 3 = wildcard BSSID + - add gas_address3 configuration parameter to control Address 3 + behavior + * added command line parameter -i to override interface parameter in + hostapd.conf + * added command completion support to hostapd_cli + * added passive client taxonomy determination (CONFIG_TAXONOMY=y + compile option and "SIGNATURE " control interface command) + * number of small fixes + 2015-09-27 - v2.5 * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding [http://w1.fi/security/2015-2/] (CVE-2015-4141) --- contrib/wpa/hostapd/README.orig +++ contrib/wpa/hostapd/README @@ -2,7 +2,7 @@ Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2015, Jouni Malinen and contributors +Copyright (c) 2002-2019, Jouni Malinen and contributors All Rights Reserved. This program is licensed under the BSD license (the one with @@ -70,7 +70,7 @@ Current hardware/software requirements: - drivers: Host AP driver for Prism2/2.5/3. - (http://hostap.epitest.fi/) + (http://w1.fi/hostap-driver.html) Please note that station firmware version needs to be 1.7.0 or newer to work in WPA mode. @@ -81,8 +81,7 @@ Any wired Ethernet driver for wired IEEE 802.1X authentication (experimental code) - FreeBSD -current (with some kernel mods that have not yet been - committed when hostapd v0.3.0 was released) + FreeBSD -current BSD net80211 layer (e.g., Atheros driver) @@ -186,24 +185,14 @@ the Authentication Server. Other than this, the functionality is similar to the case with the co-located Authentication Server. -Authentication Server and Supplicant ------------------------------------- +Authentication Server +--------------------- Any RADIUS server supporting EAP should be usable as an IEEE 802.1X Authentication Server with hostapd Authenticator. FreeRADIUS (http://www.freeradius.org/) has been successfully tested with hostapd -Authenticator and both Xsupplicant (http://www.open1x.org) and Windows -XP Supplicants. EAP/TLS was used with Xsupplicant and -EAP/MD5-Challenge with Windows XP. +Authenticator. -http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information -about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace -Cisco access point with Host AP driver, hostapd daemon, and a Prism2 -card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information -about using EAP/MD5 with FreeRADIUS, including instructions for WinXP -configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on -EAP/TLS use with WinXP Supplicant. - Automatic WEP key configuration ------------------------------- @@ -243,8 +232,8 @@ of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked to address the flaws of the base standard and has in practice completed its work in May 2004. The IEEE 802.11i amendment to the IEEE -802.11 standard was approved in June 2004 and this amendment is likely -to be published in July 2004. +802.11 standard was approved in June 2004 and this amendment was +published in July 2004. Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the IEEE 802.11i work (draft 3.0) to define a subset of the security @@ -251,8 +240,7 @@ enhancements that can be implemented with existing wlan hardware. This is called Wi-Fi Protected Access (WPA). This has now become a mandatory component of interoperability testing and certification done -by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web -site (http://www.wi-fi.org/OpenSection/protected_access.asp). +by Wi-Fi Alliance. IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm for protecting wireless networks. WEP uses RC4 with 40-bit keys, --- contrib/wpa/hostapd/README-MULTI-AP.orig +++ contrib/wpa/hostapd/README-MULTI-AP @@ -0,0 +1,160 @@ +hostapd, wpa_supplicant and the Multi-AP Specification +====================================================== + +This document describes how hostapd and wpa_supplicant can be configured to +support the Multi-AP Specification. + +Introduction to Multi-AP +------------------------ + +The Wi-Fi Alliance Multi-AP Specification is the technical specification for +Wi-Fi CERTIFIED EasyMesh(TM) [1], the Wi-Fi AllianceĀ® certification program for +Multi-AP. It defines control protocols between Wi-FiĀ® access points (APs) to +join them into a network with centralized control and operation. It is targeted +only at routers (repeaters, gateways, ...), not at clients. Clients are not +involved at all in the protocols. + +Most of the Multi-AP specification falls outside of the scope of +hostapd/wpa_supplicant. hostapd/wpa_supplicant is only involved for the items +summarized below. The rest of the protocol must be implemented by a separate +daemon, e.g., prplMesh [2]. That daemon also needs to communicate with hostapd, +e.g., to get a list of associated clients, but this can be done using the normal +hostapd interfaces. + +hostapd/wpa_supplicant needs to be configured specifically to support: +- the WPS onboarding process; +- configuring backhaul links. + +The text below refers to "Multi-AP Specification v1.0" [3]. + + +Fronthaul and backhaul links +---------------------------- + +In a Multi-AP network, the central controller can configure the BSSs on the +devices that are joined into the network. These are called fronthaul BSSs. +From the point of view of hostapd, there is nothing special about these +fronthaul BSSs. + +In addition to fronthaul BSSs, the controller can also configure backhaul +links. A backhaul link is a link between two access point devices, giving +internet access to access point devices that don't have a wired link. The +Multi-AP specification doesn't dictate this, but typically the backhaul link +will be bridged into a LAN together with (one of) the fronthaul BSS(s) and the +wired Ethernet ports. + +A backhaul link must be treated specially by hostapd and wpa_supplicant. One +side of the backhaul link is configured through the Multi-AP protocol as the +"backhaul STA", i.e., the client side of the link. A backhaul STA is like any +station and is handled appropriately by wpa_supplicant, but two additional +features are required. It must send an additional information element in each +(Re)Association Request frame ([3], section 5.2, paragraph 4). In addition, it +must use 4-address mode for all frames sent over this link ([3], section 14). +Therefore, wpa_supplicant must be configured explicitly as the backhaul STA +role, by setting 'multi_ap_backhaul_sta=1' in the network configuration block +or when configuring the network profile through the control interface. When +'multi_ap_backhaul_sta=1', wpa_supplicant includes the Multi-AP IE in +(Re)Association Request frame and verifies that it is included in the +(Re)Association Response frame. If it is not, association fails. If it is, +wpa_supplicant sets 4-address mode for this interface through a driver +callback. + +The AP side of the backhaul link is called a "backhaul BSS". Such a BSS must +be handled specially by hostapd, because it must add an additional information +element in each (Re)Association Response frame, but only to stations that have +identified themselves as backhaul stations ([3], section 5.2, paragraph 5-6). +This is important because it is possible to use the same BSS and SSID for +fronthaul and backhaul at the same time. The additional information element must +only be used for frames sent to a backhaul STA, not to a normal STA. Also, +frames sent to a backhaul STA must use 4-address mode, while frames sent to a +normal STA (fronthaul, when it's a fronthaul and backhaul BSS) must use +3-address mode. + +A BSS is configured in Multi-AP mode in hostapd by setting the 'multi_ap' +configuration option to 1 (backhaul BSS), 2 (fronthaul BSS), or 3 +(simultaneous backhaul and fronthaul BSS). If this option is set, hostapd +parses the Multi-AP information element in the Association Request frame. If the +station is a backhaul STA and the BSS is configured as a backhaul BSS, +hostapd sets up 4-address mode. Since there may be multiple stations connected +simultaneously, and each of them has a different RA (receiver address), a VLAN +is created for each backhaul STA and it is automatically added to a bridge. +This is the same behavior as for WDS, and the relevant option ('bridge' or +'wds_bridge') applies here as well. + +If 'multi_ap' is 1 (backhaul BSS only), any station that tries to associate +without the Multi-AP information element will be denied. + +If 'multi_ap' is 2 (fronthaul BSS only), any station that tries to associate +with the Multi-AP information element will be denied. That is also the only +difference with 'multi_ap' set to 0: in the latter case, the Multi-AP +information element is simply ignored. + +In summary, this is the end-to-end behavior for a backhaul BSS (i.e., +multi_ap_backhaul_sta=1 in wpa_supplicant on STA, and multi_ap=1 or 3 in +hostapd on AP). Note that point 1 means that hostapd must not be configured +with WPS support on the backhaul BSS (multi_ap=1). hostapd does not check for +that. + +1. Backhaul BSS beacons do not advertise WPS support (other than that, nothing + Multi-AP specific). +2. STA sends Authentication frame (nothing Multi-AP specific). +3. AP sends Authentication frame (nothing Multi-AP specific). +4. STA sends Association Request frame with Multi-AP IE. +5. AP sends Association Response frame with Multi-AP IE. +6. STA and AP both use 4-address mode for Data frames. + + +WPS support +----------- + +WPS requires more special handling. WPS must only be advertised on fronthaul +BSSs, not on backhaul BSSs, so WPS should not be enabled on a backhaul-only +BSS in hostapd.conf. The WPS configuration purely works on the fronthaul BSS. +When a WPS M1 message has an additional subelement that indicates a request for +a Multi-AP backhaul link, hostapd must not respond with the normal fronthaul +BSS credentials; instead, it should respond with the (potentially different) +backhaul BSS credentials. + +To support this, hostapd has the 'multi_ap_backhaul_ssid', +'multi_ap_backhaul_wpa_psk' and 'multi_ap_backhaul_wpa_passphrase' options. +When these are set on an BSS with WPS, they are used instead of the normal +credentials when hostapd receives a WPS M1 message with the Multi-AP IE. Only +WPA2-Personal is supported in the Multi-AP specification, so there is no need +to specify authentication or encryption options. For the backhaul credentials, +per-device PSK is not supported. + +If the BSS is a simultaneous backhaul and fronthaul BSS, there is no need to +specify the backhaul credentials, since the backhaul and fronthaul credentials +are identical. + +To enable the Multi-AP backhaul STA feature when it performs WPS, a new +parameter has been introduced to the WPS_PBC control interface call. When this +"multi_ap=1" option is set, it adds the Multi-AP backhaul subelement to the +Association Request frame and the M1 message. It then configures the new network +profile with 'multi_ap_backhaul_sta=1'. Note that this means that if the AP does +not follow the Multi-AP specification, wpa_supplicant will fail to associate. + +In summary, this is the end-to-end behavior for WPS of a backhaul link (i.e., +multi_ap=1 option is given in the wps_pbc call on the STA side, and multi_ap=2 +and multi_ap_backhaul_ssid and either multi_ap_backhaul_wpa_psk or +multi_ap_backhaul_wpa_passphrase are set to the credentials of a backhaul BSS +in hostapd on Registrar AP). + +1. Fronthaul BSS Beacon frames advertise WPS support (nothing Multi-AP + specific). +2. Enrollee sends Authentication frame (nothing Multi-AP specific). +3. AP sends Authentication frame (nothing Multi-AP specific). +4. Enrollee sends Association Request frame with Multi-AP IE. +5. AP sends Association Response frame with Multi-AP IE. +6. Enrollee sends M1 with additional Multi-AP subelement. +7. AP sends M8 with backhaul instead of fronthaul credentials. +8. Enrollee sends Deauthentication frame. + + +References +---------- + +[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh +[2] https://github.com/prplfoundation/prplMesh +[3] https://www.wi-fi.org/file/multi-ap-specification-v10 + (requires registration) --- contrib/wpa/hostapd/config_file.c.orig +++ contrib/wpa/hostapd/config_file.c @@ -1,6 +1,6 @@ /* * hostapd / Configuration file parser - * Copyright (c) 2003-2015, Jouni Malinen + * Copyright (c) 2003-2018, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,8 @@ #include "utils/common.h" #include "utils/uuid.h" #include "common/ieee802_11_defs.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" #include "drivers/driver.h" #include "eap_server/eap.h" #include "radius/radius_client.h" @@ -35,7 +37,7 @@ const char *fname) { FILE *f; - char buf[128], *pos, *pos2; + char buf[128], *pos, *pos2, *pos3; int line = 0, vlan_id; struct hostapd_vlan *vlan; @@ -80,7 +82,10 @@ pos2 = pos; while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0') pos2++; - *pos2 = '\0'; + + if (*pos2 != '\0') + *(pos2++) = '\0'; + if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) { wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d " "in '%s'", line, fname); @@ -88,6 +93,13 @@ return -1; } + while (*pos2 == ' ' || *pos2 == '\t') + pos2++; + pos3 = pos2; + while (*pos3 != ' ' && *pos3 != '\t' && *pos3 != '\0') + pos3++; + *pos3 = '\0'; + vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while reading " @@ -97,7 +109,10 @@ } vlan->vlan_id = vlan_id; + vlan->vlan_desc.untagged = vlan_id; + vlan->vlan_desc.notempty = !!vlan_id; os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); + os_strlcpy(vlan->bridge, pos2, sizeof(vlan->bridge)); vlan->next = bss->vlan; bss->vlan = vlan; } @@ -109,7 +124,7 @@ #endif /* CONFIG_NO_VLAN */ -static int hostapd_acl_comp(const void *a, const void *b) +int hostapd_acl_comp(const void *a, const void *b) { const struct mac_acl_entry *aa = a; const struct mac_acl_entry *bb = b; @@ -117,6 +132,44 @@ } +int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, + int vlan_id, const u8 *addr) +{ + struct mac_acl_entry *newacl; + + newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); + if (!newacl) { + wpa_printf(MSG_ERROR, "MAC list reallocation failed"); + return -1; + } + + *acl = newacl; + os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); + os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id)); + (*acl)[*num].vlan_id.untagged = vlan_id; + (*acl)[*num].vlan_id.notempty = !!vlan_id; + (*num)++; + + return 0; +} + + +void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num, + const u8 *addr) +{ + int i = 0; + + while (i < *num) { + if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) { + os_remove_in_array(*acl, *num, sizeof(**acl), i); + (*num)--; + } else { + i++; + } + } +} + + static int hostapd_config_read_maclist(const char *fname, struct mac_acl_entry **acl, int *num) { @@ -124,12 +177,8 @@ char buf[128], *pos; int line = 0; u8 addr[ETH_ALEN]; - struct mac_acl_entry *newacl; int vlan_id; - if (!fname) - return 0; - f = fopen(fname, "r"); if (!f) { wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname); @@ -137,7 +186,7 @@ } while (fgets(buf, sizeof(buf), f)) { - int i, rem = 0; + int rem = 0; line++; @@ -167,16 +216,7 @@ } if (rem) { - i = 0; - while (i < *num) { - if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == - 0) { - os_remove_in_array(*acl, *num, - sizeof(**acl), i); - (*num)--; - } else - i++; - } + hostapd_remove_acl_mac(acl, num, addr); continue; } vlan_id = 0; @@ -188,22 +228,16 @@ if (*pos != '\0') vlan_id = atoi(pos); - newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); - if (newacl == NULL) { - wpa_printf(MSG_ERROR, "MAC list reallocation failed"); + if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) { fclose(f); return -1; } - - *acl = newacl; - os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); - (*acl)[*num].vlan_id = vlan_id; - (*num)++; } fclose(f); - qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + if (*acl) + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); return 0; } @@ -210,6 +244,62 @@ #ifdef EAP_SERVER + +static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user, + const char *hash, size_t len, + char **pos, int line, + const char *fname) +{ + char *pos2 = *pos; + + while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#') + pos2++; + + if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */ + wpa_printf(MSG_ERROR, + "Invalid salted %s hash on line %d in '%s'", + hash, line, fname); + return -1; + } + + user->password = os_malloc(len); + if (!user->password) { + wpa_printf(MSG_ERROR, + "Failed to allocate memory for salted %s hash", + hash); + return -1; + } + + if (hexstr2bin(*pos, user->password, len) < 0) { + wpa_printf(MSG_ERROR, + "Invalid salted password on line %d in '%s'", + line, fname); + return -1; + } + user->password_len = len; + *pos += 2 * len; + + user->salt_len = (pos2 - *pos) / 2; + user->salt = os_malloc(user->salt_len); + if (!user->salt) { + wpa_printf(MSG_ERROR, + "Failed to allocate memory for salted %s hash", + hash); + return -1; + } + + if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) { + wpa_printf(MSG_ERROR, + "Invalid salt for password on line %d in '%s'", + line, fname); + return -1; + } + + *pos = pos2; + return 0; +} + + static int hostapd_config_read_eap_user(const char *fname, struct hostapd_bss_config *conf) { @@ -218,9 +308,6 @@ int line = 0, ret = 0, num_methods; struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL; - if (!fname) - return 0; - if (os_strncmp(fname, "sqlite:", 7) == 0) { #ifdef CONFIG_SQLITE os_free(conf->eap_user_sqlite); @@ -307,13 +394,12 @@ goto failed; } - user->identity = os_malloc(pos - start); + user->identity = os_memdup(start, pos - start); if (user->identity == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate " "memory for EAP identity"); goto failed; } - os_memcpy(user->identity, start, pos - start); user->identity_len = pos - start; if (pos[0] == '"' && pos[1] == '*') { @@ -431,13 +517,12 @@ goto failed; } - user->password = os_malloc(pos - start); + user->password = os_memdup(start, pos - start); if (user->password == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate " "memory for EAP password"); goto failed; } - os_memcpy(user->password, start, pos - start); user->password_len = pos - start; pos++; @@ -466,6 +551,24 @@ user->password_len = 16; user->password_hash = 1; pos = pos2; + } else if (os_strncmp(pos, "ssha1:", 6) == 0) { + pos += 6; + if (hostapd_config_eap_user_salted(user, "sha1", 20, + &pos, + line, fname) < 0) + goto failed; + } else if (os_strncmp(pos, "ssha256:", 8) == 0) { + pos += 8; + if (hostapd_config_eap_user_salted(user, "sha256", 32, + &pos, + line, fname) < 0) + goto failed; + } else if (os_strncmp(pos, "ssha512:", 8) == 0) { + pos += 8; + if (hostapd_config_eap_user_salted(user, "sha512", 64, + &pos, + line, fname) < 0) + goto failed; } else { pos2 = pos; while (*pos2 != '\0' && *pos2 != ' ' && @@ -517,19 +620,15 @@ fclose(f); if (ret == 0) { - user = conf->eap_user; - while (user) { - struct hostapd_eap_user *prev; - - prev = user; - user = user->next; - hostapd_config_free_eap_user(prev); - } + hostapd_config_free_eap_users(conf->eap_user); conf->eap_user = new_user; + } else { + hostapd_config_free_eap_users(new_user); } return ret; } + #endif /* EAP_SERVER */ @@ -631,8 +730,7 @@ } -static int hostapd_parse_das_client(struct hostapd_bss_config *bss, - const char *val) +static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val) { char *secret; @@ -640,7 +738,7 @@ if (secret == NULL) return -1; - secret++; + *secret++ = '\0'; if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr)) return -1; @@ -680,12 +778,16 @@ val |= WPA_KEY_MGMT_PSK; else if (os_strcmp(start, "WPA-EAP") == 0) val |= WPA_KEY_MGMT_IEEE8021X; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP else if (os_strcmp(start, "FT-PSK") == 0) val |= WPA_KEY_MGMT_FT_PSK; else if (os_strcmp(start, "FT-EAP") == 0) val |= WPA_KEY_MGMT_FT_IEEE8021X; -#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SHA384 + else if (os_strcmp(start, "FT-EAP-SHA384") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) val |= WPA_KEY_MGMT_PSK_SHA256; @@ -706,6 +808,30 @@ else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0) val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_FILS + else if (os_strcmp(start, "FILS-SHA256") == 0) + val |= WPA_KEY_MGMT_FILS_SHA256; + else if (os_strcmp(start, "FILS-SHA384") == 0) + val |= WPA_KEY_MGMT_FILS_SHA384; +#ifdef CONFIG_IEEE80211R_AP + else if (os_strcmp(start, "FT-FILS-SHA256") == 0) + val |= WPA_KEY_MGMT_FT_FILS_SHA256; + else if (os_strcmp(start, "FT-FILS-SHA384") == 0) + val |= WPA_KEY_MGMT_FT_FILS_SHA384; +#endif /* CONFIG_IEEE80211R_AP */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + else if (os_strcmp(start, "OWE") == 0) + val |= WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (os_strcmp(start, "DPP") == 0) + val |= WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + else if (os_strcmp(start, "OSEN") == 0) + val |= WPA_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -751,17 +877,34 @@ { size_t len = os_strlen(val); - if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL) + if (keyidx < 0 || keyidx > 3) return -1; + if (len == 0) { + int i, set = 0; + + bin_clear_free(wep->key[keyidx], wep->len[keyidx]); + wep->key[keyidx] = NULL; + wep->len[keyidx] = 0; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (wep->key[i]) + set++; + } + if (!set) + wep->keys_set = 0; + return 0; + } + + if (wep->key[keyidx] != NULL) + return -1; + if (val[0] == '"') { if (len < 2 || val[len - 1] != '"') return -1; len -= 2; - wep->key[keyidx] = os_malloc(len); + wep->key[keyidx] = os_memdup(val + 1, len); if (wep->key[keyidx] == NULL) return -1; - os_memcpy(wep->key[keyidx], val + 1, len); wep->len[keyidx] = len; } else { if (len & 1) @@ -974,7 +1117,27 @@ } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + +static int rkh_derive_key(const char *pos, u8 *key, size_t key_len) +{ + u8 oldkey[16]; + int ret; + + if (!hexstr2bin(pos, key, key_len)) + return 0; + + /* Try to use old short key for backwards compatibility */ + if (hexstr2bin(pos, oldkey, sizeof(oldkey))) + return -1; + + ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0, + key, key_len); + os_memset(oldkey, 0, sizeof(oldkey)); + return ret; +} + + static int add_r0kh(struct hostapd_bss_config *bss, char *value) { struct ft_remote_r0kh *r0kh; @@ -1008,7 +1171,7 @@ os_memcpy(r0kh->id, pos, r0kh->id_len); pos = next; - if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) { + if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) { wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos); os_free(r0kh); return -1; @@ -1053,7 +1216,7 @@ } pos = next; - if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) { + if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) { wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos); os_free(r1kh); return -1; @@ -1064,7 +1227,7 @@ return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211N @@ -1081,6 +1244,12 @@ conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; conf->secondary_channel = 1; } + if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->ht40_plus_minus_allowed = 1; + } + if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]")) + conf->secondary_channel = 0; if (os_strstr(capab, "[SMPS-STATIC]")) { conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC; @@ -1210,6 +1379,30 @@ #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + +static u8 find_bit_offset(u8 val) +{ + u8 res = 0; + + for (; val; val >>= 1) { + if (val & 1) + break; + res++; + } + + return res; +} + + +static u8 set_he_cap(int val, u8 mask) +{ + return (u8) (mask & (val << find_bit_offset(mask))); +} + +#endif /* CONFIG_IEEE80211AX */ + + #ifdef CONFIG_INTERWORKING static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, int line) @@ -1303,6 +1496,44 @@ } +static int parse_venue_url(struct hostapd_bss_config *bss, char *pos, + int line) +{ + char *sep; + size_t nlen; + struct hostapd_venue_url *url; + int ret = -1; + + sep = os_strchr(pos, ':'); + if (!sep) + goto fail; + *sep++ = '\0'; + + nlen = os_strlen(sep); + if (nlen > 254) + goto fail; + + url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1, + sizeof(struct hostapd_venue_url)); + if (!url) + goto fail; + + bss->venue_url = url; + url = &bss->venue_url[bss->venue_url_count++]; + + url->venue_number = atoi(pos); + url->url_len = nlen; + os_memcpy(url->url, sep, nlen); + + ret = 0; +fail: + if (ret) + wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'", + line, pos); + return ret; +} + + static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf, int line) { @@ -1519,6 +1750,54 @@ } +static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line) +{ + char *delim; + u16 infoid; + size_t len; + struct wpabuf *payload; + struct anqp_element *elem; + + delim = os_strchr(buf, ':'); + if (!delim) + return -1; + delim++; + infoid = atoi(buf); + len = os_strlen(delim); + if (len & 1) + return -1; + len /= 2; + payload = wpabuf_alloc(len); + if (!payload) + return -1; + if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) { + wpabuf_free(payload); + return -1; + } + + dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) { + if (elem->infoid == infoid) { + /* Update existing entry */ + wpabuf_free(elem->payload); + elem->payload = payload; + return 0; + } + } + + /* Add a new entry */ + elem = os_zalloc(sizeof(*elem)); + if (!elem) { + wpabuf_free(payload); + return -1; + } + elem->infoid = infoid; + elem->payload = payload; + dl_list_add(&bss->anqp_elem, &elem->list); + + return 0; +} + + static int parse_qos_map_set(struct hostapd_bss_config *bss, char *buf, int line) { @@ -1805,6 +2084,24 @@ } +static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (bss->last_osu == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); + return -1; + } + + os_free(bss->last_osu->osu_nai2); + bss->last_osu->osu_nai2 = os_strdup(pos); + if (bss->last_osu->osu_nai2 == NULL) + return -1; + bss->hs20_osu_providers_nai_count++; + + return 0; +} + + static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos, int line) { @@ -1864,34 +2161,28 @@ return 0; } -#endif /* CONFIG_HS20 */ - -#ifdef CONFIG_WPS_NFC -static struct wpabuf * hostapd_parse_bin(const char *buf) +static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos, + int line) { - size_t len; - struct wpabuf *ret; + char **n; - len = os_strlen(buf); - if (len & 0x01) - return NULL; - len /= 2; + n = os_realloc_array(bss->hs20_operator_icon, + bss->hs20_operator_icon_count + 1, sizeof(char *)); + if (!n) + return -1; + bss->hs20_operator_icon = n; + bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos); + if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count]) + return -1; + bss->hs20_operator_icon_count++; - ret = wpabuf_alloc(len); - if (ret == NULL) - return NULL; + return 0; +} - if (hexstr2bin(buf, wpabuf_put(ret, len), len)) { - wpabuf_free(ret); - return NULL; - } +#endif /* CONFIG_HS20 */ - return ret; -} -#endif /* CONFIG_WPS_NFC */ - #ifdef CONFIG_ACS static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf, char *pos) @@ -1934,6 +2225,157 @@ #endif /* CONFIG_ACS */ +static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf, + const char *val) +{ + struct wpabuf *elems; + + if (val[0] == '\0') { + wpabuf_free(*buf); + *buf = NULL; + return 0; + } + + elems = wpabuf_parse_bin(val); + if (!elems) { + wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'", + line, name, val); + return -1; + } + + wpabuf_free(*buf); + *buf = elems; + + return 0; +} + + +#ifdef CONFIG_FILS +static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val) +{ + struct fils_realm *realm; + size_t len; + + len = os_strlen(val); + realm = os_zalloc(sizeof(*realm) + len + 1); + if (!realm) + return -1; + + os_memcpy(realm->realm, val, len); + if (fils_domain_name_hash(val, realm->hash) < 0) { + os_free(realm); + return -1; + } + dl_list_add_tail(&bss->fils_realms, &realm->list); + + return 0; +} +#endif /* CONFIG_FILS */ + + +#ifdef EAP_SERVER +static unsigned int parse_tls_flags(const char *val) +{ + unsigned int flags = 0; + + /* Disable TLS v1.3 by default for now to avoid interoperability issue. + * This can be enabled by default once the implementation has been fully + * completed and tested with other implementations. */ + flags |= TLS_CONN_DISABLE_TLSv1_3; + + if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]")) + flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; + if (os_strstr(val, "[DISABLE-TIME-CHECKS]")) + flags |= TLS_CONN_DISABLE_TIME_CHECKS; + if (os_strstr(val, "[DISABLE-TLSv1.0]")) + flags |= TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(val, "[ENABLE-TLSv1.0]")) + flags |= TLS_CONN_ENABLE_TLSv1_0; + if (os_strstr(val, "[DISABLE-TLSv1.1]")) + flags |= TLS_CONN_DISABLE_TLSv1_1; + if (os_strstr(val, "[ENABLE-TLSv1.1]")) + flags |= TLS_CONN_ENABLE_TLSv1_1; + if (os_strstr(val, "[DISABLE-TLSv1.2]")) + flags |= TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(val, "[ENABLE-TLSv1.2]")) + flags |= TLS_CONN_ENABLE_TLSv1_2; + if (os_strstr(val, "[DISABLE-TLSv1.3]")) + flags |= TLS_CONN_DISABLE_TLSv1_3; + if (os_strstr(val, "[ENABLE-TLSv1.3]")) + flags &= ~TLS_CONN_DISABLE_TLSv1_3; + if (os_strstr(val, "[SUITEB]")) + flags |= TLS_CONN_SUITEB; + if (os_strstr(val, "[SUITEB-NO-ECDH]")) + flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB; + + return flags; +} +#endif /* EAP_SERVER */ + + +#ifdef CONFIG_SAE +static int parse_sae_password(struct hostapd_bss_config *bss, const char *val) +{ + struct sae_password_entry *pw; + const char *pos = val, *pos2, *end = NULL; + + pw = os_zalloc(sizeof(*pw)); + if (!pw) + return -1; + os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */ + + pos2 = os_strstr(pos, "|mac="); + if (pos2) { + end = pos2; + pos2 += 5; + if (hwaddr_aton(pos2, pw->peer_addr) < 0) + goto fail; + pos = pos2 + ETH_ALEN * 3 - 1; + } + + pos2 = os_strstr(pos, "|vlanid="); + if (pos2) { + if (!end) + end = pos2; + pos2 += 8; + pw->vlan_id = atoi(pos2); + } + + pos2 = os_strstr(pos, "|id="); + if (pos2) { + if (!end) + end = pos2; + pos2 += 4; + pw->identifier = os_strdup(pos2); + if (!pw->identifier) + goto fail; + } + + if (!end) { + pw->password = os_strdup(val); + if (!pw->password) + goto fail; + } else { + pw->password = os_malloc(end - val + 1); + if (!pw->password) + goto fail; + os_memcpy(pw->password, val, end - val); + pw->password[end - val] = '\0'; + } + + pw->next = bss->sae_passwords; + bss->sae_passwords = pw; + + return 0; +fail: + str_clear_free(pw->password); + os_free(pw->identifier); + os_free(pw); + return -1; +} +#endif /* CONFIG_SAE */ + + static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, const char *buf, char *pos, int line) @@ -1949,20 +2391,21 @@ os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge)); } else if (os_strcmp(buf, "driver") == 0) { int j; - /* clear to get error below if setting is invalid */ - conf->driver = NULL; + const struct wpa_driver_ops *driver = NULL; + for (j = 0; wpa_drivers[j]; j++) { if (os_strcmp(pos, wpa_drivers[j]->name) == 0) { - conf->driver = wpa_drivers[j]; + driver = wpa_drivers[j]; break; } } - if (conf->driver == NULL) { + if (!driver) { wpa_printf(MSG_ERROR, "Line %d: invalid/unknown driver '%s'", line, pos); return 1; } + conf->driver = driver; } else if (os_strcmp(buf, "driver_params") == 0) { os_free(conf->driver_params); conf->driver_params = os_strdup(pos); @@ -2006,13 +2449,16 @@ } else if (os_strcmp(buf, "utf8_ssid") == 0) { bss->ssid.utf8_ssid = atoi(pos) > 0; } else if (os_strcmp(buf, "macaddr_acl") == 0) { - bss->macaddr_acl = atoi(pos); - if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && - bss->macaddr_acl != DENY_UNLESS_ACCEPTED && - bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + enum macaddr_acl acl = atoi(pos); + + if (acl != ACCEPT_UNLESS_DENIED && + acl != DENY_UNLESS_ACCEPTED && + acl != USE_EXTERNAL_RADIUS_AUTH) { wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d", - line, bss->macaddr_acl); + line, acl); + return 1; } + bss->macaddr_acl = acl; } else if (os_strcmp(buf, "accept_mac_file") == 0) { if (hostapd_config_read_maclist(pos, &bss->accept_mac, &bss->num_accept_mac)) { @@ -2039,8 +2485,8 @@ bss->skip_inactivity_poll = atoi(pos); } else if (os_strcmp(buf, "country_code") == 0) { os_memcpy(conf->country, pos, 2); - /* FIX: make this configurable */ - conf->country[2] = ' '; + } else if (os_strcmp(buf, "country3") == 0) { + conf->country[2] = strtol(pos, NULL, 16); } else if (os_strcmp(buf, "ieee80211d") == 0) { conf->ieee80211d = atoi(pos); } else if (os_strcmp(buf, "ieee80211h") == 0) { @@ -2048,13 +2494,15 @@ } else if (os_strcmp(buf, "ieee8021x") == 0) { bss->ieee802_1x = atoi(pos); } else if (os_strcmp(buf, "eapol_version") == 0) { - bss->eapol_version = atoi(pos); - if (bss->eapol_version < 1 || bss->eapol_version > 2) { + int eapol_version = atoi(pos); + + if (eapol_version < 1 || eapol_version > 2) { wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL version (%d): '%s'.", - line, bss->eapol_version, pos); + line, eapol_version, pos); return 1; } + bss->eapol_version = eapol_version; wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version); #ifdef EAP_SERVER } else if (os_strcmp(buf, "eap_authenticator") == 0) { @@ -2077,13 +2525,32 @@ } else if (os_strcmp(buf, "private_key_passwd") == 0) { os_free(bss->private_key_passwd); bss->private_key_passwd = os_strdup(pos); + } else if (os_strcmp(buf, "check_cert_subject") == 0) { + if (!pos[0]) { + wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'", + line, pos); + return 1; + } + os_free(bss->check_cert_subject); + bss->check_cert_subject = os_strdup(pos); + if (!bss->check_cert_subject) + return 1; } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "check_crl_strict") == 0) { + bss->check_crl_strict = atoi(pos); + } else if (os_strcmp(buf, "crl_reload_interval") == 0) { + bss->crl_reload_interval = atoi(pos); } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { bss->tls_session_lifetime = atoi(pos); + } else if (os_strcmp(buf, "tls_flags") == 0) { + bss->tls_flags = parse_tls_flags(pos); } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { os_free(bss->ocsp_stapling_response); bss->ocsp_stapling_response = os_strdup(pos); + } else if (os_strcmp(buf, "ocsp_stapling_response_multi") == 0) { + os_free(bss->ocsp_stapling_response_multi); + bss->ocsp_stapling_response_multi = os_strdup(pos); } else if (os_strcmp(buf, "dh_file") == 0) { os_free(bss->dh_file); bss->dh_file = os_strdup(pos); @@ -2090,6 +2557,9 @@ } else if (os_strcmp(buf, "openssl_ciphers") == 0) { os_free(bss->openssl_ciphers); bss->openssl_ciphers = os_strdup(pos); + } else if (os_strcmp(buf, "openssl_ecdh_curves") == 0) { + os_free(bss->openssl_ecdh_curves); + bss->openssl_ecdh_curves = os_strdup(pos); } else if (os_strcmp(buf, "fragment_size") == 0) { bss->fragment_size = atoi(pos); #ifdef EAP_SERVER_FAST @@ -2139,6 +2609,8 @@ } else if (os_strcmp(buf, "eap_sim_db") == 0) { os_free(bss->eap_sim_db); bss->eap_sim_db = os_strdup(pos); + } else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) { + bss->eap_sim_db_timeout = atoi(pos); } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { bss->eap_sim_aka_result_ind = atoi(pos); #endif /* EAP_SERVER_SIM */ @@ -2150,8 +2622,10 @@ } else if (os_strcmp(buf, "pwd_group") == 0) { bss->pwd_group = atoi(pos); #endif /* EAP_SERVER_PWD */ +#ifdef CONFIG_ERP } else if (os_strcmp(buf, "eap_server_erp") == 0) { bss->eap_server_erp = atoi(pos); +#endif /* CONFIG_ERP */ #endif /* EAP_SERVER */ } else if (os_strcmp(buf, "eap_message") == 0) { char *term; @@ -2177,24 +2651,25 @@ os_free(bss->erp_domain); bss->erp_domain = os_strdup(pos); } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) { - bss->default_wep_key_len = atoi(pos); - if (bss->default_wep_key_len > 13) { - wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)", - line, - (unsigned long) bss->default_wep_key_len, - (unsigned long) - bss->default_wep_key_len * 8); + int val = atoi(pos); + + if (val < 0 || val > 13) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WEP key len %d (= %d bits)", + line, val, val * 8); return 1; } + bss->default_wep_key_len = val; } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) { - bss->individual_wep_key_len = atoi(pos); - if (bss->individual_wep_key_len < 0 || - bss->individual_wep_key_len > 13) { - wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)", - line, bss->individual_wep_key_len, - bss->individual_wep_key_len * 8); + int val = atoi(pos); + + if (val < 0 || val > 13) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WEP key len %d (= %d bits)", + line, val, val * 8); return 1; } + bss->individual_wep_key_len = val; } else if (os_strcmp(buf, "wep_rekey_period") == 0) { bss->wep_rekeying_period = atoi(pos); if (bss->wep_rekeying_period < 0) { @@ -2353,6 +2828,9 @@ bss->radius_das_time_window = atoi(pos); } else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) { bss->radius_das_require_event_timestamp = atoi(pos); + } else if (os_strcmp(buf, "radius_das_require_message_authenticator") == + 0) { + bss->radius_das_require_message_authenticator = atoi(pos); #endif /* CONFIG_NO_RADIUS */ } else if (os_strcmp(buf, "auth_algs") == 0) { bss->auth_algs = atoi(pos); @@ -2373,6 +2851,7 @@ bss->wpa = atoi(pos); } else if (os_strcmp(buf, "wpa_group_rekey") == 0) { bss->wpa_group_rekey = atoi(pos); + bss->wpa_group_rekey_set = 1; } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) { bss->wpa_strict_rekey = atoi(pos); } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) { @@ -2379,6 +2858,30 @@ bss->wpa_gmk_rekey = atoi(pos); } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) { bss->wpa_ptk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_group_update_count") == 0) { + char *endp; + unsigned long val = strtoul(pos, &endp, 0); + + if (*endp || val < 1 || val > (u32) -1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295", + line, val); + return 1; + } + bss->wpa_group_update_count = (u32) val; + } else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) { + char *endp; + unsigned long val = strtoul(pos, &endp, 0); + + if (*endp || val < 1 || val > (u32) -1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295", + line, val); + return 1; + } + bss->wpa_pairwise_update_count = (u32) val; + } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) { + bss->wpa_disable_eapol_key_retries = atoi(pos); } else if (os_strcmp(buf, "wpa_passphrase") == 0) { int len = os_strlen(pos); if (len < 8 || len > 63) { @@ -2437,7 +2940,7 @@ if (bss->wpa_pairwise & (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'", - bss->wpa_pairwise, pos); + line, pos); return 1; } } else if (os_strcmp(buf, "rsn_pairwise") == 0) { @@ -2447,9 +2950,23 @@ if (bss->rsn_pairwise & (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'", - bss->rsn_pairwise, pos); + line, pos); return 1; } + } else if (os_strcmp(buf, "group_cipher") == 0) { + bss->group_cipher = hostapd_config_parse_cipher(line, pos); + if (bss->group_cipher == -1 || bss->group_cipher == 0) + return 1; + if (bss->group_cipher != WPA_CIPHER_TKIP && + bss->group_cipher != WPA_CIPHER_CCMP && + bss->group_cipher != WPA_CIPHER_GCMP && + bss->group_cipher != WPA_CIPHER_GCMP_256 && + bss->group_cipher != WPA_CIPHER_CCMP_256) { + wpa_printf(MSG_ERROR, + "Line %d: unsupported group cipher suite '%s'", + line, pos); + return 1; + } #ifdef CONFIG_RSN_PREAUTH } else if (os_strcmp(buf, "rsn_preauth") == 0) { bss->rsn_preauth = atoi(pos); @@ -2457,11 +2974,10 @@ os_free(bss->rsn_preauth_interfaces); bss->rsn_preauth_interfaces = os_strdup(pos); #endif /* CONFIG_RSN_PREAUTH */ -#ifdef CONFIG_PEERKEY } else if (os_strcmp(buf, "peerkey") == 0) { - bss->peerkey = atoi(pos); -#endif /* CONFIG_PEERKEY */ -#ifdef CONFIG_IEEE80211R + wpa_printf(MSG_INFO, + "Line %d: Obsolete peerkey parameter ignored", line); +#ifdef CONFIG_IEEE80211R_AP } else if (os_strcmp(buf, "mobility_domain") == 0) { if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN || hexstr2bin(pos, bss->mobility_domain, @@ -2480,9 +2996,22 @@ return 1; } } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { + /* DEPRECATED: Use ft_r0_key_lifetime instead. */ + bss->r0_key_lifetime = atoi(pos) * 60; + } else if (os_strcmp(buf, "ft_r0_key_lifetime") == 0) { bss->r0_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "r1_max_key_lifetime") == 0) { + bss->r1_max_key_lifetime = atoi(pos); } else if (os_strcmp(buf, "reassociation_deadline") == 0) { bss->reassociation_deadline = atoi(pos); + } else if (os_strcmp(buf, "rkh_pos_timeout") == 0) { + bss->rkh_pos_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_neg_timeout") == 0) { + bss->rkh_neg_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_pull_timeout") == 0) { + bss->rkh_pull_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_pull_retries") == 0) { + bss->rkh_pull_retries = atoi(pos); } else if (os_strcmp(buf, "r0kh") == 0) { if (add_r0kh(bss, pos) < 0) { wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'", @@ -2499,7 +3028,9 @@ bss->pmk_r1_push = atoi(pos); } else if (os_strcmp(buf, "ft_over_ds") == 0) { bss->ft_over_ds = atoi(pos); -#endif /* CONFIG_IEEE80211R */ + } else if (os_strcmp(buf, "ft_psk_generate_local") == 0) { + bss->ft_psk_generate_local = atoi(pos); +#endif /* CONFIG_IEEE80211R_AP */ #ifndef CONFIG_NO_CTRL_IFACE } else if (os_strcmp(buf, "ctrl_interface") == 0) { os_free(bss->ctrl_interface); @@ -2577,6 +3108,8 @@ line, pos); return 1; } + } else if (os_strcmp(buf, "acs_exclude_dfs") == 0) { + conf->acs_exclude_dfs = atoi(pos); } else if (os_strcmp(buf, "channel") == 0) { if (os_strcmp(pos, "acs_survey") == 0) { #ifndef CONFIG_ACS @@ -2603,9 +3136,10 @@ * cause problems with the current implementation. * Since it is unlikely that this small numbers are * useful in real life scenarios, do not allow beacon - * period to be set below 15 TU. */ - if (val < 15 || val > 65535) { - wpa_printf(MSG_ERROR, "Line %d: invalid beacon_int %d (expected 15..65535)", + * period to be set below 10 TU. */ + if (val < 10 || val > 65535) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_int %d (expected 10..65535)", line, val); return 1; } @@ -2627,24 +3161,37 @@ } #endif /* CONFIG_ACS */ } else if (os_strcmp(buf, "dtim_period") == 0) { - bss->dtim_period = atoi(pos); - if (bss->dtim_period < 1 || bss->dtim_period > 255) { + int val = atoi(pos); + + if (val < 1 || val > 255) { wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d", - line, bss->dtim_period); + line, val); return 1; } + bss->dtim_period = val; } else if (os_strcmp(buf, "bss_load_update_period") == 0) { - bss->bss_load_update_period = atoi(pos); - if (bss->bss_load_update_period < 0 || - bss->bss_load_update_period > 100) { + int val = atoi(pos); + + if (val < 0 || val > 100) { wpa_printf(MSG_ERROR, "Line %d: invalid bss_load_update_period %d", - line, bss->bss_load_update_period); + line, val); return 1; } + bss->bss_load_update_period = val; + } else if (os_strcmp(buf, "chan_util_avg_period") == 0) { + int val = atoi(pos); + + if (val < 0) { + wpa_printf(MSG_ERROR, + "Line %d: invalid chan_util_avg_period", + line); + return 1; + } + bss->chan_util_avg_period = val; } else if (os_strcmp(buf, "rts_threshold") == 0) { conf->rts_threshold = atoi(pos); - if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) { + if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) { wpa_printf(MSG_ERROR, "Line %d: invalid rts_threshold %d", line, conf->rts_threshold); @@ -2652,8 +3199,10 @@ } } else if (os_strcmp(buf, "fragm_threshold") == 0) { conf->fragm_threshold = atoi(pos); - if (conf->fragm_threshold < 256 || - conf->fragm_threshold > 2346) { + if (conf->fragm_threshold == -1) { + /* allow a value of -1 */ + } else if (conf->fragm_threshold < 256 || + conf->fragm_threshold > 2346) { wpa_printf(MSG_ERROR, "Line %d: invalid fragm_threshold %d", line, conf->fragm_threshold); @@ -2666,7 +3215,7 @@ line, val); return 1; } - conf->send_probe_response = val; + bss->send_probe_response = val; } else if (os_strcmp(buf, "supported_rates") == 0) { if (hostapd_parse_intlist(&conf->supported_rates, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid rate list", @@ -2679,6 +3228,40 @@ line); return 1; } + } else if (os_strcmp(buf, "beacon_rate") == 0) { + int val; + + if (os_strncmp(pos, "ht:", 3) == 0) { + val = atoi(pos + 3); + if (val < 0 || val > 31) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_rate HT-MCS %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_HT; + conf->beacon_rate = val; + } else if (os_strncmp(pos, "vht:", 4) == 0) { + val = atoi(pos + 4); + if (val < 0 || val > 9) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_rate VHT-MCS %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_VHT; + conf->beacon_rate = val; + } else { + val = atoi(pos); + if (val < 10 || val > 10000) { + wpa_printf(MSG_ERROR, + "Line %d: invalid legacy beacon_rate %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_LEGACY; + conf->beacon_rate = val; + } } else if (os_strcmp(buf, "preamble") == 0) { if (atoi(pos)) conf->preamble = SHORT_PREAMBLE; @@ -2686,6 +3269,8 @@ conf->preamble = LONG_PREAMBLE; } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) { bss->ignore_broadcast_ssid = atoi(pos); + } else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) { + bss->no_probe_resp_if_max_sta = atoi(pos); } else if (os_strcmp(buf, "wep_default_key") == 0) { bss->ssid.wep.idx = atoi(pos); if (bss->ssid.wep.idx > 3) { @@ -2707,6 +3292,8 @@ #ifndef CONFIG_NO_VLAN } else if (os_strcmp(buf, "dynamic_vlan") == 0) { bss->ssid.dynamic_vlan = atoi(pos); + } else if (os_strcmp(buf, "per_sta_vif") == 0) { + bss->ssid.per_sta_vif = atoi(pos); } else if (os_strcmp(buf, "vlan_file") == 0) { if (hostapd_config_read_vlan_file(bss, pos)) { wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'", @@ -2762,6 +3349,8 @@ line); return 1; } + } else if (os_strcmp(buf, "use_driver_iface_addr") == 0) { + conf->use_driver_iface_addr = atoi(pos); #ifdef CONFIG_IEEE80211W } else if (os_strcmp(buf, "ieee80211w") == 0) { bss->ieee80211w = atoi(pos); @@ -2794,6 +3383,12 @@ return 1; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + } else if (os_strcmp(buf, "ocv") == 0) { + bss->ocv = atoi(pos); + if (bss->ocv && !bss->ieee80211w) + bss->ieee80211w = 1; +#endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211N } else if (os_strcmp(buf, "ieee80211n") == 0) { conf->ieee80211n = atoi(pos); @@ -2827,7 +3422,111 @@ conf->vht_oper_centr_freq_seg1_idx = atoi(pos); } else if (os_strcmp(buf, "vendor_vht") == 0) { bss->vendor_vht = atoi(pos); + } else if (os_strcmp(buf, "use_sta_nsts") == 0) { + bss->use_sta_nsts = atoi(pos); #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + } else if (os_strcmp(buf, "ieee80211ax") == 0) { + conf->ieee80211ax = atoi(pos); + } else if (os_strcmp(buf, "he_su_beamformer") == 0) { + conf->he_phy_capab.he_su_beamformer = atoi(pos); + } else if (os_strcmp(buf, "he_su_beamformee") == 0) { + conf->he_phy_capab.he_su_beamformee = atoi(pos); + } else if (os_strcmp(buf, "he_mu_beamformer") == 0) { + conf->he_phy_capab.he_mu_beamformer = atoi(pos); + } else if (os_strcmp(buf, "he_bss_color") == 0) { + conf->he_op.he_bss_color = atoi(pos); + } else if (os_strcmp(buf, "he_default_pe_duration") == 0) { + conf->he_op.he_default_pe_duration = atoi(pos); + } else if (os_strcmp(buf, "he_twt_required") == 0) { + conf->he_op.he_twt_required = atoi(pos); + } else if (os_strcmp(buf, "he_rts_threshold") == 0) { + conf->he_op.he_rts_threshold = atoi(pos); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_q_ack") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_Q_ACK); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_queue_request") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_QUEUE_REQUEST); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_txop_request") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_TXOP_REQUEST); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_acm") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_aci") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_timer") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_acm") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aci") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_timer") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_acm") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aci") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_timer") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_acm") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aci") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; +#endif /* CONFIG_IEEE80211AX */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) { @@ -2908,7 +3607,10 @@ } } else if (os_strcmp(buf, "ap_pin") == 0) { os_free(bss->ap_pin); - bss->ap_pin = os_strdup(pos); + if (*pos == '\0') + bss->ap_pin = NULL; + else + bss->ap_pin = os_strdup(pos); } else if (os_strcmp(buf, "skip_cred_build") == 0) { bss->skip_cred_build = atoi(pos); } else if (os_strcmp(buf, "extra_cred") == 0) { @@ -2921,6 +3623,8 @@ } } else if (os_strcmp(buf, "wps_cred_processing") == 0) { bss->wps_cred_processing = atoi(pos); + } else if (os_strcmp(buf, "wps_cred_add_sae") == 0) { + bss->wps_cred_add_sae = atoi(pos); } else if (os_strcmp(buf, "ap_settings") == 0) { os_free(bss->ap_settings); bss->ap_settings = @@ -2930,6 +3634,56 @@ line, pos); return 1; } + } else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) { + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + + if (!str || slen < 1 || slen > SSID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", + line, pos); + os_free(str); + return 1; + } + os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen); + bss->multi_ap_backhaul_ssid.ssid_len = slen; + bss->multi_ap_backhaul_ssid.ssid_set = 1; + os_free(str); + } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) { + int len = os_strlen(pos); + + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WPA passphrase length %d (expected 8..63)", + line, len); + return 1; + } + os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase); + bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos); + if (bss->multi_ap_backhaul_ssid.wpa_passphrase) { + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1; + } + } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) { + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + bss->multi_ap_backhaul_ssid.wpa_psk = + os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (!bss->multi_ap_backhaul_ssid.wpa_psk) + return 1; + if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk, + PMK_LEN) || + pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", + line, pos); + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + return 1; + } + bss->multi_ap_backhaul_ssid.wpa_psk->group = 1; + os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase); + bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL; + bss->multi_ap_backhaul_ssid.wpa_psk_set = 1; } else if (os_strcmp(buf, "upnp_iface") == 0) { os_free(bss->upnp_iface); bss->upnp_iface = os_strdup(pos); @@ -2965,15 +3719,15 @@ bss->wps_nfc_pw_from_config = 1; } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) { wpabuf_free(bss->wps_nfc_dh_pubkey); - bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos); + bss->wps_nfc_dh_pubkey = wpabuf_parse_bin(pos); bss->wps_nfc_pw_from_config = 1; } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) { wpabuf_free(bss->wps_nfc_dh_privkey); - bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos); + bss->wps_nfc_dh_privkey = wpabuf_parse_bin(pos); bss->wps_nfc_pw_from_config = 1; } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) { wpabuf_free(bss->wps_nfc_dev_pw); - bss->wps_nfc_dev_pw = hostapd_parse_bin(pos); + bss->wps_nfc_dev_pw = wpabuf_parse_bin(pos); bss->wps_nfc_pw_from_config = 1; #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ @@ -3019,12 +3773,14 @@ bss->time_zone = os_strdup(pos); if (bss->time_zone == NULL) return 1; -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) { bss->wnm_sleep_mode = atoi(pos); + } else if (os_strcmp(buf, "wnm_sleep_mode_no_keys") == 0) { + bss->wnm_sleep_mode_no_keys = atoi(pos); } else if (os_strcmp(buf, "bss_transition") == 0) { bss->bss_transition = atoi(pos); -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_INTERWORKING } else if (os_strcmp(buf, "interworking") == 0) { bss->interworking = atoi(pos); @@ -3062,6 +3818,9 @@ } else if (os_strcmp(buf, "venue_name") == 0) { if (parse_venue_name(bss, pos, line) < 0) return 1; + } else if (os_strcmp(buf, "venue_url") == 0) { + if (parse_venue_url(bss, pos, line) < 0) + return 1; } else if (os_strcmp(buf, "network_auth_type") == 0) { u8 auth_type; u16 redirect_url_len; @@ -3136,8 +3895,19 @@ } else if (os_strcmp(buf, "nai_realm") == 0) { if (parse_nai_realm(bss, pos, line) < 0) return 1; + } else if (os_strcmp(buf, "anqp_elem") == 0) { + if (parse_anqp_elem(bss, pos, line) < 0) + return 1; } else if (os_strcmp(buf, "gas_frag_limit") == 0) { - bss->gas_frag_limit = atoi(pos); + int val = atoi(pos); + + if (val <= 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid gas_frag_limit '%s'", + line, pos); + return 1; + } + bss->gas_frag_limit = val; } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { bss->gas_comeback_delay = atoi(pos); } else if (os_strcmp(buf, "qos_map_set") == 0) { @@ -3149,13 +3919,25 @@ os_free(bss->dump_msk_file); bss->dump_msk_file = os_strdup(pos); #endif /* CONFIG_RADIUS_TEST */ +#ifdef CONFIG_PROXYARP + } else if (os_strcmp(buf, "proxy_arp") == 0) { + bss->proxy_arp = atoi(pos); +#endif /* CONFIG_PROXYARP */ #ifdef CONFIG_HS20 } else if (os_strcmp(buf, "hs20") == 0) { bss->hs20 = atoi(pos); + } else if (os_strcmp(buf, "hs20_release") == 0) { + int val = atoi(pos); + + if (val < 1 || val > (HS20_VERSION >> 4) + 1) { + wpa_printf(MSG_ERROR, + "Line %d: Unsupported hs20_release: %s", + line, pos); + return 1; + } + bss->hs20_release = val; } else if (os_strcmp(buf, "disable_dgaf") == 0) { bss->disable_dgaf = atoi(pos); - } else if (os_strcmp(buf, "proxy_arp") == 0) { - bss->proxy_arp = atoi(pos); } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) { bss->na_mcast_to_ucast = atoi(pos); } else if (os_strcmp(buf, "osen") == 0) { @@ -3216,6 +3998,9 @@ } else if (os_strcmp(buf, "osu_nai") == 0) { if (hs20_parse_osu_nai(bss, pos, line) < 0) return 1; + } else if (os_strcmp(buf, "osu_nai2") == 0) { + if (hs20_parse_osu_nai2(bss, pos, line) < 0) + return 1; } else if (os_strcmp(buf, "osu_method_list") == 0) { if (hs20_parse_osu_method_list(bss, pos, line) < 0) return 1; @@ -3225,12 +4010,34 @@ } else if (os_strcmp(buf, "osu_service_desc") == 0) { if (hs20_parse_osu_service_desc(bss, pos, line) < 0) return 1; + } else if (os_strcmp(buf, "operator_icon") == 0) { + if (hs20_parse_operator_icon(bss, pos, line) < 0) + return 1; } else if (os_strcmp(buf, "subscr_remediation_url") == 0) { os_free(bss->subscr_remediation_url); bss->subscr_remediation_url = os_strdup(pos); } else if (os_strcmp(buf, "subscr_remediation_method") == 0) { bss->subscr_remediation_method = atoi(pos); + } else if (os_strcmp(buf, "hs20_t_c_filename") == 0) { + os_free(bss->t_c_filename); + bss->t_c_filename = os_strdup(pos); + } else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) { + bss->t_c_timestamp = strtol(pos, NULL, 0); + } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) { + os_free(bss->t_c_server_url); + bss->t_c_server_url = os_strdup(pos); + } else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) { + os_free(bss->hs20_sim_provisioning_url); + bss->hs20_sim_provisioning_url = os_strdup(pos); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + } else if (os_strcmp(buf, "mbo") == 0) { + bss->mbo_enabled = atoi(pos); + } else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) { + bss->mbo_cell_data_conn_pref = atoi(pos); + } else if (os_strcmp(buf, "oce") == 0) { + bss->oce = atoi(pos); +#endif /* CONFIG_MBO */ #ifdef CONFIG_TESTING_OPTIONS #define PARSE_TEST_PROBABILITY(_val) \ } else if (os_strcmp(buf, #_val) == 0) { \ @@ -3249,6 +4056,8 @@ PARSE_TEST_PROBABILITY(ignore_assoc_probability) PARSE_TEST_PROBABILITY(ignore_reassoc_probability) PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability) + } else if (os_strcmp(buf, "ecsa_ie_only") == 0) { + conf->ecsa_ie_only = atoi(pos); } else if (os_strcmp(buf, "bss_load_test") == 0) { WPA_PUT_LE16(bss->bss_load_test, atoi(pos)); pos = os_strchr(pos, ':'); @@ -3269,7 +4078,15 @@ WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos)); bss->bss_load_test_set = 1; } else if (os_strcmp(buf, "radio_measurements") == 0) { - bss->radio_measurements = atoi(pos); + /* + * DEPRECATED: This parameter will be removed in the future. + * Use rrm_neighbor_report instead. + */ + int val = atoi(pos); + + if (val & BIT(0)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_NEIGHBOR_REPORT; } else if (os_strcmp(buf, "own_ie_override") == 0) { struct wpabuf *tmp; size_t len = os_strlen(pos) / 2; @@ -3288,39 +4105,30 @@ wpabuf_free(bss->own_ie_override); bss->own_ie_override = tmp; + } else if (os_strcmp(buf, "sae_reflection_attack") == 0) { + bss->sae_reflection_attack = atoi(pos); + } else if (os_strcmp(buf, "sae_commit_override") == 0) { + wpabuf_free(bss->sae_commit_override); + bss->sae_commit_override = wpabuf_parse_bin(pos); #endif /* CONFIG_TESTING_OPTIONS */ - } else if (os_strcmp(buf, "vendor_elements") == 0) { - struct wpabuf *elems; - size_t len = os_strlen(pos); - if (len & 0x01) { - wpa_printf(MSG_ERROR, - "Line %d: Invalid vendor_elements '%s'", - line, pos); +#ifdef CONFIG_SAE + } else if (os_strcmp(buf, "sae_password") == 0) { + if (parse_sae_password(bss, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password", + line); return 1; } - len /= 2; - if (len == 0) { - wpabuf_free(bss->vendor_elements); - bss->vendor_elements = NULL; - return 0; - } - - elems = wpabuf_alloc(len); - if (elems == NULL) +#endif /* CONFIG_SAE */ + } else if (os_strcmp(buf, "vendor_elements") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos)) return 1; - - if (hexstr2bin(pos, wpabuf_put(elems, len), len)) { - wpabuf_free(elems); - wpa_printf(MSG_ERROR, - "Line %d: Invalid vendor_elements '%s'", - line, pos); + } else if (os_strcmp(buf, "assocresp_elements") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->assocresp_elements, pos)) return 1; - } - - wpabuf_free(bss->vendor_elements); - bss->vendor_elements = elems; } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) { bss->sae_anti_clogging_threshold = atoi(pos); + } else if (os_strcmp(buf, "sae_sync") == 0) { + bss->sae_sync = atoi(pos); } else if (os_strcmp(buf, "sae_groups") == 0) { if (hostapd_parse_intlist(&bss->sae_groups, pos)) { wpa_printf(MSG_ERROR, @@ -3328,6 +4136,8 @@ line, pos); return 1; } + } else if (os_strcmp(buf, "sae_require_mfp") == 0) { + bss->sae_require_mfp = atoi(pos); } else if (os_strcmp(buf, "local_pwr_constraint") == 0) { int val = atoi(pos); if (val < 0 || val > 255) { @@ -3391,7 +4201,8 @@ return -1; } val = strtol(pos, &endp, 0); - if (*endp || val < 1 || val > FST_MAX_LLT_MS) { + if (*endp || val < 1 || + (unsigned long int) val > FST_MAX_LLT_MS) { wpa_printf(MSG_ERROR, "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)", line, val, pos, FST_MAX_LLT_MS); @@ -3409,6 +4220,135 @@ } else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) { os_free(bss->no_auth_if_seen_on); bss->no_auth_if_seen_on = os_strdup(pos); + } else if (os_strcmp(buf, "lci") == 0) { + wpabuf_free(conf->lci); + conf->lci = wpabuf_parse_bin(pos); + if (conf->lci && wpabuf_len(conf->lci) == 0) { + wpabuf_free(conf->lci); + conf->lci = NULL; + } + } else if (os_strcmp(buf, "civic") == 0) { + wpabuf_free(conf->civic); + conf->civic = wpabuf_parse_bin(pos); + if (conf->civic && wpabuf_len(conf->civic) == 0) { + wpabuf_free(conf->civic); + conf->civic = NULL; + } + } else if (os_strcmp(buf, "rrm_neighbor_report") == 0) { + if (atoi(pos)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_NEIGHBOR_REPORT; + } else if (os_strcmp(buf, "rrm_beacon_report") == 0) { + if (atoi(pos)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | + WLAN_RRM_CAPS_BEACON_REPORT_TABLE; + } else if (os_strcmp(buf, "gas_address3") == 0) { + bss->gas_address3 = atoi(pos); + } else if (os_strcmp(buf, "stationary_ap") == 0) { + conf->stationary_ap = atoi(pos); + } else if (os_strcmp(buf, "ftm_responder") == 0) { + bss->ftm_responder = atoi(pos); + } else if (os_strcmp(buf, "ftm_initiator") == 0) { + bss->ftm_initiator = atoi(pos); +#ifdef CONFIG_FILS + } else if (os_strcmp(buf, "fils_cache_id") == 0) { + if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fils_cache_id '%s'", + line, pos); + return 1; + } + bss->fils_cache_id_set = 1; + } else if (os_strcmp(buf, "fils_realm") == 0) { + if (parse_fils_realm(bss, pos) < 0) + return 1; + } else if (os_strcmp(buf, "fils_dh_group") == 0) { + bss->fils_dh_group = atoi(pos); + } else if (os_strcmp(buf, "dhcp_server") == 0) { + if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) { + bss->dhcp_rapid_commit_proxy = atoi(pos); + } else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) { + bss->fils_hlp_wait_time = atoi(pos); + } else if (os_strcmp(buf, "dhcp_server_port") == 0) { + bss->dhcp_server_port = atoi(pos); + } else if (os_strcmp(buf, "dhcp_relay_port") == 0) { + bss->dhcp_relay_port = atoi(pos); +#endif /* CONFIG_FILS */ + } else if (os_strcmp(buf, "multicast_to_unicast") == 0) { + bss->multicast_to_unicast = atoi(pos); + } else if (os_strcmp(buf, "broadcast_deauth") == 0) { + bss->broadcast_deauth = atoi(pos); +#ifdef CONFIG_DPP + } else if (os_strcmp(buf, "dpp_connector") == 0) { + os_free(bss->dpp_connector); + bss->dpp_connector = os_strdup(pos); + } else if (os_strcmp(buf, "dpp_netaccesskey") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos)) + return 1; + } else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) { + bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0); + } else if (os_strcmp(buf, "dpp_csign") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos)) + return 1; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_OWE + } else if (os_strcmp(buf, "owe_transition_bssid") == 0) { + if (hwaddr_aton(pos, bss->owe_transition_bssid)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid owe_transition_bssid", + line); + return 1; + } + } else if (os_strcmp(buf, "owe_transition_ssid") == 0) { + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + + if (!str || slen < 1 || slen > SSID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", + line, pos); + os_free(str); + return 1; + } + os_memcpy(bss->owe_transition_ssid, str, slen); + bss->owe_transition_ssid_len = slen; + os_free(str); + } else if (os_strcmp(buf, "owe_transition_ifname") == 0) { + os_strlcpy(bss->owe_transition_ifname, pos, + sizeof(bss->owe_transition_ifname)); + } else if (os_strcmp(buf, "owe_groups") == 0) { + if (hostapd_parse_intlist(&bss->owe_groups, pos)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid owe_groups value '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) { + bss->coloc_intf_reporting = atoi(pos); +#endif /* CONFIG_OWE */ + } else if (os_strcmp(buf, "multi_ap") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 3) { + wpa_printf(MSG_ERROR, "Line %d: Invalid multi_ap '%s'", + line, buf); + return -1; + } + + bss->multi_ap = val; + } else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) { + conf->rssi_reject_assoc_rssi = atoi(pos); + } else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) { + conf->rssi_reject_assoc_timeout = atoi(pos); + } else if (os_strcmp(buf, "pbss") == 0) { + bss->pbss = atoi(pos); } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", @@ -3429,7 +4369,7 @@ { struct hostapd_config *conf; FILE *f; - char buf[512], *pos; + char buf[4096], *pos; int line = 0; int errors = 0; size_t i; --- contrib/wpa/hostapd/config_file.h.orig +++ contrib/wpa/hostapd/config_file.h @@ -13,5 +13,10 @@ int hostapd_set_iface(struct hostapd_config *conf, struct hostapd_bss_config *bss, const char *field, char *value); +int hostapd_acl_comp(const void *a, const void *b); +int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, + int vlan_id, const u8 *addr); +void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num, + const u8 *addr); #endif /* CONFIG_FILE_H */ --- contrib/wpa/hostapd/ctrl_iface.c.orig +++ contrib/wpa/hostapd/ctrl_iface.c @@ -1,6 +1,6 @@ /* * hostapd / UNIX domain socket -based control interface - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2018, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,10 +19,20 @@ #include #include +#ifdef CONFIG_CTRL_IFACE_UDP +#include +#endif /* CONFIG_CTRL_IFACE_UDP */ + #include "utils/common.h" #include "utils/eloop.h" +#include "utils/module_tests.h" #include "common/version.h" #include "common/ieee802_11_defs.h" +#include "common/ctrl_iface_common.h" +#ifdef CONFIG_DPP +#include "common/dpp.h" +#endif /* CONFIG_DPP */ +#include "common/wpa_ctrl.h" #include "crypto/tls.h" #include "drivers/driver.h" #include "eapol_auth/eapol_auth_sm.h" @@ -42,6 +52,9 @@ #include "ap/wnm_ap.h" #include "ap/wpa_auth.h" #include "ap/beacon.h" +#include "ap/neighbor_db.h" +#include "ap/rrm.h" +#include "ap/dpp_hostapd.h" #include "wps/wps_defs.h" #include "wps/wps.h" #include "fst/fst_ctrl_iface.h" @@ -51,15 +64,16 @@ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256 -struct wpa_ctrl_dst { - struct wpa_ctrl_dst *next; - struct sockaddr_un addr; - socklen_t addrlen; - int debug_level; - int errors; -}; +#ifdef CONFIG_CTRL_IFACE_UDP +#define COOKIE_LEN 8 +static unsigned char cookie[COOKIE_LEN]; +static unsigned char gcookie[COOKIE_LEN]; +#define HOSTAPD_CTRL_IFACE_PORT 8877 +#define HOSTAPD_CTRL_IFACE_PORT_LIMIT 50 +#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT 8878 +#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT 50 +#endif /* CONFIG_CTRL_IFACE_UDP */ - static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, enum wpa_msg_type type, const char *buf, size_t len); @@ -66,81 +80,27 @@ static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, - struct sockaddr_un *from, - socklen_t fromlen) + struct sockaddr_storage *from, + socklen_t fromlen, const char *input) { - struct wpa_ctrl_dst *dst; - - dst = os_zalloc(sizeof(*dst)); - if (dst == NULL) - return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dst->next = hapd->ctrl_dst; - hapd->ctrl_dst = dst; - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", - (u8 *) from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); - return 0; + return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input); } static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst, *prev = NULL; - - dst = hapd->ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); - if (prev == NULL) - hapd->ctrl_dst = dst->next; - else - prev->next = dst->next; - os_free(dst); - return 0; - } - prev = dst; - dst = dst->next; - } - return -1; + return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen); } static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen, char *level) { - struct wpa_ctrl_dst *dst; - - wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); - - dst = hapd->ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " - "level", (u8 *) from->sun_path, fromlen - - offsetof(struct sockaddr_un, sun_path)); - dst->debug_level = atoi(level); - return 0; - } - dst = dst->next; - } - - return -1; + return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level); } @@ -808,7 +768,7 @@ #endif /* CONFIG_INTERWORKING */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, const char *cmd) @@ -883,7 +843,9 @@ char *url = NULL; int ret; u8 nei_rep[1000]; - u8 *nei_pos = nei_rep; + int nei_len; + u8 mbo[10]; + size_t mbo_len = 0; if (hwaddr_aton(cmd, addr)) { wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); @@ -921,7 +883,7 @@ /* TODO: TSF configurable/learnable */ bss_term_dur[0] = 4; /* Subelement ID */ bss_term_dur[1] = 10; /* Length */ - os_memset(bss_term_dur, 2, 8); + os_memset(&bss_term_dur[2], 0, 8); end = os_strchr(pos, ','); if (end == NULL) { wpa_printf(MSG_DEBUG, "Invalid bss_term data"); @@ -931,100 +893,11 @@ WPA_PUT_LE16(&bss_term_dur[10], atoi(end)); } + nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep, + sizeof(nei_rep)); + if (nei_len < 0) + return -1; - /* - * BSS Transition Candidate List Entries - Neighbor Report elements - * neighbor=,,, - * ,[,] - */ - pos = cmd; - while (pos) { - u8 *nei_start; - long int val; - char *endptr, *tmp; - - pos = os_strstr(pos, " neighbor="); - if (!pos) - break; - if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) { - wpa_printf(MSG_DEBUG, - "Not enough room for additional neighbor"); - return -1; - } - pos += 10; - - nei_start = nei_pos; - *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT; - nei_pos++; /* length to be filled in */ - - if (hwaddr_aton(pos, nei_pos)) { - wpa_printf(MSG_DEBUG, "Invalid BSSID"); - return -1; - } - nei_pos += ETH_ALEN; - pos += 17; - if (*pos != ',') { - wpa_printf(MSG_DEBUG, "Missing BSSID Information"); - return -1; - } - pos++; - - val = strtol(pos, &endptr, 0); - WPA_PUT_LE32(nei_pos, val); - nei_pos += 4; - if (*endptr != ',') { - wpa_printf(MSG_DEBUG, "Missing Operating Class"); - return -1; - } - pos = endptr + 1; - - *nei_pos++ = atoi(pos); /* Operating Class */ - pos = os_strchr(pos, ','); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, "Missing Channel Number"); - return -1; - } - pos++; - - *nei_pos++ = atoi(pos); /* Channel Number */ - pos = os_strchr(pos, ','); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, "Missing PHY Type"); - return -1; - } - pos++; - - *nei_pos++ = atoi(pos); /* PHY Type */ - end = os_strchr(pos, ' '); - tmp = os_strchr(pos, ','); - if (tmp && (!end || tmp < end)) { - /* Optional Subelements (hexdump) */ - size_t len; - - pos = tmp + 1; - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) { - wpa_printf(MSG_DEBUG, - "Not enough room for neighbor subelements"); - return -1; - } - if (len & 0x01 || - hexstr2bin(pos, nei_pos, len / 2) < 0) { - wpa_printf(MSG_DEBUG, - "Invalid neighbor subelement info"); - return -1; - } - nei_pos += len / 2; - pos = end; - } - - nei_start[1] = nei_pos - nei_start - 2; - } - pos = os_strstr(cmd, " url="); if (pos) { size_t len; @@ -1049,17 +922,115 @@ if (os_strstr(cmd, " disassoc_imminent=1")) req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT; +#ifdef CONFIG_MBO + pos = os_strstr(cmd, "mbo="); + if (pos) { + unsigned int mbo_reason, cell_pref, reassoc_delay; + u8 *mbo_pos = mbo; + + ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason, + &reassoc_delay, &cell_pref); + if (ret != 3) { + wpa_printf(MSG_DEBUG, + "MBO requires three arguments: mbo=::"); + ret = -1; + goto fail; + } + + if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) { + wpa_printf(MSG_DEBUG, + "Invalid MBO transition reason code %u", + mbo_reason); + ret = -1; + goto fail; + } + + /* Valid values for Cellular preference are: 0, 1, 255 */ + if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) { + wpa_printf(MSG_DEBUG, + "Invalid MBO cellular capability %u", + cell_pref); + ret = -1; + goto fail; + } + + if (reassoc_delay > 65535 || + (reassoc_delay && + !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) { + wpa_printf(MSG_DEBUG, + "MBO: Assoc retry delay is only valid in disassoc imminent mode"); + ret = -1; + goto fail; + } + + *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON; + *mbo_pos++ = 1; + *mbo_pos++ = mbo_reason; + *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF; + *mbo_pos++ = 1; + *mbo_pos++ = cell_pref; + + if (reassoc_delay) { + *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY; + *mbo_pos++ = 2; + WPA_PUT_LE16(mbo_pos, reassoc_delay); + mbo_pos += 2; + } + + mbo_len = mbo_pos - mbo; + } +#endif /* CONFIG_MBO */ + ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, valid_int, bss_term_dur, url, - nei_pos > nei_rep ? nei_rep : NULL, - nei_pos - nei_rep); + nei_len ? nei_rep : NULL, nei_len, + mbo_len ? mbo : NULL, mbo_len); +#ifdef CONFIG_MBO +fail: +#endif /* CONFIG_MBO */ os_free(url); return ret; } -#endif /* CONFIG_WNM */ +static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + unsigned int auto_report, timeout; + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for Collocated Interference Request", + MAC2STR(addr)); + return -1; + } + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + auto_report = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + timeout = atoi(pos); + + return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout); +} + +#endif /* CONFIG_WNM_AP */ + + static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, char *buf, size_t buflen) { @@ -1083,7 +1054,7 @@ return pos - buf; pos += ret; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "FT-PSK "); if (os_snprintf_error(end - pos, ret)) @@ -1096,6 +1067,14 @@ return pos - buf; pos += ret; } +#ifdef CONFIG_SHA384 + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SHA384 */ #ifdef CONFIG_SAE if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { ret = os_snprintf(pos, end - pos, "FT-SAE "); @@ -1104,7 +1083,21 @@ pos += ret; } #endif /* CONFIG_SAE */ -#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_FILS + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_FILS */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); @@ -1141,7 +1134,39 @@ return pos - buf; pos += ret; } +#ifdef CONFIG_FILS + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "FILS-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "FILS-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { + ret = os_snprintf(pos, end - pos, "OWE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { + ret = os_snprintf(pos, end - pos, "DPP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_DPP */ + if (pos > buf && *(pos - 1) == ' ') { *(pos - 1) = '\0'; pos--; @@ -1269,6 +1294,42 @@ } +static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd) +{ + struct sta_info *sta; + struct vlan_description vlan_id; + + if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED) + return; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, + sta->addr, &vlan_id) || + (vlan_id.notempty && + vlan_compare(&vlan_id, sta->vlan_desc))) + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + } +} + + +static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd) +{ + struct sta_info *sta; + struct vlan_description vlan_id; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, sta->addr, + &vlan_id) && + (!vlan_id.notempty || + !vlan_compare(&vlan_id, sta->vlan_desc))) + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + } +} + static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) { char *value; @@ -1306,51 +1367,67 @@ wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); #endif /* CONFIG_WPS_TESTING */ -#ifdef CONFIG_INTERWORKING - } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) { - int val = atoi(value); - if (val <= 0) - ret = -1; - else - hapd->gas_frag_limit = val; -#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { hapd->ext_mgmt_frame_handling = atoi(value); } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { hapd->ext_eapol_frame_io = atoi(value); +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { + os_free(hapd->dpp_config_obj_override); + hapd->dpp_config_obj_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) { + os_free(hapd->dpp_discovery_override); + hapd->dpp_discovery_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) { + os_free(hapd->dpp_groups_override); + hapd->dpp_groups_override = os_strdup(value); + } else if (os_strcasecmp(cmd, + "dpp_ignore_netaccesskey_mismatch") == 0) { + hapd->dpp_ignore_netaccesskey_mismatch = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_test") == 0) { + dpp_test = atoi(value); +#endif /* CONFIG_DPP */ #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_MBO + } else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) { + int val; + + if (!hapd->conf->mbo_enabled) + return -1; + + val = atoi(value); + if (val < 0 || val > 1) + return -1; + + hapd->mbo_assoc_disallow = val; + ieee802_11_update_beacons(hapd->iface); + + /* + * TODO: Need to configure drivers that do AP MLME offload with + * disallowing station logic. + */ +#endif /* CONFIG_MBO */ +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { + os_free(hapd->dpp_configurator_params); + hapd->dpp_configurator_params = os_strdup(value); +#endif /* CONFIG_DPP */ } else { - struct sta_info *sta; - int vlan_id; - ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); if (ret) return ret; if (os_strcasecmp(cmd, "deny_mac_file") == 0) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (hostapd_maclist_found( - hapd->conf->deny_mac, - hapd->conf->num_deny_mac, sta->addr, - &vlan_id) && - (!vlan_id || vlan_id == sta->vlan_id)) - ap_sta_disconnect( - hapd, sta, sta->addr, - WLAN_REASON_UNSPECIFIED); - } - } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED && - os_strcasecmp(cmd, "accept_mac_file") == 0) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!hostapd_maclist_found( - hapd->conf->accept_mac, - hapd->conf->num_accept_mac, - sta->addr, &vlan_id) || - (vlan_id && vlan_id != sta->vlan_id)) - ap_sta_disconnect( - hapd, sta, sta->addr, - WLAN_REASON_UNSPECIFIED); - } + hostapd_disassoc_deny_mac(hapd); + } else if (os_strcasecmp(cmd, "accept_mac_file") == 0) { + hostapd_disassoc_accept_mac(hapd); + } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 || + os_strncmp(cmd, "wmm_ac_", 7) == 0) { + hapd->parameter_set_count++; + if (ieee802_11_update_beacons(hapd->iface)) + wpa_printf(MSG_DEBUG, + "Failed to update beacons with WMM parameters"); } } @@ -1411,6 +1488,63 @@ } +static int +hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + struct hostapd_wpa_psk *psk; + const u8 *pmk; + int pmk_len; + int pmk_match; + int sta_match; + int bss_match; + int reason; + + pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); + + for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) { + pmk_match = PMK_LEN == pmk_len && + os_memcmp(psk->psk, pmk, pmk_len) == 0; + sta_match = psk->group == 0 && + os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0; + bss_match = psk->group == 1; + + if (pmk_match && (sta_match || bss_match)) + return 0; + } + + wpa_printf(MSG_INFO, "STA " MACSTR + " PSK/passphrase no longer valid - disconnect", + MAC2STR(sta->addr)); + reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + hostapd_drv_sta_deauth(hapd, sta->addr, reason); + ap_sta_deauthenticate(hapd, sta, reason); + + return 0; +} + + +static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + int err; + + hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk); + + err = hostapd_setup_wpa_psk(conf); + if (err < 0) { + wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d", + err); + return -1; + } + + ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter, + NULL); + + return 0; +} + + #ifdef CONFIG_TESTING_OPTIONS static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd) @@ -1500,6 +1634,137 @@ } +static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd, + char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf; + int stype = 0, ok = 0; + union wpa_event_data event; + + if (!hapd->ext_mgmt_frame_handling) + return -1; + + /* stype= ok=<0/1> buf= */ + + wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd); + + pos = cmd; + param = os_strstr(pos, "stype="); + if (param) { + param += 6; + stype = atoi(param); + } + + param = os_strstr(pos, " ok="); + if (param) { + param += 4; + ok = atoi(param); + } + + param = os_strstr(pos, " buf="); + if (!param) + return -1; + param += 5; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (!buf || hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_TYPE_MGMT; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.stype = stype; + event.tx_status.ack = ok; + hapd->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event); + hapd->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + +static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd, + char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf; + int freq = 0, datarate = 0, ssi_signal = 0; + union wpa_event_data event; + + if (!hapd->ext_mgmt_frame_handling) + return -1; + + /* freq= datarate= ssi_signal= frame= */ + + wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd); + + pos = cmd; + param = os_strstr(pos, "freq="); + if (param) { + param += 5; + freq = atoi(param); + } + + param = os_strstr(pos, " datarate="); + if (param) { + param += 10; + datarate = atoi(param); + } + + param = os_strstr(pos, " ssi_signal="); + if (param) { + param += 12; + ssi_signal = atoi(param); + } + + param = os_strstr(pos, " frame="); + if (param == NULL) + return -1; + param += 7; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.freq = freq; + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + event.rx_mgmt.datarate = datarate; + hapd->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event); + hapd->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd) { char *pos; @@ -1557,8 +1822,8 @@ #define HWSIM_PACKETLEN 1500 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) -void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) +static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { struct hostapd_data *hapd = ctx; const struct ether_header *eth; @@ -1745,8 +2010,6 @@ static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; char *pos; wpa_trace_fail_after = atoi(cmd); @@ -1770,9 +2033,6 @@ char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, wpa_trace_fail_func); #else /* WPA_TRACE_BFD */ @@ -1784,8 +2044,6 @@ static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; char *pos; wpa_trace_test_fail_after = atoi(cmd); @@ -1809,9 +2067,6 @@ char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, wpa_trace_test_fail_func); #else /* WPA_TRACE_BFD */ @@ -1819,6 +2074,245 @@ #endif /* WPA_TRACE_BFD */ } + +static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + u8 zero[WPA_TK_MAX_LEN]; + + os_memset(zero, 0, sizeof(zero)); + + if (hwaddr_aton(cmd, addr)) + return -1; + +#ifdef CONFIG_IEEE80211W + if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) { + if (hapd->last_igtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 1, NULL, 0, + zero, hapd->last_igtk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 1, NULL, 0, + hapd->last_igtk, + hapd->last_igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + + if (is_broadcast_ether_addr(addr)) { + if (hapd->last_gtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 1, NULL, 0, + zero, hapd->last_gtk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 1, NULL, 0, + hapd->last_gtk, hapd->last_gtk_len); + } + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + if (sta->last_tk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR, + MAC2STR(sta->addr)); + + /* First, use a zero key to avoid any possible duplicate key avoidance + * in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + zero, sta->last_tk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC/RSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + sta->last_tk, sta->last_tk_len); +} + + +static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *pos = cmd; + enum wpa_alg alg; + int idx, set_tx; + u8 seq[6], key[WPA_TK_MAX_LEN]; + size_t key_len; + + /* parameters: alg addr idx set_tx seq key */ + + alg = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + if (hwaddr_aton(pos, addr)) + return -1; + pos += 17; + if (*pos != ' ') + return -1; + pos++; + idx = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + set_tx = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + if (hexstr2bin(pos, seq, sizeof(seq)) < 0) + return -1; + pos += 2 * 6; + if (*pos != ' ') + return -1; + pos++; + key_len = os_strlen(pos) / 2; + if (hexstr2bin(pos, key, key_len) < 0) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Set key"); + return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx, + set_tx, seq, 6, key, key_len); +} + + +static void restore_tk(void *ctx1, void *ctx2) +{ + struct hostapd_data *hapd = ctx1; + struct sta_info *sta = ctx2; + + wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR, + MAC2STR(sta->addr)); + /* This does not really restore the TSC properly, so this will result + * in replay protection issues for now since there is no clean way of + * preventing encryption of a single EAPOL frame. */ + hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + sta->last_tk, sta->last_tk_len); +} + + +static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m1(sta->wpa_sm, + os_strstr(cmd, "change-anonce") != NULL, + plain ? restore_tk : NULL, hapd, sta); +} + + +static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m3(sta->wpa_sm, + plain ? restore_tk : NULL, hapd, sta); +} + + +static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd, + const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, + "TESTING: Send group M1 for the same GTK and zero RSC to " + MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_group_m1(sta->wpa_sm, + plain ? restore_tk : NULL, hapd, sta); +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -1835,6 +2329,11 @@ return ret; for (i = 0; i < iface->num_bss; i++) { + + /* Save CHAN_SWITCH VHT config */ + hostapd_chan_switch_vht_config( + iface->bss[i], settings.freq_params.vht_enabled); + ret = hostapd_switch_channel(iface->bss[i], &settings); if (ret) { /* FIX: What do we do if CSA fails in the middle of @@ -1875,13 +2374,13 @@ /* cmd: [] */ vendor_id = strtoul(cmd, &pos, 16); - if (!isblank(*pos)) + if (!isblank((unsigned char) *pos)) return -EINVAL; subcmd = strtoul(pos, &pos, 10); if (*pos != '\0') { - if (!isblank(*pos++)) + if (!isblank((unsigned char) *pos++)) return -EINVAL; data_len = os_strlen(pos); } @@ -2016,6 +2515,9 @@ struct hostapd_sta_info *info; struct os_reltime now; + if (!iface->num_sta_seen) + return 0; + sta_track_expire(iface, 0); pos = buf; @@ -2028,8 +2530,9 @@ int ret; os_reltime_sub(&now, &info->last_seen, &age); - ret = os_snprintf(pos, end - pos, MACSTR " %u\n", - MAC2STR(info->addr), (unsigned int) age.sec); + ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n", + MAC2STR(info->addr), (unsigned int) age.sec, + info->ssi_signal); if (os_snprintf_error(end - pos, ret)) break; pos += ret; @@ -2040,10 +2543,378 @@ #endif /* NEED_AP_MLME */ +static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address"); + return -1; + } + + return hostapd_send_lci_req(hapd, addr); +} + + +static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd) +{ + u8 addr[ETH_ALEN]; + char *token, *context = NULL; + int random_interval, min_ap; + u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS]; + unsigned int n_responders; + + token = str_token(cmd, " ", &context); + if (!token || hwaddr_aton(token, addr)) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE - Bad destination address"); + return -1; + } + + token = str_token(cmd, " ", &context); + if (!token) + return -1; + + random_interval = atoi(token); + if (random_interval < 0 || random_interval > 0xffff) + return -1; + + token = str_token(cmd, " ", &context); + if (!token) + return -1; + + min_ap = atoi(token); + if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP) + return -1; + + n_responders = 0; + while ((token = str_token(cmd, " ", &context))) { + if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE: Too many responders"); + return -1; + } + + if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE: Bad responder address"); + return -1; + } + + n_responders++; + } + + if (!n_responders) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE - No FTM responder address"); + return -1; + } + + return hostapd_send_range_req(hapd, addr, random_interval, min_ap, + responders, n_responders); +} + + +static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd, + const char *cmd, char *reply, + size_t reply_size) +{ + u8 addr[ETH_ALEN]; + const char *pos; + struct wpabuf *req; + int ret; + u8 req_mode = 0; + + if (hwaddr_aton(cmd, addr)) + return -1; + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + if (os_strncmp(pos, "req_mode=", 9) == 0) { + int val = hex2byte(pos + 9); + + if (val < 0) + return -1; + req_mode = val; + pos += 11; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + } + req = wpabuf_parse_bin(pos); + if (!req) + return -1; + + ret = hostapd_send_beacon_req(hapd, addr, req_mode, req); + wpabuf_free(req); + if (ret >= 0) + ret = os_snprintf(reply, reply_size, "%d", ret); + return ret; +} + + +static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) +{ + struct wpa_ssid_value ssid; + u8 bssid[ETH_ALEN]; + struct wpabuf *nr, *lci = NULL, *civic = NULL; + int stationary = 0; + char *tmp; + int ret; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Neighbor report is not enabled"); + return -1; + } + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad or missing SSID"); + return -1; + } + buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' '); + if (!buf) + return -1; + + tmp = os_strstr(buf, "nr="); + if (!tmp) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Missing Neighbor Report element"); + return -1; + } + + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + + nr = wpabuf_parse_bin(tmp + 3); + if (!nr) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad Neighbor Report element"); + return -1; + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "lci="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + lci = wpabuf_parse_bin(tmp + 4); + if (!lci) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad LCI subelement"); + wpabuf_free(nr); + return -1; + } + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "civic="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + civic = wpabuf_parse_bin(tmp + 6); + if (!civic) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad civic subelement"); + wpabuf_free(nr); + wpabuf_free(lci); + return -1; + } + } + + if (!buf) + goto set; + + if (os_strstr(buf, "stat")) + stationary = 1; + +set: + ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic, + stationary); + + wpabuf_free(nr); + wpabuf_free(lci); + wpabuf_free(civic); + + return ret; +} + + +static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd, + char *buf) +{ + struct wpa_ssid_value ssid; + u8 bssid[ETH_ALEN]; + char *tmp; + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID"); + return -1; + } + + return hostapd_neighbor_remove(hapd, bssid, &ssid); +} + + +static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf, + size_t buflen) +{ + int ret, i; + char *pos, *end; + + ret = os_snprintf(buf, buflen, "%016llX:\n", + (long long unsigned) iface->drv_flags); + if (os_snprintf_error(buflen, ret)) + return -1; + + pos = buf + ret; + end = buf + buflen; + + for (i = 0; i < 64; i++) { + if (iface->drv_flags & (1LLU << i)) { + ret = os_snprintf(pos, end - pos, "%s\n", + driver_flag_to_string(1LLU << i)); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + } + + return pos - buf; +} + + +static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct vlan_description vlan_id; + + if (!(*num)) + return 0; + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + if (hostapd_maclist_found(*acl, *num, addr, &vlan_id)) + hostapd_remove_acl_mac(acl, num, addr); + + return 0; +} + + +static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl, + int *num) +{ + while (*num) + hostapd_remove_acl_mac(acl, num, (*acl)[0].addr); +} + + +static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num, + char *buf, size_t buflen) +{ + int i = 0, len = 0, ret = 0; + + if (!acl) + return 0; + + while (i < num) { + ret = os_snprintf(buf + len, buflen - len, + MACSTR " VLAN_ID=%d\n", + MAC2STR(acl[i].addr), + acl[i].vlan_id.untagged); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + i++; + len += ret; + } + return len; +} + + +static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct vlan_description vlan_id; + int ret = 0, vlanid = 0; + const char *pos; + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = os_strstr(cmd, "VLAN_ID="); + if (pos) + vlanid = atoi(pos + 8); + + if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) { + ret = hostapd_add_acl_maclist(acl, num, vlanid, addr); + if (ret != -1 && *acl) + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + } + + return ret < 0 ? -1 : 0; +} + + +static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd, + const char *field, char *buf, + size_t buflen) +{ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field); + +#ifdef CONFIG_DPP + if (os_strcmp(field, "dpp") == 0) { + int res; + +#ifdef CONFIG_DPP2 + res = os_snprintf(buf, buflen, "DPP=2"); +#else /* CONFIG_DPP2 */ + res = os_snprintf(buf, buflen, "DPP=1"); +#endif /* CONFIG_DPP2 */ + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_DPP */ + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", + field); + + return -1; +} + + static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, char *buf, char *reply, int reply_size, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { int reply_len, res; @@ -2057,6 +2928,8 @@ } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; + } else if (os_strncmp(buf, "NOTE ", 5) == 0) { + wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); } else if (os_strcmp(buf, "STATUS") == 0) { reply_len = hostapd_ctrl_iface_status(hapd, reply, reply_size); @@ -2104,8 +2977,11 @@ reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, reply_size); } else if (os_strcmp(buf, "ATTACH") == 0) { - if (hostapd_ctrl_iface_attach(hapd, from, fromlen)) + if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL)) reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7)) + reply_len = -1; } else if (os_strcmp(buf, "DETACH") == 0) { if (hostapd_ctrl_iface_detach(hapd, from, fromlen)) reply_len = -1; @@ -2122,6 +2998,14 @@ } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { if (hostapd_ctrl_iface_disassociate(hapd, buf + 13)) reply_len = -1; +#ifdef CONFIG_TAXONOMY + } else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) { + reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10, + reply, reply_size); +#endif /* CONFIG_TAXONOMY */ + } else if (os_strncmp(buf, "POLL_STA ", 9) == 0) { + if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9)) + reply_len = -1; } else if (os_strcmp(buf, "STOP_AP") == 0) { if (hostapd_ctrl_iface_stop_ap(hapd)) reply_len = -1; @@ -2188,7 +3072,7 @@ if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16)) reply_len = -1; #endif /* CONFIG_HS20 */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) reply_len = -1; @@ -2198,7 +3082,10 @@ } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) { if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11)) reply_len = -1; -#endif /* CONFIG_WNM */ + } else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) { + if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15)) + reply_len = -1; +#endif /* CONFIG_WNM_AP */ } else if (os_strcmp(buf, "GET_CONFIG") == 0) { reply_len = hostapd_ctrl_iface_get_config(hapd, reply, reply_size); @@ -2211,6 +3098,9 @@ } else if (os_strncmp(buf, "ENABLE", 6) == 0) { if (hostapd_ctrl_iface_enable(hapd->iface)) reply_len = -1; + } else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) { + if (hostapd_ctrl_iface_reload_wpa_psk(hapd)) + reply_len = -1; } else if (os_strncmp(buf, "RELOAD", 6) == 0) { if (hostapd_ctrl_iface_reload(hapd->iface)) reply_len = -1; @@ -2227,6 +3117,13 @@ } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8)) reply_len = -1; + } else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) { + if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd, + buf + 23) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) { + if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0) + reply_len = -1; } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0) reply_len = -1; @@ -2250,6 +3147,24 @@ reply_len = -1; } else if (os_strcmp(buf, "GET_FAIL") == 0) { reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size); + } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) { + if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "SET_KEY ", 8) == 0) { + if (hostapd_ctrl_set_key(hapd, buf + 8) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) { + if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) { + if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) { + if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "REKEY_GTK") == 0) { + if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) @@ -2276,6 +3191,165 @@ reply_len = hostapd_ctrl_iface_track_sta_list( hapd, reply, reply_size); #endif /* NEED_AP_MLME */ + } else if (os_strcmp(buf, "PMKSA") == 0) { + reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply, + reply_size); + } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { + hostapd_ctrl_iface_pmksa_flush(hapd); + } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) { + if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) { + if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) { + if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) { + if (hostapd_ctrl_iface_req_lci(hapd, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) { + if (hostapd_ctrl_iface_req_range(hapd, buf + 10)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) { + reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11, + reply, reply_size); + } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { + reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, + reply_size); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); + } else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) { + if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) { + if (!hostapd_ctrl_iface_acl_add_mac( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac, buf + 19)) + hostapd_disassoc_accept_mac(hapd); + else + reply_len = -1; + } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) { + hostapd_ctrl_iface_acl_del_mac( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac, buf + 19); + } else if (os_strcmp(buf + 11, "SHOW") == 0) { + reply_len = hostapd_ctrl_iface_acl_show_mac( + hapd->conf->accept_mac, + hapd->conf->num_accept_mac, reply, reply_size); + } else if (os_strcmp(buf + 11, "CLEAR") == 0) { + hostapd_ctrl_iface_acl_clear_list( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac); + } + } else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) { + if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) { + if (!hostapd_ctrl_iface_acl_add_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17)) + hostapd_disassoc_deny_mac(hapd); + } else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) { + hostapd_ctrl_iface_acl_del_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17); + } else if (os_strcmp(buf + 9, "SHOW") == 0) { + reply_len = hostapd_ctrl_iface_acl_show_mac( + hapd->conf->deny_mac, + hapd->conf->num_deny_mac, reply, reply_size); + } else if (os_strcmp(buf + 9, "CLEAR") == 0) { + hostapd_ctrl_iface_acl_clear_list( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac); + } +#ifdef CONFIG_DPP + } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) { + res = hostapd_dpp_qr_code(hapd, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { + res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { + if (dpp_bootstrap_remove(hapd->iface->interfaces->dpp, + buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { + const char *uri; + + uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp, + atoi(buf + 22)); + if (!uri) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%s", uri); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { + reply_len = dpp_bootstrap_info(hapd->iface->interfaces->dpp, + atoi(buf + 19), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { + if (hostapd_dpp_auth_init(hapd, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) { + if (hostapd_dpp_listen(hapd, buf + 11) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) { + hostapd_dpp_stop(hapd); + hostapd_dpp_listen_stop(hapd); + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { + res = dpp_configurator_add(hapd->iface->interfaces->dpp, + buf + 20); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { + if (dpp_configurator_remove(hapd->iface->interfaces->dpp, + buf + 24) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { + if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { + reply_len = dpp_configurator_get_key_id( + hapd->iface->interfaces->dpp, + atoi(buf + 25), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { + res = hostapd_dpp_pkex_add(hapd, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) { + if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0) + reply_len = -1; +#endif /* CONFIG_DPP */ +#ifdef RADIUS_SERVER + } else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) { + if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0) + reply_len = -1; +#endif /* RADIUS_SERVER */ + } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { + reply_len = hostapd_ctrl_iface_get_capability( + hapd, buf + 15, reply, reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -2296,12 +3370,15 @@ struct hostapd_data *hapd = eloop_ctx; char buf[4096]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); - char *reply; + char *reply, *pos = buf; const int reply_size = 4096; int reply_len; int level = MSG_DEBUG; +#ifdef CONFIG_CTRL_IFACE_UDP + unsigned char lcookie[COOKIE_LEN]; +#endif /* CONFIG_CTRL_IFACE_UDP */ res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); @@ -2311,9 +3388,6 @@ return; } buf[res] = '\0'; - if (os_strcmp(buf, "PING") == 0) - level = MSG_EXCESSIVE; - wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res); reply = os_malloc(reply_size); if (reply == NULL) { @@ -2325,10 +3399,46 @@ return; } - reply_len = hostapd_ctrl_iface_receive_process(hapd, buf, +#ifdef CONFIG_CTRL_IFACE_UDP + if (os_strcmp(buf, "GET_COOKIE") == 0) { + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + cookie, COOKIE_LEN); + reply_len = 7 + 2 * COOKIE_LEN; + goto done; + } + + if (os_strncmp(buf, "COOKIE=", 7) != 0 || + hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "CTRL: No cookie in the request - drop request"); + os_free(reply); + return; + } + + if (os_memcmp(cookie, lcookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid cookie in the request - drop request"); + os_free(reply); + return; + } + + pos = buf + 7 + 2 * COOKIE_LEN; + while (*pos == ' ') + pos++; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + if (os_strcmp(pos, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res); + + reply_len = hostapd_ctrl_iface_receive_process(hapd, pos, reply, reply_size, &from, fromlen); +#ifdef CONFIG_CTRL_IFACE_UDP +done: +#endif /* CONFIG_CTRL_IFACE_UDP */ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", @@ -2338,6 +3448,7 @@ } +#ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) { char *buf; @@ -2357,6 +3468,7 @@ buf[len - 1] = '\0'; return buf; } +#endif /* CONFIG_CTRL_IFACE_UDP */ static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, @@ -2372,6 +3484,99 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { +#ifdef CONFIG_CTRL_IFACE_UDP + int port = HOSTAPD_CTRL_IFACE_PORT; + char p[32] = { 0 }; + char port_str[40], *tmp; + char *pos; + struct addrinfo hints = { 0 }, *res, *saveres; + int n; + + if (hapd->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + if (hapd->conf->ctrl_interface == NULL) + return 0; + + pos = os_strstr(hapd->conf->ctrl_interface, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port"); + goto fail; + } + } + + dl_list_init(&hapd->ctrl_dst); + hapd->ctrl_sock = -1; + os_get_random(cookie, COOKIE_LEN); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + hints.ai_flags = AI_PASSIVE; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + hints.ai_family = AF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_family = AF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_socktype = SOCK_DGRAM; + +try_again: + os_snprintf(p, sizeof(p), "%d", port); + n = getaddrinfo(NULL, p, &hints, &res); + if (n) { + wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); + goto fail; + } + + saveres = res; + hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (hapd->ctrl_sock < 0) { + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); + goto fail; + } + + if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) { + port--; + if ((HOSTAPD_CTRL_IFACE_PORT - port) < + HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos) + goto try_again; + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); + goto fail; + } + + freeaddrinfo(saveres); + + os_snprintf(port_str, sizeof(port_str), "udp:%d", port); + tmp = os_strdup(port_str); + if (tmp) { + os_free(hapd->conf->ctrl_interface); + hapd->conf->ctrl_interface = tmp; + } + wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); + + if (eloop_register_read_sock(hapd->ctrl_sock, + hostapd_ctrl_iface_receive, hapd, NULL) < + 0) { + hostapd_ctrl_iface_deinit(hapd); + return -1; + } + + hapd->msg_ctx = hapd; + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); + + return 0; + +fail: + if (hapd->ctrl_sock >= 0) + close(hapd->ctrl_sock); + return -1; +#else /* CONFIG_CTRL_IFACE_UDP */ struct sockaddr_un addr; int s = -1; char *fname = NULL; @@ -2381,6 +3586,8 @@ return 0; } + dl_list_init(&hapd->ctrl_dst); + if (hapd->conf->ctrl_interface == NULL) return 0; @@ -2396,9 +3603,9 @@ } if (hapd->conf->ctrl_interface_gid_set && - chown(hapd->conf->ctrl_interface, -1, - hapd->conf->ctrl_interface_gid) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(hapd->conf->ctrl_interface, -1, + hapd->conf->ctrl_interface_gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); return -1; } @@ -2405,9 +3612,9 @@ if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && - chown(hapd->conf->ctrl_interface, -1, - hapd->iface->interfaces->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(hapd->conf->ctrl_interface, -1, + hapd->iface->interfaces->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); return -1; } @@ -2480,8 +3687,8 @@ } if (hapd->conf->ctrl_interface_gid_set && - chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", + lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", strerror(errno)); goto fail; } @@ -2488,8 +3695,8 @@ if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && - chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", + lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", strerror(errno)); goto fail; } @@ -2520,6 +3727,7 @@ os_free(fname); } return -1; +#endif /* CONFIG_CTRL_IFACE_UDP */ } @@ -2528,10 +3736,14 @@ struct wpa_ctrl_dst *dst, *prev; if (hapd->ctrl_sock > -1) { +#ifndef CONFIG_CTRL_IFACE_UDP char *fname; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + eloop_unregister_read_sock(hapd->ctrl_sock); close(hapd->ctrl_sock); hapd->ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP fname = hostapd_ctrl_iface_path(hapd); if (fname) unlink(fname); @@ -2550,15 +3762,12 @@ strerror(errno)); } } +#endif /* !CONFIG_CTRL_IFACE_UDP */ } - dst = hapd->ctrl_dst; - hapd->ctrl_dst = NULL; - while (dst) { - prev = dst; - dst = dst->next; - os_free(prev); - } + dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); #ifdef CONFIG_TESTING_OPTIONS l2_packet_deinit(hapd->l2_test); @@ -2590,54 +3799,19 @@ static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces, - struct sockaddr_un *from, - socklen_t fromlen) + struct sockaddr_storage *from, + socklen_t fromlen, char *input) { - struct wpa_ctrl_dst *dst; - - dst = os_zalloc(sizeof(*dst)); - if (dst == NULL) - return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dst->next = interfaces->global_ctrl_dst; - interfaces->global_ctrl_dst = dst; - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)", - from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); - return 0; + return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen, + input); } static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst, *prev = NULL; - - dst = interfaces->global_ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, - "CTRL_IFACE monitor detached (global)", - from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); - if (prev == NULL) - interfaces->global_ctrl_dst = dst->next; - else - prev->next = dst->next; - os_free(dst); - return 0; - } - prev = dst; - dst = dst->next; - } - return -1; + return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen); } @@ -2648,6 +3822,16 @@ wps_testing_dummy_cred = 0; wps_corrupt_pkhash = 0; #endif /* CONFIG_WPS_TESTING */ + +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_DPP + dpp_test = DPP_TEST_DISABLED; +#endif /* CONFIG_DPP */ +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_DPP + dpp_global_clear(interfaces->dpp); +#endif /* CONFIG_DPP */ } @@ -2791,6 +3975,51 @@ static int +hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces, + const char *input, + char *reply, int reply_size) +{ + size_t i, j; + int res; + char *pos, *end; + struct hostapd_iface *iface; + int show_ctrl = 0; + + if (input) + show_ctrl = !!os_strstr(input, "ctrl"); + + pos = reply; + end = reply + reply_size; + + for (i = 0; i < interfaces->count; i++) { + iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_bss_config *conf; + + conf = iface->conf->bss[j]; + if (show_ctrl) + res = os_snprintf(pos, end - pos, + "%s ctrl_iface=%s\n", + conf->iface, + conf->ctrl_interface ? + conf->ctrl_interface : "N/A"); + else + res = os_snprintf(pos, end - pos, "%s\n", + conf->iface); + if (os_snprintf_error(end - pos, res)) { + *pos = '\0'; + return pos - reply; + } + pos += res; + } + } + + return pos - reply; +} + + +static int hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces, char *cmd) { @@ -2839,7 +4068,7 @@ const char *ifname, char *buf, char *reply, int reply_size, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { struct hostapd_data *hapd; @@ -2863,15 +4092,18 @@ void *sock_ctx) { void *interfaces = eloop_ctx; - char buf[256]; + char buffer[256], *buf = buffer; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); char *reply; int reply_len; const int reply_size = 4096; +#ifdef CONFIG_CTRL_IFACE_UDP + unsigned char lcookie[COOKIE_LEN]; +#endif /* CONFIG_CTRL_IFACE_UDP */ - res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", @@ -2894,6 +4126,35 @@ os_memcpy(reply, "OK\n", 3); reply_len = 3; +#ifdef CONFIG_CTRL_IFACE_UDP + if (os_strcmp(buf, "GET_COOKIE") == 0) { + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + gcookie, COOKIE_LEN); + reply_len = 7 + 2 * COOKIE_LEN; + goto send_reply; + } + + if (os_strncmp(buf, "COOKIE=", 7) != 0 || + hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "CTRL: No cookie in the request - drop request"); + os_free(reply); + return; + } + + if (os_memcmp(gcookie, lcookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid cookie in the request - drop request"); + os_free(reply); + return; + } + + buf += 7 + 2 * COOKIE_LEN; + while (*buf == ' ') + buf++; +#endif /* CONFIG_CTRL_IFACE_UDP */ + if (os_strncmp(buf, "IFNAME=", 7) == 0) { char *pos = os_strchr(buf + 7, ' '); @@ -2922,8 +4183,12 @@ reply_len = -1; } else if (os_strcmp(buf, "ATTACH") == 0) { if (hostapd_global_ctrl_iface_attach(interfaces, &from, - fromlen)) + fromlen, NULL)) reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_global_ctrl_iface_attach(interfaces, &from, + fromlen, buf + 7)) + reply_len = -1; } else if (os_strcmp(buf, "DETACH") == 0) { if (hostapd_global_ctrl_iface_detach(interfaces, &from, fromlen)) @@ -2930,7 +4195,6 @@ reply_len = -1; #ifdef CONFIG_MODULE_TESTS } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { - int hapd_module_tests(void); if (hapd_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ @@ -2954,6 +4218,11 @@ reply_len = os_snprintf(reply, reply_size, "OK\n"); else reply_len = -1; + } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { + reply_len = hostapd_global_ctrl_iface_interfaces( + interfaces, buf + 10, reply, sizeof(buffer)); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); } else { wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " "ignored"); @@ -2975,6 +4244,7 @@ } +#ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) { char *buf; @@ -2994,10 +4264,93 @@ buf[len - 1] = '\0'; return buf; } +#endif /* CONFIG_CTRL_IFACE_UDP */ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) { +#ifdef CONFIG_CTRL_IFACE_UDP + int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT; + char p[32] = { 0 }; + char *pos; + struct addrinfo hints = { 0 }, *res, *saveres; + int n; + + if (interface->global_ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + if (interface->global_iface_path == NULL) + return 0; + + pos = os_strstr(interface->global_iface_path, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port"); + goto fail; + } + } + + os_get_random(gcookie, COOKIE_LEN); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + hints.ai_flags = AI_PASSIVE; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + hints.ai_family = AF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_family = AF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_socktype = SOCK_DGRAM; + +try_again: + os_snprintf(p, sizeof(p), "%d", port); + n = getaddrinfo(NULL, p, &hints, &res); + if (n) { + wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); + goto fail; + } + + saveres = res; + interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (interface->global_ctrl_sock < 0) { + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); + goto fail; + } + + if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) < + 0) { + port++; + if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) < + HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos) + goto try_again; + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); + goto fail; + } + + freeaddrinfo(saveres); + + wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port); + + if (eloop_register_read_sock(interface->global_ctrl_sock, + hostapd_global_ctrl_iface_receive, + interface, NULL) < 0) { + hostapd_global_ctrl_iface_deinit(interface); + return -1; + } + + return 0; + +fail: + if (interface->global_ctrl_sock >= 0) + close(interface->global_ctrl_sock); + return -1; +#else /* CONFIG_CTRL_IFACE_UDP */ struct sockaddr_un addr; int s = -1; char *fname = NULL; @@ -3017,9 +4370,9 @@ goto fail; } } else if (interface->ctrl_iface_group && - chown(interface->global_iface_path, -1, - interface->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(interface->global_iface_path, -1, + interface->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); goto fail; } @@ -3076,8 +4429,8 @@ } if (interface->ctrl_iface_group && - chown(fname, -1, interface->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(fname, -1, interface->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); goto fail; } @@ -3103,12 +4456,15 @@ os_free(fname); } return -1; +#endif /* CONFIG_CTRL_IFACE_UDP */ } void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) { +#ifndef CONFIG_CTRL_IFACE_UDP char *fname = NULL; +#endif /* CONFIG_CTRL_IFACE_UDP */ struct wpa_ctrl_dst *dst, *prev; if (interfaces->global_ctrl_sock > -1) { @@ -3115,6 +4471,7 @@ eloop_unregister_read_sock(interfaces->global_ctrl_sock); close(interfaces->global_ctrl_sock); interfaces->global_ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP fname = hostapd_global_ctrl_iface_path(interfaces); if (fname) { unlink(fname); @@ -3134,26 +4491,36 @@ strerror(errno)); } } +#endif /* CONFIG_CTRL_IFACE_UDP */ } os_free(interfaces->global_iface_path); interfaces->global_iface_path = NULL; - dst = interfaces->global_ctrl_dst; - interfaces->global_ctrl_dst = NULL; - while (dst) { - prev = dst; - dst = dst->next; - os_free(prev); - } + dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst, + struct wpa_ctrl_dst, list) + os_free(dst); } +static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst, + const char *buf) +{ + /* Enable Probe Request events based on explicit request. + * Other events are enabled by default. + */ + if (str_starts(buf, RX_PROBE_REQUEST)) + return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST); + return 1; +} + + static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, enum wpa_msg_type type, const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; + struct dl_list *ctrl_dst; struct msghdr msg; int idx; struct iovec io[2]; @@ -3162,13 +4529,13 @@ if (type != WPA_MSG_ONLY_GLOBAL) { s = hapd->ctrl_sock; - dst = hapd->ctrl_dst; + ctrl_dst = &hapd->ctrl_dst; } else { s = hapd->iface->interfaces->global_ctrl_sock; - dst = hapd->iface->interfaces->global_ctrl_dst; + ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst; } - if (s < 0 || dst == NULL) + if (s < 0 || dl_list_empty(ctrl_dst)) return; os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); @@ -3181,12 +4548,11 @@ msg.msg_iovlen = 2; idx = 0; - while (dst) { - next = dst->next; - if (level >= dst->debug_level) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", - (u8 *) dst->addr.sun_path, dst->addrlen - - offsetof(struct sockaddr_un, sun_path)); + dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { + if ((level >= dst->debug_level) && + hostapd_ctrl_check_event_enabled(dst, buf)) { + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send", + &dst->addr, dst->addrlen); msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; if (sendmsg(s, &msg, 0) < 0) { @@ -3210,7 +4576,6 @@ dst->errors = 0; } idx++; - dst = next; } } --- contrib/wpa/hostapd/defconfig.orig +++ contrib/wpa/hostapd/defconfig @@ -18,6 +18,9 @@ # Driver interface for drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y +# QCA vendor extensions to nl80211 +#CONFIG_DRIVER_NL80211_QCA=y + # driver_nl80211.c requires libnl. If you are compiling it yourself # you may need to point hostapd to your version of libnl. # @@ -28,7 +31,7 @@ #CONFIG_LIBNL20=y # Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored) -#CONFIG_LIBNL32=y +CONFIG_LIBNL32=y # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) @@ -47,12 +50,12 @@ # WPA2/IEEE 802.11i RSN pre-authentication CONFIG_RSN_PREAUTH=y -# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) -CONFIG_PEERKEY=y - # IEEE 802.11w (management frame protection) CONFIG_IEEE80211W=y +# Support Operating Channel Validation +#CONFIG_OCV=y + # Integrated EAP server CONFIG_EAP=y @@ -154,6 +157,12 @@ # IEEE 802.11ac (Very High Throughput) support #CONFIG_IEEE80211AC=y +# IEEE 802.11ax HE support +# Note: This is experimental and work in progress. The definitions are still +# subject to change and this should not be expected to interoperate with the +# final IEEE 802.11ax version. +#CONFIG_IEEE80211AX=y + # Remove debugging code that is printing out debug messages to stdout. # This can be used to reduce the size of the hostapd considerably if debugging # code is not needed. @@ -163,6 +172,9 @@ # Disabled by default. #CONFIG_DEBUG_FILE=y +# Send debug messages to syslog instead of stdout +#CONFIG_DEBUG_SYSLOG=y + # Add support for sending all debug messages (regardless of debug verbosity) # to the Linux kernel tracing facility. This helps debug the entire stack by # making it easy to record everything happening from the driver up into the @@ -240,6 +252,11 @@ # requirements described above. #CONFIG_NO_RANDOM_POOL=y +# Should we attempt to use the getrandom(2) call that provides more reliable +# yet secure randomness source than /dev/random on Linux 3.17 and newer. +# Requires glibc 2.25 to build, falls back to /dev/random if unavailable. +#CONFIG_GETRANDOM=y + # Should we use poll instead of select? Select is used by default. #CONFIG_ELOOP_POLL=y @@ -246,10 +263,14 @@ # Should we use epoll instead of select? Select is used by default. #CONFIG_ELOOP_EPOLL=y +# Should we use kqueue instead of select? Select is used by default. +#CONFIG_ELOOP_KQUEUE=y + # Select TLS implementation # openssl = OpenSSL (default) # gnutls = GnuTLS # internal = Internal TLSv1 implementation (experimental) +# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental) # none = Empty template #CONFIG_TLS=openssl @@ -262,6 +283,10 @@ # can be enabled to enable use of stronger crypto algorithms. #CONFIG_TLSV12=y +# Select which ciphers to use by default with OpenSSL if the user does not +# specify them. +#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW" + # If CONFIG_TLS=internal is used, additional library and include paths are # needed for LibTomMath. Alternatively, an integrated, minimal version of # LibTomMath can be used. See beginning of libtommath.c for details on benefits @@ -326,3 +351,31 @@ # http://wireless.kernel.org/en/users/Documentation/acs # #CONFIG_ACS=y + +# Multiband Operation support +# These extentions facilitate efficient use of multiple frequency bands +# available to the AP and the devices that may associate with it. +#CONFIG_MBO=y + +# Client Taxonomy +# Has the AP retain the Probe Request and (Re)Association Request frames from +# a client, from which a signature can be produced which can identify the model +# of client device like "Nexus 6P" or "iPhone 5s". +#CONFIG_TAXONOMY=y + +# Fast Initial Link Setup (FILS) (IEEE 802.11ai) +#CONFIG_FILS=y +# FILS shared key authentication with PFS +#CONFIG_FILS_SK_PFS=y + +# Include internal line edit mode in hostapd_cli. This can be used to provide +# limited command line editing and history support. +#CONFIG_WPA_CLI_EDIT=y + +# Opportunistic Wireless Encryption (OWE) +# Experimental implementation of draft-harkins-owe-07.txt +#CONFIG_OWE=y + +# Override default value for the wpa_disable_eapol_key_retries configuration +# parameter. See that parameter in hostapd.conf for more details. +#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1 --- contrib/wpa/hostapd/hapd_module_tests.c.orig +++ contrib/wpa/hostapd/hapd_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" int hapd_module_tests(void) { --- contrib/wpa/hostapd/hlr_auc_gw.c.orig +++ contrib/wpa/hostapd/hlr_auc_gw.c @@ -1,6 +1,6 @@ /* * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator - * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen + * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -284,7 +284,7 @@ f = fopen(fname, "r"); if (f == NULL) { - printf("Could not open GSM tripler data file '%s'\n", fname); + printf("Could not open GSM triplet data file '%s'\n", fname); return -1; } @@ -312,66 +312,40 @@ } /* IMSI */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid IMSI (%s)\n", - fname, line, pos); + pos2 = NULL; + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) >= sizeof(g->imsi)) { + printf("%s:%d - Invalid IMSI\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) >= sizeof(g->imsi)) { - printf("%s:%d - Too long IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } os_strlcpy(g->imsi, pos, sizeof(g->imsi)); - pos = pos2 + 1; /* Kc */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { + printf("%s:%d - Invalid Kc\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { - printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; /* SRES */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid SRES (%s)\n", fname, line, - pos); + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 8 || + hexstr2bin(pos, g->sres, 4)) { + printf("%s:%d - Invalid SRES\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) { - printf("%s:%d - Invalid SRES (%s)\n", fname, line, - pos); - ret = -1; - break; - } - pos = pos2 + 1; /* RAND */ - pos2 = strchr(pos, ':'); - if (pos2) - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) { - printf("%s:%d - Invalid RAND (%s)\n", fname, line, - pos); + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, g->_rand, 16)) { + printf("%s:%d - Invalid RAND\n", fname, line); ret = -1; break; } - pos = pos2 + 1; g->next = gsm_db; gsm_db = g; @@ -450,86 +424,58 @@ } /* IMSI */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid IMSI (%s)\n", - fname, line, pos); + pos2 = NULL; + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) >= sizeof(m->imsi)) { + printf("%s:%d - Invalid IMSI\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) >= sizeof(m->imsi)) { - printf("%s:%d - Too long IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } os_strlcpy(m->imsi, pos, sizeof(m->imsi)); - pos = pos2 + 1; /* Ki */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, m->ki, 16)) { + printf("%s:%d - Invalid Ki\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) { - printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; /* OPc */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, m->opc, 16)) { + printf("%s:%d - Invalid OPc\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) { - printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; /* AMF */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { + printf("%s:%d - Invalid AMF\n", fname, line); ret = -1; break; } - *pos2 = '\0'; - if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { - printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; /* SQN */ - pos2 = strchr(pos, ' '); - if (pos2) - *pos2 = '\0'; - if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) { - printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos); + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 12 || + hexstr2bin(pos, m->sqn, 6)) { + printf("%s:%d - Invalid SEQ\n", fname, line); ret = -1; break; } - if (pos2) { - pos = pos2 + 1; + pos = str_token(buf, " ", &pos2); + if (pos) { m->res_len = atoi(pos); if (m->res_len && (m->res_len < EAP_AKA_RES_MIN_LEN || m->res_len > EAP_AKA_RES_MAX_LEN)) { - printf("%s:%d - Invalid RES_len (%s)\n", - fname, line, pos); + printf("%s:%d - Invalid RES_len\n", + fname, line); ret = -1; break; } @@ -1027,7 +973,7 @@ { printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " "database/authenticator\n" - "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen \n" + "Copyright (c) 2005-2017, Jouni Malinen \n" "\n" "usage:\n" "hlr_auc_gw [-hu] [-s] [-g] " --- contrib/wpa/hostapd/hostapd.conf.orig +++ contrib/wpa/hostapd/hostapd.conf @@ -3,6 +3,8 @@ # AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for # management frames with the Host AP driver); wlan0 with many nl80211 drivers +# Note: This attribute can be overridden by the values supplied with the '-i' +# command line parameter. interface=wlan0 # In case of atheros and nl80211 driver interfaces, an additional @@ -96,8 +98,25 @@ # Country code (ISO/IEC 3166-1). Used to set regulatory domain. # Set as needed to indicate country in which device is operating. # This can limit available channels and transmit power. +# These two octets are used as the first two octets of the Country String +# (dot11CountryString) #country_code=US +# The third octet of the Country String (dot11CountryString) +# This parameter is used to set the third octet of the country string. +# +# All environments of the current frequency band and country (default) +#country3=0x20 +# Outdoor environment only +#country3=0x4f +# Indoor environment only +#country3=0x49 +# Noncountry entity (country_code=XX) +#country3=0x58 +# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f +# Annex E, Table E-4 (Global operating classes) +#country3=0x04 + # Enable IEEE 802.11d. This advertises the country_code and the set of allowed # channels and transmit power levels based on the regulatory limits. The # country_code setting must be configured with the correct country for @@ -125,11 +144,13 @@ # ieee80211d=1 and local_pwr_constraint configured. #spectrum_mgmt_required=1 -# Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g, -# ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to -# specify band). When using ACS (see channel parameter), a special value "any" -# can be used to indicate that any support band can be used. This special case -# is currently supported only with drivers with which offloaded ACS is used. +# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz), +# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used +# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this +# needs to be set to hw_mode=a. When using ACS (see channel parameter), a +# special value "any" can be used to indicate that any support band can be used. +# This special case is currently supported only with drivers with which +# offloaded ACS is used. # Default: IEEE 802.11b hw_mode=g @@ -173,11 +194,16 @@ # Channel list restriction. This option allows hostapd to select one of the # provided channels when a channel should be automatically selected. # Channel list can be provided as range using hyphen ('-') or individual -# channels can be specified by space (' ') seperated values +# channels can be specified by space (' ') separated values # Default: all channels allowed in selected hw_mode #chanlist=100 104 108 112 116 #chanlist=1 6 11-13 +# Exclude DFS channels from ACS +# This option can be used to exclude all DFS channels from the ACS channel list +# in cases where the driver supports DFS channels. +#acs_exclude_dfs=1 + # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) beacon_int=100 @@ -192,16 +218,16 @@ # (default: 2007) max_num_sta=255 -# RTS/CTS threshold; 2347 = disabled (default); range 0..2347 +# RTS/CTS threshold; -1 = disabled (default); range -1..65535 # If this field is not included in hostapd.conf, hostapd will not control # RTS threshold and 'iwconfig wlan# rts ' can be used to set it. -rts_threshold=2347 +rts_threshold=-1 -# Fragmentation threshold; 2346 = disabled (default); range 256..2346 +# Fragmentation threshold; -1 = disabled (default); range -1, 256..2346 # If this field is not included in hostapd.conf, hostapd will not control # fragmentation threshold and 'iwconfig wlan# frag ' can be used to set # it. -fragm_threshold=2346 +fragm_threshold=-1 # Rate configuration # Default is to enable all rates supported by the hardware. This configuration @@ -223,6 +249,19 @@ #basic_rates=10 20 55 110 #basic_rates=60 120 240 +# Beacon frame TX rate configuration +# This sets the TX rate that is used to transmit Beacon frames. If this item is +# not included, the driver default rate (likely lowest rate) is used. +# Legacy (CCK/OFDM rates): +# beacon_rate= +# HT: +# beacon_rate=ht: +# VHT: +# beacon_rate=vht: +# +# For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM). +#beacon_rate=10 + # Short Preamble # This parameter can be used to enable optional use of short preamble for # frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance. @@ -267,7 +306,14 @@ # requests for broadcast SSID ignore_broadcast_ssid=0 -# Additional vendor specfic elements for Beacon and Probe Response frames +# Do not reply to broadcast Probe Request frames from unassociated STA if there +# is no room for additional stations (max_num_sta). This can be used to +# discourage a STA from trying to associate with this AP if the association +# would be rejected due to maximum STA limit. +# Default: 0 (disabled) +#no_probe_resp_if_max_sta=0 + +# Additional vendor specific elements for Beacon and Probe Response frames # This parameter can be used to add additional vendor specific element(s) into # the end of the Beacon and Probe Response frames. The format for these # element(s) is a hexdump of the raw information elements (id+len+payload for @@ -274,9 +320,16 @@ # one or more elements) #vendor_elements=dd0411223301 +# Additional vendor specific elements for (Re)Association Response frames +# This parameter can be used to add additional vendor specific element(s) into +# the end of the (Re)Association Response frames. The format for these +# element(s) is a hexdump of the raw information elements (id+len+payload for +# one or more elements) +#assocresp_elements=dd0411223301 + # TX queue parameters (EDCF / bursting) # tx_queue__ -# queues: data0, data1, data2, data3, after_beacon, beacon +# queues: data0, data1, data2, data3 # (data0 is the highest priority queue) # parameters: # aifs: AIFS (default 2) @@ -385,6 +438,13 @@ wmm_ac_vo_acm=0 # Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102 +# Enable Multi-AP functionality +# 0 = disabled (default) +# 1 = AP support backhaul BSS +# 2 = AP support fronthaul BSS +# 3 = AP supports both backhaul BSS and fronthaul BSS +#multi_ap=0 + # Static WEP key configuration # # The key number to use when transmitting. @@ -458,6 +518,12 @@ # Beacon and Probe Response frames. #bss_load_update_period=50 +# Channel utilization averaging period (in BUs) +# This field is used to enable and configure channel utilization average +# calculation with bss_load_update_period. This should be in multiples of +# bss_load_update_period for more accurate calculation. +#chan_util_avg_period=600 + # Fixed BSS Load value for testing purposes # This field can be used to configure hostapd to add a fixed BSS Load element # into Beacon and Probe Response frames for testing purposes. The format is @@ -464,6 +530,26 @@ # :: #bss_load_test=12:80:20000 +# Multicast to unicast conversion +# Request that the AP will do multicast-to-unicast conversion for ARP, IPv4, and +# IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent +# to each station separately, with the DA replaced by their own MAC address +# rather than the group address. +# +# Note that this may break certain expectations of the receiver, such as the +# ability to drop unicast IP packets received within multicast L2 frames, or the +# ability to not send ICMP destination unreachable messages for packets received +# in L2 multicast (which is required, but the receiver can't tell the difference +# if this new option is enabled). +# +# This also doesn't implement the 802.11 DMS (directed multicast service). +# +#multicast_to_unicast=0 + +# Send broadcast Deauthentication frame on AP start/stop +# Default: 1 (enabled) +#broadcast_deauth=1 + ##### IEEE 802.11n related configuration ###################################### # ieee80211n: Whether IEEE 802.11n (HT) is enabled @@ -470,6 +556,7 @@ # 0 = disabled (default) # 1 = enabled # Note: You will also need to enable WMM for full HT functionality. +# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band. #ieee80211n=1 # ht_capab: HT capabilities (list of flags) @@ -523,6 +610,7 @@ # 0 = disabled (default) # 1 = enabled # Note: You will also need to enable WMM for full VHT functionality. +# Note: hw_mode=a is used to specify that 5 GHz band is used with VHT. #ieee80211ac=1 # vht_capab: VHT capabilities (list of flags) @@ -605,9 +693,9 @@ # VHT TXOP PS: [VHT-TXOP-PS] # Indicates whether or not the AP supports VHT TXOP Power Save Mode # or whether or not the STA is in VHT TXOP Power Save mode -# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS +# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS # mode -# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save +# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save # mode # # +HTC-VHT Capable: [HTC-VHT] @@ -665,6 +753,78 @@ # #vht_oper_centr_freq_seg1_idx=159 +# Workaround to use station's nsts capability in (Re)Association Response frame +# This may be needed with some deployed devices as an interoperability +# workaround for beamforming if the AP's capability is greater than the +# station's capability. This is disabled by default and can be enabled by +# setting use_sta_nsts=1. +#use_sta_nsts=0 + +##### IEEE 802.11ax related configuration ##################################### + +#ieee80211ax: Whether IEEE 802.11ax (HE) is enabled +# 0 = disabled (default) +# 1 = enabled +#ieee80211ax=1 + +#he_su_beamformer: HE single user beamformer support +# 0 = not supported (default) +# 1 = supported +#he_su_beamformer=1 + +#he_su_beamformee: HE single user beamformee support +# 0 = not supported (default) +# 1 = supported +#he_su_beamformee=1 + +#he_mu_beamformer: HE multiple user beamformer support +# 0 = not supported (default) +# 1 = supported +#he_mu_beamformer=1 + +# he_bss_color: BSS color +# 0 = no BSS color (default) +# unsigned integer = BSS color +#he_bss_color=0 + +#he_default_pe_duration: The duration of PE field in an HE PPDU in us +# Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us +#he_default_pe_duration=0 + +#he_twt_required: Whether TWT is required +# 0 = not required (default) +# 1 = required +#he_twt_required=0 + +#he_rts_threshold: Duration of STA transmission +# 0 = not set (default) +# unsigned integer = duration in units of 16 us +#he_rts_threshold=0 + +#he_mu_edca_qos_info_param_count +#he_mu_edca_qos_info_q_ack +#he_mu_edca_qos_info_queue_request=1 +#he_mu_edca_qos_info_txop_request +#he_mu_edca_ac_be_aifsn=0 +#he_mu_edca_ac_be_ecwmin=15 +#he_mu_edca_ac_be_ecwmax=15 +#he_mu_edca_ac_be_timer=255 +#he_mu_edca_ac_bk_aifsn=0 +#he_mu_edca_ac_bk_aci=1 +#he_mu_edca_ac_bk_ecwmin=15 +#he_mu_edca_ac_bk_ecwmax=15 +#he_mu_edca_ac_bk_timer=255 +#he_mu_edca_ac_vi_ecwmin=15 +#he_mu_edca_ac_vi_ecwmax=15 +#he_mu_edca_ac_vi_aifsn=0 +#he_mu_edca_ac_vi_aci=2 +#he_mu_edca_ac_vi_timer=255 +#he_mu_edca_ac_vo_aifsn=0 +#he_mu_edca_ac_vo_aci=3 +#he_mu_edca_ac_vo_ecwmin=15 +#he_mu_edca_ac_vo_ecwmax=15 +#he_mu_edca_ac_vo_timer=255 + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization @@ -762,12 +922,56 @@ # valid CRL signed by the CA is required to be included in the ca_cert file. # This can be done by using PEM format for CA certificate and CRL and # concatenating these into one file. Whenever CRL changes, hostapd needs to be -# restarted to take the new CRL into use. +# restarted to take the new CRL into use. Alternatively, crl_reload_interval can +# be used to configure periodic updating of the loaded CRL information. # 0 = do not verify CRLs (default) # 1 = check the CRL of the user certificate # 2 = check all CRLs in the certificate path #check_crl=1 +# Specify whether to ignore certificate CRL validity time mismatches with +# errors X509_V_ERR_CERT_HAS_EXPIRED and X509_V_ERR_CERT_NOT_YET_VALID. +# +# 0 = ignore errors +# 1 = do not ignore errors (default) +#check_crl_strict=1 + +# CRL reload interval in seconds +# This can be used to reload ca_cert file and the included CRL on every new TLS +# session if difference between last reload and the current reload time in +# seconds is greater than crl_reload_interval. +# Note: If interval time is very short, CPU overhead may be negatively affected +# and it is advised to not go below 300 seconds. +# This is applicable only with check_crl values 1 and 2. +# 0 = do not reload CRLs (default) +# crl_reload_interval = 300 + +# If check_cert_subject is set, the value of every field will be checked +# against the DN of the subject in the client certificate. If the values do +# not match, the certificate verification will fail, rejecting the user. +# This option allows hostapd to match every individual field in the right order +# against the DN of the subject in the client certificate. +# +# For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will check +# every individual DN field of the subject in the client certificate. If OU=XYZ +# comes first in terms of the order in the client certificate (DN field of +# client certificate C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), hostapd will reject the +# client because the order of 'OU' is not matching the specified string in +# check_cert_subject. +# +# This option also allows '*' as a wildcard. This option has some limitation. +# It can only be used as per the following example. +# +# For example, check_cert_subject=C=US/O=XX/OU=Production* and we have two +# clients and DN of the subject in the first client certificate is +# (C=US/O=XX/OU=Production Unit) and DN of the subject in the second client is +# (C=US/O=XX/OU=Production Factory). In this case, hostapd will allow both +# clients because the value of 'OU' field in both client certificates matches +# 'OU' value in 'check_cert_subject' up to 'wildcard'. +# +# * (Allow all clients, e.g., check_cert_subject=*) +#check_cert_subject=string + # TLS Session Lifetime in seconds # This can be used to allow TLS sessions to be cached and resumed with an # abbreviated handshake when using EAP-TLS/TTLS/PEAP. @@ -774,6 +978,27 @@ # (default: 0 = session caching and resumption disabled) #tls_session_lifetime=3600 +# TLS flags +# [ALLOW-SIGN-RSA-MD5] = allow MD5-based certificate signatures (depending on +# the TLS library, these may be disabled by default to enforce stronger +# security) +# [DISABLE-TIME-CHECKS] = ignore certificate validity time (this requests +# the TLS library to accept certificates even if they are not currently +# valid, i.e., have expired or have not yet become valid; this should be +# used only for testing purposes) +# [DISABLE-TLSv1.0] = disable use of TLSv1.0 +# [ENABLE-TLSv1.0] = explicitly enable use of TLSv1.0 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.1] = disable use of TLSv1.1 +# [ENABLE-TLSv1.1] = explicitly enable use of TLSv1.1 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.2] = disable use of TLSv1.2 +# [ENABLE-TLSv1.2] = explicitly enable use of TLSv1.2 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.3] = disable use of TLSv1.3 +# [ENABLE-TLSv1.3] = enable TLSv1.3 (experimental - disabled by default) +#tls_flags=[flag1][flag2]... + # Cached OCSP stapling response (DER encoded) # If set, this file is sent as a certificate status response by the EAP server # if the EAP peer requests certificate status in the ClientHello message. @@ -788,6 +1013,11 @@ # -respout /tmp/ocsp-cache.der #ocsp_stapling_response=/tmp/ocsp-cache.der +# Cached OCSP stapling response list (DER encoded OCSPResponseList) +# This is similar to ocsp_stapling_response, but the extended version defined in +# RFC 6961 to allow multiple OCSP responses to be provided. +#ocsp_stapling_response_multi=/tmp/ocsp-multi-cache.der + # dh_file: File path to DH/DSA parameters file (in PEM format) # This is an optional configuration file for setting parameters for an # ephemeral DH key exchange. In most cases, the default RSA authentication does @@ -803,12 +1033,26 @@ # OpenSSL cipher string # # This is an OpenSSL specific configuration option for configuring the default -# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default. +# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW" +# by default) is used. # See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation # on cipher suite configuration. This is applicable only if hostapd is built to # use OpenSSL. #openssl_ciphers=DEFAULT:!EXP:!LOW +# OpenSSL ECDH curves +# +# This is an OpenSSL specific configuration option for configuring the ECDH +# curves for EAP-TLS/TTLS/PEAP/FAST server. If not set, automatic curve +# selection is enabled. If set to an empty string, ECDH curve configuration is +# not done (the exact library behavior depends on the library version). +# Otherwise, this is a colon separated list of the supported curves (e.g., +# P-521:P-384:P-256). This is applicable only if hostapd is built to use +# OpenSSL. This must not be used for Suite B cases since the same OpenSSL +# parameter is set differently in those cases and this might conflict with that +# design. +#openssl_ecdh_curves=P-521:P-384:P-256 + # Fragment size for EAP methods #fragment_size=1400 @@ -825,6 +1069,11 @@ #eap_sim_db=unix:/tmp/hlr_auc_gw.sock #eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db +# EAP-SIM DB request timeout +# This parameter sets the maximum time to wait for a database request response. +# The parameter value is in seconds. +#eap_sim_db_timeout=1 + # Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret, # random value. It is configured as a 16-octet value in hex format. It can be # generated, e.g., with the following command: @@ -888,11 +1137,23 @@ # The own IP address of the access point (used as NAS-IP-Address) own_ip_addr=127.0.0.1 -# Optional NAS-Identifier string for RADIUS messages. When used, this should be -# a unique to the NAS within the scope of the RADIUS server. For example, a -# fully qualified domain name can be used here. +# NAS-Identifier string for RADIUS messages. When used, this should be unique +# to the NAS within the scope of the RADIUS server. Please note that hostapd +# uses a separate RADIUS client for each BSS and as such, a unique +# nas_identifier value should be configured separately for each BSS. This is +# particularly important for cases where RADIUS accounting is used +# (Accounting-On/Off messages are interpreted as clearing all ongoing sessions +# and that may get interpreted as applying to all BSSes if the same +# NAS-Identifier value is used.) For example, a fully qualified domain name +# prefixed with a unique identifier of the BSS (e.g., BSSID) can be used here. +# # When using IEEE 802.11r, nas_identifier must be set and must be between 1 and # 48 octets long. +# +# It is mandatory to configure either own_ip_addr or nas_identifier to be +# compliant with the RADIUS protocol. When using RADIUS accounting, it is +# strongly recommended that nas_identifier is set to a unique value for each +# BSS. #nas_identifier=ap.example.com # RADIUS client forced local IP address for the access point @@ -952,11 +1213,24 @@ # Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value # VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can # be used to set static client MAC address to VLAN ID mapping. -# 0 = disabled (default) -# 1 = option; use default interface if RADIUS server does not include VLAN ID +# Dynamic VLAN mode is also used with VLAN ID assignment based on WPA/WPA2 +# passphrase from wpa_psk_file or vlan_id parameter from sae_password. +# 0 = disabled (default); only VLAN IDs from accept_mac_file will be used +# 1 = optional; use default interface if RADIUS server does not include VLAN ID # 2 = required; reject authentication if RADIUS server does not include VLAN ID #dynamic_vlan=0 +# Per-Station AP_VLAN interface mode +# If enabled, each station is assigned its own AP_VLAN interface. +# This implies per-station group keying and ebtables filtering of inter-STA +# traffic (when passed through the AP). +# If the sta is not assigned to any VLAN, then its AP_VLAN interface will be +# added to the bridge given by the "bridge" configuration option (see above). +# Otherwise, it will be added to the per-VLAN bridge. +# 0 = disabled (default) +# 1 = enabled +#per_sta_vif=0 + # VLAN interface list for dynamic VLAN mode is read from a separate text file. # This list is used to map VLAN ID from the RADIUS server to a network # interface. Each station is bound to one interface in the same way as with @@ -965,6 +1239,7 @@ # white space (space or tab). # If no entries are provided by this file, the station is statically mapped # to . interfaces. +# Each line can optionally also contain the name of a bridge to add the VLAN to #vlan_file=/etc/hostapd.vlan # Interface where 802.1q tagged packets should appear when a RADIUS server is @@ -1028,6 +1303,8 @@ #radius_das_port=3799 # # DAS client (the host that can send Disconnect/CoA requests) and shared secret +# Format: +# IP address 0.0.0.0 can be used to allow requests from any address. #radius_das_client=192.168.1.123 shared secret here # # DAS Event-Timestamp time window in seconds @@ -1035,6 +1312,9 @@ # # DAS require Event-Timestamp #radius_das_require_event_timestamp=1 +# +# DAS require Message-Authenticator +#radius_das_require_message_authenticator=1 ##### RADIUS authentication server configuration ############################## @@ -1071,7 +1351,10 @@ # and/or WPA2 (full IEEE 802.11i/RSN): # bit0 = WPA # bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) -#wpa=1 +# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2. +# In other words, for WPA3, wpa=2 is used the configuration (and +# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK). +#wpa=2 # WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase @@ -1100,17 +1383,39 @@ # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be # added to enable SHA256-based stronger algorithms. +# WPA-PSK = WPA-Personal / WPA2-Personal +# WPA-PSK-SHA256 = WPA2-Personal using SHA256 +# WPA-EAP = WPA-Enterprise / WPA2-Enterprise +# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256 +# SAE = SAE (WPA3-Personal) +# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite +# FT-PSK = FT with passphrase/PSK +# FT-EAP = FT with EAP +# FT-EAP-SHA384 = FT with EAP using SHA384 +# FT-SAE = FT with SAE +# FILS-SHA256 = Fast Initial Link Setup with SHA256 +# FILS-SHA384 = Fast Initial Link Setup with SHA384 +# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256 +# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384 +# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open) +# DPP = Device Provisioning Protocol +# OSEN = Hotspot 2.0 online signup with encryption # (dot11RSNAConfigAuthenticationSuitesTable) #wpa_key_mgmt=WPA-PSK WPA-EAP # Set of accepted cipher suites (encryption algorithms) for pairwise keys # (unicast packets). This is a space separated list of algorithms: -# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] -# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# CCMP = AES in Counter mode with CBC-MAC (CCMP-128) +# TKIP = Temporal Key Integrity Protocol +# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key +# GCMP = Galois/counter mode protocol (GCMP-128) +# GCMP-256 = Galois/counter mode protocol with 256-bit key # Group cipher suite (encryption algorithm for broadcast and multicast frames) # is automatically selected based on this configuration. If only CCMP is # allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, -# TKIP will be used as the group cipher. +# TKIP will be used as the group cipher. The optional group_cipher parameter can +# be used to override this automatic selection. +# # (dot11RSNAConfigPairwiseCiphersTable) # Pairwise cipher for WPA (v1) (default: TKIP) #wpa_pairwise=TKIP CCMP @@ -1117,14 +1422,34 @@ # Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) #rsn_pairwise=CCMP +# Optional override for automatic group cipher selection +# This can be used to select a specific group cipher regardless of which +# pairwise ciphers were enabled for WPA and RSN. It should be noted that +# overriding the group cipher with an unexpected value can result in +# interoperability issues and in general, this parameter is mainly used for +# testing purposes. +#group_cipher=CCMP + # Time interval for rekeying GTK (broadcast/multicast encryption keys) in # seconds. (dot11RSNAConfigGroupRekeyTime) -#wpa_group_rekey=600 +# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the +# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the +# group cipher. +#wpa_group_rekey=86400 # Rekey GTK when any STA that possesses the current GTK is leaving the BSS. # (dot11RSNAConfigGroupRekeyStrict) #wpa_strict_rekey=1 +# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is +#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount) +# This value should only be increased when stations are constantly +# deauthenticated during GTK rekeying with the log message +# "group key handshake failed...". +# You should consider to also increase wpa_pairwise_update_count then. +# Range 1..4294967295; default: 4 +#wpa_group_update_count=4 + # Time interval for rekeying GMK (master key used internally to generate GTKs # (in seconds). #wpa_gmk_rekey=86400 @@ -1133,6 +1458,36 @@ # PTK to mitigate some attacks against TKIP deficiencies. #wpa_ptk_rekey=600 +# The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way +# Handshake are retried per 4-Way Handshake attempt. +# (dot11RSNAConfigPairwiseUpdateCount) +# Range 1..4294967295; default: 4 +#wpa_pairwise_update_count=4 + +# Workaround for key reinstallation attacks +# +# This parameter can be used to disable retransmission of EAPOL-Key frames that +# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This +# is similar to setting wpa_group_update_count=1 and +# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with +# extended timeout on the response to avoid causing issues with stations that +# may use aggressive power saving have very long time in replying to the +# EAPOL-Key messages. +# +# This option can be used to work around key reinstallation attacks on the +# station (supplicant) side in cases those station devices cannot be updated +# for some reason. By removing the retransmissions the attacker cannot cause +# key reinstallation with a delayed frame transmission. This is related to the +# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079, +# CVE-2017-13080, and CVE-2017-13081. +# +# This workaround might cause interoperability issues and reduced robustness of +# key negotiation especially in environments with heavy traffic load due to the +# number of attempts to perform the key exchange is reduced significantly. As +# such, this workaround is disabled by default (unless overridden in build +# configuration). To enable this, set the parameter to 1. +#wpa_disable_eapol_key_retries=1 + # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN # authentication and key handshake before actually associating with a new AP. @@ -1148,12 +1503,6 @@ # one. #rsn_preauth_interfaces=eth0 -# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is -# allowed. This is only used with RSN/WPA2. -# 0 = disabled (default) -# 1 = enabled -#peerkey=1 - # ieee80211w: Whether management frame protection (MFP) is enabled # 0 = disabled (default) # 1 = optional @@ -1181,6 +1530,13 @@ # dot11AssociationSAQueryRetryTimeout, 1...4294967295 #assoc_sa_query_retry_timeout=201 +# ocv: Operating Channel Validation +# This is a countermeasure against multi-channel man-in-the-middle attacks. +# Enabling this automatically also enables ieee80211w, if not yet enabled. +# 0 = disabled (default) +# 1 = enabled +#ocv=1 + # disable_pmksa_caching: Disable PMKSA caching # This parameter can be used to disable caching of PMKSA created through EAP # authentication. RSN preauthentication may still end up using PMKSA caching if @@ -1196,20 +1552,134 @@ # 1 = enabled #okc=1 +# SAE password +# This parameter can be used to set passwords for SAE. By default, the +# wpa_passphrase value is used if this separate parameter is not used, but +# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though +# SAE passwords do not have such constraints. If the BSS enabled both SAE and +# WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK +# uses the wpa_passphrase value. +# +# Each sae_password entry is added to a list of available passwords. This +# corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value +# starts with the password (dot11RSNAConfigPasswordCredential). That value can +# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and +# by optional password identifier (dot11RSNAConfigPasswordIdentifier). In +# addition, an optional VLAN ID specification can be used to bind the station +# to the specified VLAN whenver the specific SAE password entry is used. +# +# If the peer MAC address is not included or is set to the wildcard address +# (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a +# specific peer MAC address is included, only a station with that MAC address +# is allowed to use the entry. +# +# If the password identifier (with non-zero length) is included, the entry is +# limited to be used only with that specified identifier. + +# The last matching (based on peer MAC address and identifier) entry is used to +# select which password to use. Setting sae_password to an empty string has a +# special meaning of removing all previously added entries. +# +# sae_password uses the following encoding: +#[|mac=][|vlanid=][|id=] +# Examples: +#sae_password=secret +#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff +#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier +#sae_password=example secret|vlanid=3|id=pw identifier + # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold) # This parameter defines how many open SAE instances can be in progress at the # same time before the anti-clogging mechanism is taken into use. #sae_anti_clogging_threshold=5 +# Maximum number of SAE synchronization errors (dot11RSNASAESync) +# The offending SAe peer will be disconnected if more than this many +# synchronization errors happen. +#sae_sync=5 + # Enabled SAE finite cyclic groups # SAE implementation are required to support group 19 (ECC group defined over a -# 256-bit prime order field). All groups that are supported by the -# implementation are enabled by default. This configuration parameter can be +# 256-bit prime order field). This configuration parameter can be used to +# specify a set of allowed groups. If not included, only the mandatory group 19 +# is enabled. +# The group values are listed in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 +# Note that groups 1, 2, 5, 22, 23, and 24 should not be used in production +# purposes due limited security (see RFC 8247). Groups that are not as strong as +# group 19 (ECC, NIST P-256) are unlikely to be useful for production use cases +# since all implementations are required to support group 19. +#sae_groups=19 20 21 + +# Require MFP for all associations using SAE +# This parameter can be used to enforce negotiation of MFP for all associations +# that negotiate use of SAE. This is used in cases where SAE-capable devices are +# known to be MFP-capable and the BSS is configured with optional MFP +# (ieee80211w=1) for legacy support. The non-SAE stations can connect without +# MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1. +#sae_require_mfp=0 + +# FILS Cache Identifier (16-bit value in hexdump format) +#fils_cache_id=0011 + +# FILS Realm Information +# One or more FILS realms need to be configured when FILS is enabled. This list +# of realms is used to define which realms (used in keyName-NAI by the client) +# can be used with FILS shared key authentication for ERP. +#fils_realm=example.com +#fils_realm=example.org + +# FILS DH Group for PFS +# 0 = PFS disabled with FILS shared key authentication (default) +# 1-65535 DH Group to use for FILS PFS +#fils_dh_group=0 + +# OWE DH groups +# OWE implementations are required to support group 19 (NIST P-256). All groups +# that are supported by the implementation (e.g., groups 19, 20, and 21 when +# using OpenSSL) are enabled by default. This configuration parameter can be # used to specify a limited set of allowed groups. The group values are listed # in the IANA registry: -# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 -#sae_groups=19 20 21 25 26 +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10 +#owe_groups=19 20 21 +# OWE transition mode configuration +# Pointer to the matching open/OWE BSS +#owe_transition_bssid= +# SSID in same format as ssid2 described above. +#owe_transition_ssid= +# Alternatively, OWE transition mode BSSID/SSID can be configured with a +# reference to a BSS operated by this hostapd process. +#owe_transition_ifname= + +# DHCP server for FILS HLP +# If configured, hostapd will act as a DHCP relay for all FILS HLP requests +# that include a DHCPDISCOVER message and send them to the specific DHCP +# server for processing. hostapd will then wait for a response from that server +# before replying with (Re)Association Response frame that encapsulates this +# DHCP response. own_ip_addr is used as the local address for the communication +# with the DHCP server. +#dhcp_server=127.0.0.1 + +# DHCP server UDP port +# Default: 67 +#dhcp_server_port=67 + +# DHCP relay UDP port on the local device +# Default: 67; 0 means not to bind any specific port +#dhcp_relay_port=67 + +# DHCP rapid commit proxy +# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to +# allow the rapid commit options (two message DHCP exchange) to be used with a +# server that supports only the four message DHCP exchange. This is disabled by +# default (= 0) and can be enabled by setting this to 1. +#dhcp_rapid_commit_proxy=0 + +# Wait time for FILS HLP (dot11HLPWaitTime) in TUs +# default: 30 TUs (= 30.72 milliseconds) +#fils_hlp_wait_time=30 + ##### IEEE 802.11r configuration ############################################## # Mobility Domain identifier (dot11FTMobilityDomainID, MDID) @@ -1222,12 +1692,20 @@ # 1 to 48 octet identifier. # This is configured with nas_identifier (see RADIUS client section above). -# Default lifetime of the PMK-RO in minutes; range 1..65535 +# Default lifetime of the PMK-R0 in seconds; range 60..4294967295 +# (default: 14 days / 1209600 seconds; 0 = disable timeout) # (dot11FTR0KeyLifetime) -#r0_key_lifetime=10000 +#ft_r0_key_lifetime=1209600 +# Maximum lifetime for PMK-R1; applied only if not zero +# PMK-R1 is removed at latest after this limit. +# Removing any PMK-R1 for expiry can be disabled by setting this to -1. +# (default: 0) +#r1_max_key_lifetime=0 + # PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID) # 6-octet identifier as a hex string. +# Defaults to BSSID. #r1_key_holder=000102030405 # Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535) @@ -1235,23 +1713,53 @@ #reassociation_deadline=1000 # List of R0KHs in the same Mobility Domain -# format: <128-bit key as hex string> +# format: <256-bit key as hex string> # This list is used to map R0KH-ID (NAS Identifier) to a destination MAC # address when requesting PMK-R1 key from the R0KH that the STA used during the # Initial Mobility Domain Association. -#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f -#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff +#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f +#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff # And so on.. One line per R0KH. +# Wildcard entry: +# Upon receiving a response from R0KH, it will be added to this list, so +# subsequent requests won't be broadcast. If R0KH does not reply, it will be +# blacklisted. +#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff # List of R1KHs in the same Mobility Domain -# format: <128-bit key as hex string> +# format: <256-bit key as hex string> # This list is used to map R1KH-ID to a destination MAC address when sending # PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD # that can request PMK-R1 keys. -#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f -#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff +#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f +#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff # And so on.. One line per R1KH. +# Wildcard entry: +# Upon receiving a request from an R1KH not yet known, it will be added to this +# list and thus will receive push notifications. +#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff +# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above) +# Special values: 0 -> do not expire +# Warning: do not cache implies no sequence number validation with wildcards +#rkh_pos_timeout=86400 (default = 1 day) + +# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request +# and number of retries. +#rkh_pull_timeout=1000 (default = 1 second) +#rkh_pull_retries=4 (default) + +# Timeout (seconds) for non replying R0KH (see wildcard entries above) +# Special values: 0 -> do not cache +# default: 60 seconds +#rkh_neg_timeout=60 + +# Note: The R0KH/R1KH keys used to be 128-bit in length before the message +# format was changed. That shorter key length is still supported for backwards +# compatibility of the configuration files. If such a shorter key is used, a +# 256-bit key is derived from it. For new deployments, configuring the 256-bit +# key is recommended. + # Whether PMK-R1 push is enabled at R0KH # 0 = do not push PMK-R1 to all configured R1KHs (default) # 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived @@ -1262,6 +1770,14 @@ # 1 = FT-over-DS enabled (default) #ft_over_ds=1 +# Whether to generate FT response locally for PSK networks +# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as +# the required information (PSK and other session data) is already locally +# available. +# 0 = disabled (default) +# 1 = enabled +#ft_psk_generate_local=0 + ##### Neighbor table ########################################################## # Maximum number of entries kept in AP table (either for neigbor table or for # detecting Overlapping Legacy BSS Condition). The oldest entry will be @@ -1452,6 +1968,14 @@ # the configuration appropriately in this case. #wps_cred_processing=0 +# Whether to enable SAE (WPA3-Personal transition mode) automatically for +# WPA2-PSK credentials received using WPS. +# 0 = only add the explicitly listed WPA2-PSK configuration (default) +# 1 = add both the WPA2-PSK and SAE configuration and enable PMF so that the +# AP gets configured in WPA3-Personal transition mode (supports both +# WPA2-Personal (PSK) and WPA3-Personal (SAE) clients). +#wps_cred_add_sae=0 + # AP Settings Attributes for M7 # By default, hostapd generates the AP Settings Attributes for M7 based on the # current configuration. It is possible to override this by providing a file @@ -1460,6 +1984,15 @@ # attribute. #ap_settings=hostapd.ap_settings +# Multi-AP backhaul BSS config +# Used in WPS when multi_ap=2 or 3. Defines "backhaul BSS" credentials. +# These are passed in WPS M8 instead of the normal (fronthaul) credentials +# if the Enrollee has the Multi-AP subelement set. Backhaul SSID is formatted +# like ssid2. The key is set like wpa_psk or wpa_passphrase. +#multi_ap_backhaul_ssid="backhaul" +#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#multi_ap_backhaul_wpa_passphrase=secret passphrase + # WPS UPnP interface # If set, support for external Registrars is enabled. #upnp_iface=br0 @@ -1532,6 +2065,18 @@ # 1 = enabled (allow stations to use WNM-Sleep Mode) #wnm_sleep_mode=1 +# WNM-Sleep Mode GTK/IGTK workaround +# Normally, WNM-Sleep Mode exit with management frame protection negotiated +# would result in the current GTK/IGTK getting added into the WNM-Sleep Mode +# Response frame. Some station implementations may have a vulnerability that +# results in GTK/IGTK reinstallation based on this frame being replayed. This +# configuration parameter can be used to disable that behavior and use EAPOL-Key +# frames for GTK/IGTK update instead. This would likely be only used with +# wpa_disable_eapol_key_retries=1 that enables a workaround for similar issues +# with EAPOL-Key. This is related to station side vulnerabilities CVE-2017-13087 +# and CVE-2017-13088. To enable this AP-side workaround, set the parameter to 1. +#wnm_sleep_mode_no_keys=0 + # BSS Transition Management # 0 = disabled (default) # 1 = enabled @@ -1619,6 +2164,15 @@ # (double quoted string, printf-escaped string) #venue_name=P"eng:Example\nvenue" +# Venue URL information +# This parameter can be used to configure one or more Venue URL Duples to +# provide additional information corresponding to Venue Name information. +# Each entry has a Venue Number value separated by colon from the Venue URL +# string. Venue Number indicates the corresponding venue_name entry (1 = 1st +# venue_name, 2 = 2nd venue_name, and so on; 0 = no matching venue_name) +#venue_url=1:http://www.example.com/info-eng +#venue_url=2:http://www.example.com/info-fin + # Network Authentication Type # This parameter indicates what type of network authentication is used in the # network. @@ -1684,6 +2238,24 @@ # username/password #nai_realm=0,example.org,13[5:6],21[2:4][5:7] +# Arbitrary ANQP-element configuration +# Additional ANQP-elements with arbitrary values can be defined by specifying +# their contents in raw format as a hexdump of the payload. Note that these +# values will override ANQP-element contents that may have been specified in the +# more higher layer configuration parameters listed above. +# format: anqp_elem=: +# For example, AP Geospatial Location ANQP-element with unknown location: +#anqp_elem=265:0000 +# For example, AP Civic Location ANQP-element with unknown location: +#anqp_elem=266:000000 + +# GAS Address 3 behavior +# 0 = P2P specification (Address3 = AP BSSID) workaround enabled by default +# based on GAS request Address3 +# 1 = IEEE 802.11 standard compliant regardless of GAS request Address3 +# 2 = Force non-compliant behavior (Address3 = AP BSSID for all cases) +#gas_address3=0 + # QoS Map Set configuration # # Comma delimited QoS Map Set in decimal values @@ -1771,7 +2343,27 @@ # channels 36-48): #hs20_operating_class=5173 -# OSU icons +# Terms and Conditions information +# +# hs20_t_c_filename contains the Terms and Conditions filename that the AP +# indicates in RADIUS Access-Request messages. +#hs20_t_c_filename=terms-and-conditions +# +# hs20_t_c_timestamp contains the Terms and Conditions timestamp that the AP +# indicates in RADIUS Access-Request messages. Usually, this contains the number +# of seconds since January 1, 1970 00:00 UTC showing the time when the file was +# last modified. +#hs20_t_c_timestamp=1234567 +# +# hs20_t_c_server_url contains a template for the Terms and Conditions server +# URL. This template is used to generate the URL for a STA that needs to +# acknowledge Terms and Conditions. Unlike the other hs20_t_c_* parameters, this +# parameter is used on the authentication server, not the AP. +# Macros: +# @1@ = MAC address of the STA (colon separated hex octets) +#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123 + +# OSU and Operator icons # ::::: #hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png #hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png @@ -1783,12 +2375,15 @@ # OSU Providers # One or more sets of following parameter. Each OSU provider is started by the # mandatory osu_server_uri item. The other parameters add information for the -# last added OSU provider. +# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN +# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI +# value for OSEN authentication when using a shared BSS (Single SSID) for OSU. # #osu_server_uri=https://example.com/osu/ #osu_friendly_name=eng:Example operator #osu_friendly_name=fin:Esimerkkipalveluntarjoaja #osu_nai=anonymous@example.com +#osu_nai2=anonymous@example.com #osu_method_list=1 0 #osu_icon=icon32 #osu_icon=icon64 @@ -1797,6 +2392,50 @@ # #osu_server_uri=... +# Operator Icons +# Operator icons are specified using references to the hs20_icon entries +# (Name subfield). This information, if present, is advertsised in the +# Operator Icon Metadata ANQO-element. +#operator_icon=icon32 +#operator_icon=icon64 + +##### Multiband Operation (MBO) ############################################### +# +# MBO enabled +# 0 = disabled (default) +# 1 = enabled +#mbo=1 +# +# Cellular data connection preference +# 0 = Excluded - AP does not want STA to use the cellular data connection +# 1 = AP prefers the STA not to use cellular data connection +# 255 = AP prefers the STA to use cellular data connection +#mbo_cell_data_conn_pref=1 + +##### Optimized Connectivity Experience (OCE) ################################# +# +# Enable OCE specific features (bitmap) +# BIT(0) - Reserved +# Set BIT(1) (= 2) to enable OCE in STA-CFON mode +# Set BIT(2) (= 4) to enable OCE in AP mode +# Default is 0 = OCE disabled +#oce=0 + +# RSSI-based assocition rejection +# +# Reject STA association if RSSI is below given threshold (in dBm) +# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled) +# Note: This rejection happens based on a signal strength detected while +# receiving a single frame and as such, there is significant risk of the value +# not being accurate and this resulting in valid stations being rejected. As +# such, this functionality is not recommended to be used for purposes other than +# testing. +#rssi_reject_assoc_rssi=-75 +# +# Association retry delay in seconds allowed by the STA if RSSI has not met the +# threshold (range: 0..255, default=30). +#rssi_reject_assoc_timeout=30 + ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration @@ -1823,6 +2462,36 @@ # Transitioning between states). #fst_llt=100 +##### Radio measurements / location ########################################### + +# The content of a LCI measurement subelement +#lci= + +# The content of a location civic measurement subelement +#civic= + +# Enable neighbor report via radio measurements +#rrm_neighbor_report=1 + +# Enable beacon report via radio measurements +#rrm_beacon_report=1 + +# Publish fine timing measurement (FTM) responder functionality +# This parameter only controls publishing via Extended Capabilities element. +# Actual functionality is managed outside hostapd. +#ftm_responder=0 + +# Publish fine timing measurement (FTM) initiator functionality +# This parameter only controls publishing via Extended Capabilities element. +# Actual functionality is managed outside hostapd. +#ftm_initiator=0 +# +# Stationary AP config indicates that the AP doesn't move hence location data +# can be considered as always up to date. If configured, LCI data will be sent +# as a radio measurement even if the request doesn't contain a max age element +# that allows sending of such data. Default: 0. +#stationary_ap=0 + ##### TESTING OPTIONS ######################################################### # # The options in this section are only available when the build configuration @@ -1844,6 +2513,10 @@ # # Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability #corrupt_gtk_rekey_mic_probability=0.0 +# +# Include only ECSA IE without CSA IE where possible +# (channel switch operating class is needed) +#ecsa_ie_only=0 ##### Multiple BSSID support ################################################## # @@ -1866,6 +2539,10 @@ # - is not the same as the MAC address of the radio # - is not the same as any other explicitly specified BSSID # +# Alternatively, the 'use_driver_iface_addr' parameter can be used to request +# hostapd to use the driver auto-generated interface address (e.g., to use the +# exact MAC addresses allocated to the device). +# # Not all drivers support multiple BSSes. The exact mechanism for determining # the driver capabilities is driver specific. With the current (i.e., a recent # kernel) drivers using nl80211, this information can be checked with "iw list" --- contrib/wpa/hostapd/hostapd.eap_user_sqlite.orig +++ contrib/wpa/hostapd/hostapd.eap_user_sqlite @@ -3,7 +3,8 @@ methods TEXT, password TEXT, remediation TEXT, - phase2 INTEGER + phase2 INTEGER, + t_c_timestamp INTEGER ); CREATE TABLE wildcards( @@ -24,3 +25,18 @@ username TEXT, note TEXT ); + +CREATE TABLE pending_tc( + mac_addr TEXT PRIMARY KEY, + identity TEXT +); + +CREATE TABLE current_sessions( + mac_addr TEXT PRIMARY KEY, + identity TEXT, + start_time TEXT, + nas TEXT, + hs20_t_c_filtering BOOLEAN, + waiting_coa_ack BOOLEAN, + coa_ack_received BOOLEAN +); --- contrib/wpa/hostapd/hostapd.wpa_psk.orig +++ contrib/wpa/hostapd/hostapd.wpa_psk @@ -3,7 +3,13 @@ # Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that # anyone can use. PSK can be configured as an ASCII passphrase of 8..63 # characters or as a 256-bit hex PSK (64 hex digits). +# An optional key identifier can be added by prefixing the line with +# keyid= +# An optional VLAN ID can be specified by prefixing the line with +# vlanid=. 00:00:00:00:00:00 secret passphrase 00:11:22:33:44:55 another passphrase 00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +keyid=example_id 00:11:22:33:44:77 passphrase with keyid +vlanid=3 00:00:00:00:00:00 passphrase with vlanid 00:00:00:00:00:00 another passphrase for all STAs --- contrib/wpa/hostapd/hostapd_cli.c.orig +++ contrib/wpa/hostapd/hostapd_cli.c @@ -1,6 +1,6 @@ /* * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,80 +15,14 @@ #include "utils/eloop.h" #include "utils/edit.h" #include "common/version.h" +#include "common/cli.h" +#ifndef CONFIG_NO_CTRL_IFACE static const char *const hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2015, Jouni Malinen and contributors"; +"Copyright (c) 2004-2019, Jouni Malinen and contributors"; - -static const char *const hostapd_cli_license = -"This software may be distributed under the terms of the BSD license.\n" -"See README for more details.\n"; - -static const char *const hostapd_cli_full_license = -"This software may be distributed under the terms of the BSD license.\n" -"\n" -"Redistribution and use in source and binary forms, with or without\n" -"modification, are permitted provided that the following conditions are\n" -"met:\n" -"\n" -"1. Redistributions of source code must retain the above copyright\n" -" notice, this list of conditions and the following disclaimer.\n" -"\n" -"2. Redistributions in binary form must reproduce the above copyright\n" -" notice, this list of conditions and the following disclaimer in the\n" -" documentation and/or other materials provided with the distribution.\n" -"\n" -"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" -" names of its contributors may be used to endorse or promote products\n" -" derived from this software without specific prior written permission.\n" -"\n" -"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" -"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" -"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" -"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" -"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" -"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" -"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" -"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" -"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" -"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" -"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" -"\n"; - -static const char *const commands_help = -"Commands:\n" -" mib get MIB variables (dot1x, dot11, radius)\n" -" sta get MIB variables for one station\n" -" all_sta get MIB variables for all stations\n" -" new_sta add a new station\n" -" deauthenticate deauthenticate a station\n" -" disassociate disassociate a station\n" -#ifdef CONFIG_IEEE80211W -" sa_query send SA Query to a station\n" -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WPS -" wps_pin [timeout] [addr] add WPS Enrollee PIN\n" -" wps_check_pin verify PIN checksum\n" -" wps_pbc indicate button pushed to initiate PBC\n" -" wps_cancel cancel the pending WPS operation\n" -#ifdef CONFIG_WPS_NFC -" wps_nfc_tag_read report read NFC tag with WPS data\n" -" wps_nfc_config_token build NFC configuration token\n" -" wps_nfc_token manager NFC password token\n" -#endif /* CONFIG_WPS_NFC */ -" wps_ap_pin [params..] enable/disable AP PIN\n" -" wps_config configure AP\n" -" wps_get_status show current WPS status\n" -#endif /* CONFIG_WPS */ -" get_config show current configuration\n" -" help show this usage help\n" -" interface [ifname] show interfaces/select interface\n" -" level change debug level\n" -" license show full hostapd_cli license\n" -" quit exit hostapd_cli\n"; - static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; static int hostapd_cli_attached = 0; @@ -104,8 +38,17 @@ static const char *action_file = NULL; static int ping_interval = 5; static int interactive = 0; +static int event_handler_registered = 0; +static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */ +static void print_help(FILE *stream, const char *cmd); +static char ** list_cmd_list(void); +static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx); +static void update_stations(struct wpa_ctrl *ctrl); +static void cli_event(const char *str); + + static void usage(void) { fprintf(stderr, "%s\n", hostapd_cli_version); @@ -128,20 +71,49 @@ " -B run a daemon in the background\n" " -i Interface to listen on (default: first " "interface found in the\n" - " socket path)\n\n" - "%s", - commands_help); + " socket path)\n\n"); + print_help(stderr, NULL); } +static void register_event_handler(struct wpa_ctrl *ctrl) +{ + if (!ctrl_conn) + return; + if (interactive) { + event_handler_registered = + !eloop_register_read_sock(wpa_ctrl_get_fd(ctrl), + hostapd_cli_receive, + NULL, NULL); + } +} + + +static void unregister_event_handler(struct wpa_ctrl *ctrl) +{ + if (!ctrl_conn) + return; + if (interactive && event_handler_registered) { + eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl)); + event_handler_registered = 0; + } +} + + static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) { +#ifndef CONFIG_CTRL_IFACE_UDP char *cfile; int flen; +#endif /* !CONFIG_CTRL_IFACE_UDP */ if (ifname == NULL) return NULL; +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl_conn = wpa_ctrl_open(ifname); + return ctrl_conn; +#else /* CONFIG_CTRL_IFACE_UDP */ flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; cfile = malloc(flen); if (cfile == NULL) @@ -158,6 +130,7 @@ ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); free(cfile); return ctrl_conn; +#endif /* CONFIG_CTRL_IFACE_UDP */ } @@ -166,6 +139,7 @@ if (ctrl_conn == NULL) return; + unregister_event_handler(ctrl_conn); if (hostapd_cli_attached) { wpa_ctrl_detach(ctrl_conn); hostapd_cli_attached = 0; @@ -175,13 +149,45 @@ } +static int hostapd_cli_reconnect(const char *ifname) +{ + char *next_ctrl_ifname; + + hostapd_cli_close_connection(); + + if (!ifname) + return -1; + + next_ctrl_ifname = os_strdup(ifname); + os_free(ctrl_ifname); + ctrl_ifname = next_ctrl_ifname; + if (!ctrl_ifname) + return -1; + + ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); + if (!ctrl_conn) + return -1; + if (!interactive && !action_file) + return 0; + if (wpa_ctrl_attach(ctrl_conn) == 0) { + hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); + update_stations(ctrl_conn); + } else { + printf("Warning: Failed to attach to hostapd.\n"); + } + return 0; +} + + static void hostapd_cli_msg_cb(char *msg, size_t len) { + cli_event(msg); printf("%s\n", msg); } -static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print) { char buf[4096]; size_t len; @@ -209,12 +215,28 @@ } -static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) +static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd) { return _wpa_ctrl_command(ctrl, cmd, 1); } +static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, + int min_args, int argc, char *argv[]) +{ + char buf[4096]; + + if (argc < min_args) { + printf("Invalid %s command - at least %d argument%s required.\n", + cmd, min_args, min_args > 1 ? "s are" : " is"); + return -1; + } + if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "PING"); @@ -298,6 +320,21 @@ } +static char ** hostapd_complete_stations(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -348,6 +385,22 @@ } +#ifdef CONFIG_TAXONOMY +static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + + if (argc != 1) { + printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n"); + return -1; + } + os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} +#endif /* CONFIG_TAXONOMY */ + + #ifdef CONFIG_IEEE80211W static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -667,8 +720,8 @@ } -static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, - char *addr, size_t addr_len) +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd, + char *addr, size_t addr_len, int print) { char buf[4096], *pos; size_t len; @@ -692,7 +745,8 @@ buf[len] = '\0'; if (memcmp(buf, "FAIL", 4) == 0) return -1; - printf("%s", buf); + if (print) + printf("%s", buf); pos = buf; while (*pos != '\0' && *pos != '\n') @@ -708,27 +762,59 @@ { char addr[32], cmd[64]; - if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1)) return 0; do { snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); - } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0); return -1; } +static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char addr[32], cmd[64]; + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) + return 0; + do { + if (os_strcmp(addr, "") != 0) + printf("%s\n", addr); + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); + + return 0; +} + + static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - printf("%s", commands_help); + print_help(stdout, argc > 0 ? argv[0] : NULL); return 0; } +static char ** hostapd_cli_complete_help(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = list_cmd_list(); + break; + } + + return res; +} + + static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license); + printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license); return 0; } @@ -839,6 +925,47 @@ } +static void update_stations(struct wpa_ctrl *ctrl) +{ + char addr[32], cmd[64]; + + if (!ctrl || !interactive) + return; + + cli_txt_list_flush(&stations); + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) + return; + do { + if (os_strcmp(addr, "") != 0) + cli_txt_list_add(&stations, addr); + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); +} + + +static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl, + struct dl_list *interfaces) +{ + struct dirent *dent; + DIR *dir; + + if (!ctrl || !interfaces) + return; + dir = opendir(ctrl_iface_dir); + if (dir == NULL) + return; + + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + cli_txt_list_add(interfaces, dent->d_name); + } + closedir(dir); +} + + static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) { struct dirent *dent; @@ -869,22 +996,7 @@ hostapd_cli_list_interfaces(ctrl); return 0; } - - hostapd_cli_close_connection(); - os_free(ctrl_ifname); - ctrl_ifname = os_strdup(argv[0]); - if (ctrl_ifname == NULL) - return -1; - - if (hostapd_cli_open_connection(ctrl_ifname)) { - printf("Connected to interface '%s.\n", ctrl_ifname); - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - } else { - printf("Warning: Failed to attach to " - "hostapd.\n"); - } - } else { + if (hostapd_cli_reconnect(argv[0]) != 0) { printf("Could not connect to interface '%s' - re-trying\n", ctrl_ifname); } @@ -892,9 +1004,27 @@ } +static char ** hostapd_complete_interface(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + DEFINE_DL_LIST(interfaces); + + switch (arg) { + case 1: + hostapd_cli_get_interfaces(ctrl_conn, &interfaces); + res = cli_txt_list_array(&interfaces); + cli_txt_list_flush(&interfaces); + break; + } + + return res; +} + + static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; + char cmd[2048]; int res; if (argc != 2) { @@ -912,6 +1042,44 @@ } +static char ** hostapd_complete_set(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { +#ifdef CONFIG_WPS_TESTING + "wps_version_number", "wps_testing_dummy_cred", + "wps_corrupt_pkhash", +#endif /* CONFIG_WPS_TESTING */ +#ifdef CONFIG_INTERWORKING + "gas_frag_limit", +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_TESTING_OPTIONS + "ext_mgmt_frame_handling", "ext_eapol_frame_io", +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_MBO + "mbo_assoc_disallow", +#endif /* CONFIG_MBO */ + "deny_mac_file", "accept_mac_file", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res; + + res = os_calloc(num_fields + 1, sizeof(char *)); + if (!res) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (!res[i]) + return res; + } + return res; + } + return NULL; +} + + static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -932,6 +1100,31 @@ } +static char ** hostapd_complete_get(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + "version", "tls_library", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res; + + res = os_calloc(num_fields + 1, sizeof(char *)); + if (!res) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (!res[i]) + return res; + } + return res; + } + return NULL; +} + + #ifdef CONFIG_FST static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1068,68 +1261,460 @@ } +static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) + return -1; + return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]); +} + + +static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA"); +} + + +static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA_FLUSH"); +} + + +static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[2048]; + int res; + + if (argc < 3 || argc > 6) { + printf("Invalid set_neighbor command: needs 3-6 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s", + argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "", + argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long SET_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[400]; + int res; + + if (argc != 2) { + printf("Invalid remove_neighbor command: needs 2 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REMOVE_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid req_lci command - requires destination address\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REQ_LCI command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc < 4) { + printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n"); + return -1; + } + + return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv); +} + + +static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DRIVER_FLAGS"); +} + + +#ifdef CONFIG_DPP + +static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN"); +} + + +static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv); +} + +#endif /* CONFIG_DPP */ + + +static int hostapd_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv); +} + + +static int hostapd_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv); +} + + +static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "POLL_STA", 1, argc, argv); +} + + +static int hostapd_cli_cmd_req_beacon(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "REQ_BEACON", 2, argc, argv); +} + + +static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD_WPA_PSK"); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); + char ** (*completion)(const char *str, int pos); + const char *usage; }; static const struct hostapd_cli_cmd hostapd_cli_commands[] = { - { "ping", hostapd_cli_cmd_ping }, - { "mib", hostapd_cli_cmd_mib }, - { "relog", hostapd_cli_cmd_relog }, - { "status", hostapd_cli_cmd_status }, - { "sta", hostapd_cli_cmd_sta }, - { "all_sta", hostapd_cli_cmd_all_sta }, - { "new_sta", hostapd_cli_cmd_new_sta }, - { "deauthenticate", hostapd_cli_cmd_deauthenticate }, - { "disassociate", hostapd_cli_cmd_disassociate }, + { "ping", hostapd_cli_cmd_ping, NULL, + "= pings hostapd" }, + { "mib", hostapd_cli_cmd_mib, NULL, + "= get MIB variables (dot1x, dot11, radius)" }, + { "relog", hostapd_cli_cmd_relog, NULL, + "= reload/truncate debug log output file" }, + { "status", hostapd_cli_cmd_status, NULL, + "= show interface status info" }, + { "sta", hostapd_cli_cmd_sta, hostapd_complete_stations, + " = get MIB variables for one station" }, + { "all_sta", hostapd_cli_cmd_all_sta, NULL, + "= get MIB variables for all stations" }, + { "list_sta", hostapd_cli_cmd_list_sta, NULL, + "= list all stations" }, + { "new_sta", hostapd_cli_cmd_new_sta, NULL, + " = add a new station" }, + { "deauthenticate", hostapd_cli_cmd_deauthenticate, + hostapd_complete_stations, + " = deauthenticate a station" }, + { "disassociate", hostapd_cli_cmd_disassociate, + hostapd_complete_stations, + " = disassociate a station" }, +#ifdef CONFIG_TAXONOMY + { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations, + " = get taxonomy signature for a station" }, +#endif /* CONFIG_TAXONOMY */ #ifdef CONFIG_IEEE80211W - { "sa_query", hostapd_cli_cmd_sa_query }, + { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations, + " = send SA Query to a station" }, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS - { "wps_pin", hostapd_cli_cmd_wps_pin }, - { "wps_check_pin", hostapd_cli_cmd_wps_check_pin }, - { "wps_pbc", hostapd_cli_cmd_wps_pbc }, - { "wps_cancel", hostapd_cli_cmd_wps_cancel }, + { "wps_pin", hostapd_cli_cmd_wps_pin, NULL, + " [timeout] [addr] = add WPS Enrollee PIN" }, + { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL, + " = verify PIN checksum" }, + { "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL, + "= indicate button pushed to initiate PBC" }, + { "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL, + "= cancel the pending WPS operation" }, #ifdef CONFIG_WPS_NFC - { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read }, - { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token }, - { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token }, - { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel }, + { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL, + " = report read NFC tag with WPS data" }, + { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL, + " = build NFC configuration token" }, + { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL, + " = manager NFC password token" }, + { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL, + NULL }, #endif /* CONFIG_WPS_NFC */ - { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin }, - { "wps_config", hostapd_cli_cmd_wps_config }, - { "wps_get_status", hostapd_cli_cmd_wps_get_status }, + { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL, + " [params..] = enable/disable AP PIN" }, + { "wps_config", hostapd_cli_cmd_wps_config, NULL, + " = configure AP" }, + { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL, + "= show current WPS status" }, #endif /* CONFIG_WPS */ - { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent }, - { "ess_disassoc", hostapd_cli_cmd_ess_disassoc }, - { "bss_tm_req", hostapd_cli_cmd_bss_tm_req }, - { "get_config", hostapd_cli_cmd_get_config }, - { "help", hostapd_cli_cmd_help }, - { "interface", hostapd_cli_cmd_interface }, + { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, + "= send Disassociation Imminent notification" }, + { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, + "= send ESS Dissassociation Imminent notification" }, + { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, + "= send BSS Transition Management Request" }, + { "get_config", hostapd_cli_cmd_get_config, NULL, + "= show current configuration" }, + { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help, + "= show this usage help" }, + { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface, + "[ifname] = show interfaces/select interface" }, #ifdef CONFIG_FST - { "fst", hostapd_cli_cmd_fst }, + { "fst", hostapd_cli_cmd_fst, NULL, + " = send FST-MANAGER control interface command" }, #endif /* CONFIG_FST */ - { "level", hostapd_cli_cmd_level }, - { "license", hostapd_cli_cmd_license }, - { "quit", hostapd_cli_cmd_quit }, - { "set", hostapd_cli_cmd_set }, - { "get", hostapd_cli_cmd_get }, - { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set }, - { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf }, - { "chan_switch", hostapd_cli_cmd_chan_switch }, - { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif }, - { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req }, - { "vendor", hostapd_cli_cmd_vendor }, - { "enable", hostapd_cli_cmd_enable }, - { "reload", hostapd_cli_cmd_reload }, - { "disable", hostapd_cli_cmd_disable }, - { "erp_flush", hostapd_cli_cmd_erp_flush }, - { "log_level", hostapd_cli_cmd_log_level }, - { NULL, NULL } + { "raw", hostapd_cli_cmd_raw, NULL, + " = send unprocessed command" }, + { "level", hostapd_cli_cmd_level, NULL, + " = change debug level" }, + { "license", hostapd_cli_cmd_license, NULL, + "= show full hostapd_cli license" }, + { "quit", hostapd_cli_cmd_quit, NULL, + "= exit hostapd_cli" }, + { "set", hostapd_cli_cmd_set, hostapd_complete_set, + " = set runtime variables" }, + { "get", hostapd_cli_cmd_get, hostapd_complete_get, + " = get runtime info" }, + { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, + " = set QoS Map set element" }, + { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, + hostapd_complete_stations, + " = send QoS Map Configure frame" }, + { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, + " [sec_channel_offset=] [center_freq1=]\n" + " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n" + " = initiate channel switch announcement" }, + { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, + " \n" + " = send WNM-Notification Subscription Remediation Request" }, + { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, + " [url]\n" + " = send WNM-Notification imminent deauthentication indication" }, + { "vendor", hostapd_cli_cmd_vendor, NULL, + " []\n" + " = send vendor driver command" }, + { "enable", hostapd_cli_cmd_enable, NULL, + "= enable hostapd on current interface" }, + { "reload", hostapd_cli_cmd_reload, NULL, + "= reload configuration for current interface" }, + { "disable", hostapd_cli_cmd_disable, NULL, + "= disable hostapd on current interface" }, + { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, + "= drop all ERP keys"}, + { "log_level", hostapd_cli_cmd_log_level, NULL, + "[level] = show/change log verbosity level" }, + { "pmksa", hostapd_cli_cmd_pmksa, NULL, + " = show PMKSA cache entries" }, + { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, + " = flush PMKSA cache" }, + { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, + " [lci=] [civic=] [stat]\n" + " = add AP to neighbor database" }, + { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL, + " = remove AP from neighbor database" }, + { "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations, + " = send LCI request to a station"}, + { "req_range", hostapd_cli_cmd_req_range, NULL, + " = send FTM range request"}, + { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, + " = show supported driver flags"}, +#ifdef CONFIG_DPP + { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL, + "report a scanned DPP URI from a QR Code" }, + { "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL, + "type= [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" }, + { "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL, + "*| = remove DPP bootstrap information" }, + { "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL, + " = get DPP bootstrap URI" }, + { "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL, + " = show DPP bootstrap information" }, + { "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL, + "peer= [own=] = initiate DPP bootstrapping" }, + { "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL, + " = start DPP listen" }, + { "dpp_stop_listen", hostapd_cli_cmd_dpp_stop_listen, NULL, + "= stop DPP listen" }, + { "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL, + "[curve=..] [key=..] = add DPP configurator" }, + { "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove, + NULL, + "*| = remove DPP configurator" }, + { "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key, + NULL, + " = Get DPP configurator's private key" }, + { "dpp_configurator_sign", hostapd_cli_cmd_dpp_configurator_sign, NULL, + "conf= configurator= = generate self DPP configuration" }, + { "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL, + "add PKEX code" }, + { "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL, + "*| = remove DPP pkex information" }, +#endif /* CONFIG_DPP */ + { "accept_acl", hostapd_cli_cmd_accept_macacl, NULL, + "=Add/Delete/Show/Clear accept MAC ACL" }, + { "deny_acl", hostapd_cli_cmd_deny_macacl, NULL, + "=Add/Delete/Show/Clear deny MAC ACL" }, + { "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations, + " = poll a STA to check connectivity with a QoS null frame" }, + { "req_beacon", hostapd_cli_cmd_req_beacon, NULL, + " [req_mode=] = send a Beacon report request to a station" }, + { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL, + "= reload wpa_psk_file only" }, + { NULL, NULL, NULL, NULL } }; +/* + * Prints command usage, lines are padded with the specified string. + */ +static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd, + const char *pad) +{ + char c; + size_t n; + + if (cmd->usage == NULL) + return; + fprintf(stream, "%s%s ", pad, cmd->cmd); + for (n = 0; (c = cmd->usage[n]); n++) { + fprintf(stream, "%c", c); + if (c == '\n') + fprintf(stream, "%s", pad); + } + fprintf(stream, "\n"); +} + + +static void print_help(FILE *stream, const char *cmd) +{ + int n; + + fprintf(stream, "commands:\n"); + for (n = 0; hostapd_cli_commands[n].cmd; n++) { + if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd)) + print_cmd_help(stream, &hostapd_cli_commands[n], " "); + } +} + + static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { const struct hostapd_cli_cmd *cmd, *match = NULL; @@ -1169,6 +1754,34 @@ } +static void cli_event(const char *str) +{ + const char *start, *s; + + start = os_strchr(str, '>'); + if (start == NULL) + return; + + start++; + + if (str_starts(start, AP_STA_CONNECTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_add(&stations, s + 1); + return; + } + + if (str_starts(start, AP_STA_DISCONNECTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_del_addr(&stations, s + 1); + return; + } +} + + static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, int action_monitor) { @@ -1176,7 +1789,7 @@ if (ctrl_conn == NULL) return; while (wpa_ctrl_pending(ctrl)) { - char buf[256]; + char buf[4096]; size_t len = sizeof(buf) - 1; if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { buf[len] = '\0'; @@ -1183,6 +1796,7 @@ if (action_monitor) hostapd_cli_action_process(buf, len); else { + cli_event(buf); if (in_read && first) printf("\n"); first = 0; @@ -1196,35 +1810,9 @@ } -#define max_args 10 - -static int tokenize_cmd(char *cmd, char *argv[]) +static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx) { - char *pos; - int argc = 0; - - pos = cmd; - for (;;) { - while (*pos == ' ') - pos++; - if (*pos == '\0') - break; - argv[argc] = pos; - argc++; - if (argc == max_args) - break; - if (*pos == '"') { - char *pos2 = os_strrchr(pos, '"'); - if (pos2) - pos = pos2 + 1; - } - while (*pos != '\0' && *pos != ' ') - pos++; - if (*pos == ' ') - *pos++ = '\0'; - } - - return argc; + hostapd_cli_recv_pending(ctrl_conn, 0, 0); } @@ -1234,18 +1822,8 @@ printf("Connection to hostapd lost - trying to reconnect\n"); hostapd_cli_close_connection(); } - if (!ctrl_conn) { - ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); - if (ctrl_conn) { - printf("Connection to hostapd re-established\n"); - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - } else { - printf("Warning: Failed to attach to " - "hostapd.\n"); - } - } - } + if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0) + printf("Connection to hostapd re-established\n"); if (ctrl_conn) hostapd_cli_recv_pending(ctrl_conn, 1, 0); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); @@ -1274,18 +1852,100 @@ } +static char ** list_cmd_list(void) +{ + char **res; + int i, count; + + count = ARRAY_SIZE(hostapd_cli_commands); + res = os_calloc(count + 1, sizeof(char *)); + if (res == NULL) + return NULL; + + for (i = 0; hostapd_cli_commands[i].cmd; i++) { + res[i] = os_strdup(hostapd_cli_commands[i].cmd); + if (res[i] == NULL) + break; + } + + return res; +} + + +static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str, + int pos) +{ + int i; + + for (i = 0; hostapd_cli_commands[i].cmd; i++) { + if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0) + continue; + if (hostapd_cli_commands[i].completion) + return hostapd_cli_commands[i].completion(str, pos); + if (!hostapd_cli_commands[i].usage) + return NULL; + edit_clear_line(); + printf("\r%s\n", hostapd_cli_commands[i].usage); + edit_redraw(); + break; + } + + return NULL; +} + + +static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str, + int pos) +{ + char **res; + const char *end; + char *cmd; + + end = os_strchr(str, ' '); + if (end == NULL || str + pos < end) + return list_cmd_list(); + + cmd = os_malloc(pos + 1); + if (cmd == NULL) + return NULL; + os_memcpy(cmd, str, pos); + cmd[end - str] = '\0'; + res = hostapd_cli_cmd_completion(cmd, str, pos); + os_free(cmd); + return res; +} + + static void hostapd_cli_interactive(void) { + char *hfile = NULL; + char *home; + printf("\nInteractive mode\n\n"); +#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR + home = CONFIG_HOSTAPD_CLI_HISTORY_DIR; +#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ + home = getenv("HOME"); +#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ + if (home) { + const char *fname = ".hostapd_cli_history"; + int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; + hfile = os_malloc(hfile_len); + if (hfile) + os_snprintf(hfile, hfile_len, "%s/%s", home, fname); + } + eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, - NULL, NULL, NULL, NULL); + hostapd_cli_edit_completion_cb, NULL, hfile, NULL); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); eloop_run(); - edit_deinit(NULL, NULL); + cli_txt_list_flush(&stations); + edit_deinit(hfile, NULL); + os_free(hfile); eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); } @@ -1388,8 +2048,7 @@ interactive = (argc == optind) && (action_file == NULL); if (interactive) { - printf("%s\n\n%s\n\n", hostapd_cli_version, - hostapd_cli_license); + printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license); } if (eloop_init()) @@ -1413,7 +2072,7 @@ closedir(dir); } } - ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); + hostapd_cli_reconnect(ctrl_ifname); if (ctrl_conn) { if (warning_displayed) printf("Connection established.\n"); @@ -1434,18 +2093,10 @@ continue; } - if (interactive || action_file) { - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - } else { - printf("Warning: Failed to attach to hostapd.\n"); - if (action_file) - return -1; - } - } - - if (daemonize && os_daemonize(pid_file)) + if (action_file && !hostapd_cli_attached) return -1; + if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) + return -1; if (interactive) hostapd_cli_interactive(); @@ -1454,8 +2105,18 @@ else wpa_request(ctrl_conn, argc - optind, &argv[optind]); + unregister_event_handler(ctrl_conn); os_free(ctrl_ifname); eloop_destroy(); hostapd_cli_cleanup(); return 0; } + +#else /* CONFIG_NO_CTRL_IFACE */ + +int main(int argc, char *argv[]) +{ + return -1; +} + +#endif /* CONFIG_NO_CTRL_IFACE */ --- contrib/wpa/hostapd/main.c.orig +++ contrib/wpa/hostapd/main.c @@ -1,6 +1,6 @@ /* * hostapd / main() - * Copyright (c) 2002-2015, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,6 +18,7 @@ #include "crypto/random.h" #include "crypto/tls.h" #include "common/version.h" +#include "common/dpp.h" #include "drivers/driver.h" #include "eap_server/eap.h" #include "eap_server/tncs.h" @@ -24,6 +25,7 @@ #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" +#include "ap/dpp_hostapd.h" #include "fst/fst.h" #include "config_file.h" #include "eap_register.h" @@ -108,6 +110,10 @@ module_str ? module_str : "", module_str ? ": " : "", txt); +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) + conf_stdout = 0; +#endif /* CONFIG_DEBUG_SYSLOG */ if ((conf_stdout & module) && level >= conf_stdout_level) { wpa_debug_print_timestamp(); wpa_printf(MSG_INFO, "%s", format); @@ -171,7 +177,8 @@ if (global.drv_priv[i] == NULL && wpa_drivers[i]->global_init) { - global.drv_priv[i] = wpa_drivers[i]->global_init(); + global.drv_priv[i] = + wpa_drivers[i]->global_init(iface->interfaces); if (global.drv_priv[i] == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize " "driver '%s'", @@ -216,11 +223,20 @@ iface->drv_flags = capa.flags; iface->smps_modes = capa.smps_modes; iface->probe_resp_offloads = capa.probe_resp_offloads; + /* + * Use default extended capa values from per-radio information + */ iface->extended_capa = capa.extended_capa; iface->extended_capa_mask = capa.extended_capa_mask; iface->extended_capa_len = capa.extended_capa_len; iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; + /* + * Override extended capa with per-interface type (AP), if + * available from the driver. + */ + hostapd_get_ext_capa(iface); + triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa); if (triggs && hapd->driver->set_wowlan) { if (hapd->driver->set_wowlan(hapd->drv_priv, triggs)) @@ -238,10 +254,10 @@ * * This function is used to parse configuration file for a full interface (one * or more BSSes sharing the same radio) and allocate memory for the BSS - * interfaces. No actiual driver operations are started. + * interfaces. No actual driver operations are started. */ static struct hostapd_iface * -hostapd_interface_init(struct hapd_interfaces *interfaces, +hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name, const char *config_fname, int debug) { struct hostapd_iface *iface; @@ -251,6 +267,12 @@ iface = hostapd_init(interfaces, config_fname); if (!iface) return NULL; + + if (if_name) { + os_strlcpy(iface->conf->bss[0]->iface, if_name, + sizeof(iface->conf->bss[0]->iface)); + } + iface->interfaces = interfaces; for (k = 0; k < debug; k++) { @@ -260,7 +282,8 @@ if (iface->conf->bss[0]->iface[0] == '\0' && !hostapd_drv_none(iface->bss[0])) { - wpa_printf(MSG_ERROR, "Interface name not specified in %s", + wpa_printf(MSG_ERROR, + "Interface name not specified in %s, nor by '-i' parameter", config_fname); hostapd_interface_deinit_free(iface); return NULL; @@ -329,6 +352,7 @@ wpa_printf(MSG_ERROR, "Failed to initialize event loop"); return -1; } + interfaces->eloop_initialized = 1; random_init(entropy_file); @@ -356,7 +380,7 @@ } -static void hostapd_global_deinit(const char *pid_file) +static void hostapd_global_deinit(const char *pid_file, int eloop_initialized) { int i; @@ -374,7 +398,8 @@ random_deinit(); - eloop_destroy(); + if (eloop_initialized) + eloop_destroy(); #ifndef CONFIG_NATIVE_WINDOWS closelog(); @@ -408,9 +433,16 @@ } #endif /* EAP_SERVER_TNC */ - if (daemonize && os_daemonize(pid_file)) { - wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); - return -1; + if (daemonize) { + if (os_daemonize(pid_file)) { + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); + return -1; + } + if (eloop_sock_requeue()) { + wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s", + strerror(errno)); + return -1; + } } eloop_run(); @@ -425,7 +457,7 @@ "hostapd v" VERSION_STR "\n" "User space daemon for IEEE 802.11 AP management,\n" "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" - "Copyright (c) 2002-2015, Jouni Malinen " + "Copyright (c) 2002-2019, Jouni Malinen " "and contributors\n"); } @@ -437,7 +469,8 @@ "\n" "usage: hostapd [-hdBKtv] [-P ] [-e ] " "\\\n" - " [-g ] [-G ] \\\n" + " [-g ] [-G ]\\\n" + " [-i ]\\\n" " \n" "\n" "options:\n" @@ -453,9 +486,14 @@ " -f log output to debug file instead of stdout\n" #endif /* CONFIG_DEBUG_FILE */ #ifdef CONFIG_DEBUG_LINUX_TRACING - " -T = record to Linux tracing in addition to logging\n" + " -T record to Linux tracing in addition to logging\n" " (records all messages regardless of debug verbosity)\n" #endif /* CONFIG_DEBUG_LINUX_TRACING */ + " -i list of interface names to use\n" +#ifdef CONFIG_DEBUG_SYSLOG + " -s log output to syslog instead of stdout\n" +#endif /* CONFIG_DEBUG_SYSLOG */ + " -S start all the interfaces synchronously\n" " -t include timestamps in some debug messages\n" " -v show hostapd version\n"); @@ -466,9 +504,8 @@ static const char * hostapd_msg_ifname_cb(void *ctx) { struct hostapd_data *hapd = ctx; - if (hapd && hapd->iconf && hapd->iconf->bss && - hapd->iconf->num_bss > 0 && hapd->iconf->bss[0]) - return hapd->iconf->bss[0]->iface; + if (hapd && hapd->conf) + return hapd->conf->iface; return NULL; } @@ -476,11 +513,16 @@ static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, const char *path) { +#ifndef CONFIG_CTRL_IFACE_UDP char *pos; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + os_free(interfaces->global_iface_path); interfaces->global_iface_path = os_strdup(path); if (interfaces->global_iface_path == NULL) return -1; + +#ifndef CONFIG_CTRL_IFACE_UDP pos = os_strrchr(interfaces->global_iface_path, '/'); if (pos == NULL) { wpa_printf(MSG_ERROR, "No '/' in the global control interface " @@ -492,6 +534,7 @@ *pos = '\0'; interfaces->global_iface_name = pos + 1; +#endif /* !CONFIG_CTRL_IFACE_UDP */ return 0; } @@ -513,6 +556,43 @@ } +static int hostapd_get_interface_names(char ***if_names, + size_t *if_names_size, + char *arg) +{ + char *if_name, *tmp, **nnames; + size_t i; + + if (!arg) + return -1; + if_name = strtok_r(arg, ",", &tmp); + + while (if_name) { + nnames = os_realloc_array(*if_names, 1 + *if_names_size, + sizeof(char *)); + if (!nnames) + goto fail; + *if_names = nnames; + + (*if_names)[*if_names_size] = os_strdup(if_name); + if (!(*if_names)[*if_names_size]) + goto fail; + (*if_names_size)++; + if_name = strtok_r(NULL, ",", &tmp); + } + + return 0; + +fail: + for (i = 0; i < *if_names_size; i++) + os_free((*if_names)[i]); + os_free(*if_names); + *if_names = NULL; + *if_names_size = 0; + return -1; +} + + #ifdef CONFIG_WPS static int gen_uuid(const char *txt_addr) { @@ -570,6 +650,9 @@ #ifdef CONFIG_DEBUG_LINUX_TRACING int enable_trace_dbg = 0; #endif /* CONFIG_DEBUG_LINUX_TRACING */ + int start_ifaces_in_sync = 0; + char **if_names = NULL; + size_t if_names_size = 0; if (os_program_init()) return -1; @@ -584,10 +667,18 @@ interfaces.global_iface_path = NULL; interfaces.global_iface_name = NULL; interfaces.global_ctrl_sock = -1; - interfaces.global_ctrl_dst = NULL; + dl_list_init(&interfaces.global_ctrl_dst); +#ifdef CONFIG_ETH_P_OUI + dl_list_init(&interfaces.eth_p_oui); +#endif /* CONFIG_ETH_P_OUI */ +#ifdef CONFIG_DPP + interfaces.dpp = dpp_global_init(); + if (!interfaces.dpp) + return -1; +#endif /* CONFIG_DPP */ for (;;) { - c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:"); + c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:"); if (c < 0) break; switch (c) { @@ -644,10 +735,23 @@ bss_config = tmp_bss; bss_config[num_bss_configs++] = optarg; break; +#ifdef CONFIG_DEBUG_SYSLOG + case 's': + wpa_debug_syslog = 1; + break; +#endif /* CONFIG_DEBUG_SYSLOG */ + case 'S': + start_ifaces_in_sync = 1; + break; #ifdef CONFIG_WPS case 'u': return gen_uuid(optarg); #endif /* CONFIG_WPS */ + case 'i': + if (hostapd_get_interface_names(&if_names, + &if_names_size, optarg)) + goto out; + break; default: usage(); break; @@ -664,6 +768,10 @@ wpa_debug_open_file(log_file); else wpa_debug_setup_stdout(); +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) + wpa_debug_open_syslog(); +#endif /* CONFIG_DEBUG_SYSLOG */ #ifdef CONFIG_DEBUG_LINUX_TRACING if (enable_trace_dbg) { int tret = wpa_debug_open_linux_tracing(); @@ -705,7 +813,13 @@ /* Allocate and parse configuration for full interface files */ for (i = 0; i < interfaces.count; i++) { + char *if_name = NULL; + + if (i < if_names_size) + if_name = if_names[i]; + interfaces.iface[i] = hostapd_interface_init(&interfaces, + if_name, argv[optind + i], debug); if (!interfaces.iface[i]) { @@ -712,6 +826,8 @@ wpa_printf(MSG_ERROR, "Failed to initialize interface"); goto out; } + if (start_ifaces_in_sync) + interfaces.iface[i]->need_to_start_in_sync = 1; } /* Allocate and parse configuration for per-BSS files */ @@ -787,10 +903,16 @@ } os_free(interfaces.iface); - eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); - hostapd_global_deinit(pid_file); +#ifdef CONFIG_DPP + dpp_global_deinit(interfaces.dpp); +#endif /* CONFIG_DPP */ + + if (interfaces.eloop_initialized) + eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); + hostapd_global_deinit(pid_file, interfaces.eloop_initialized); os_free(pid_file); + wpa_debug_close_syslog(); if (log_file) wpa_debug_close_file(); wpa_debug_close_linux_tracing(); @@ -797,6 +919,10 @@ os_free(bss_config); + for (i = 0; i < if_names_size; i++) + os_free(if_names[i]); + os_free(if_names); + fst_global_deinit(); os_program_deinit(); --- contrib/wpa/hostapd/wps-ap-nfc.py.orig +++ contrib/wpa/hostapd/wps-ap-nfc.py @@ -26,7 +26,7 @@ success_file = None def summary(txt): - print txt + print(txt) if summary_file: with open(summary_file, 'a') as f: f.write(txt + "\n") @@ -42,12 +42,12 @@ if os.path.isdir(wpas_ctrl): try: ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] - except OSError, error: - print "Could not find hostapd: ", error + except OSError as error: + print("Could not find hostapd: ", error) return None if len(ifaces) < 1: - print "No hostapd control interface found" + print("No hostapd control interface found") return None for ctrl in ifaces: @@ -54,7 +54,7 @@ try: wpas = wpaspy.Ctrl(ctrl) return wpas - except Exception, e: + except Exception as e: pass return None @@ -133,15 +133,15 @@ def process_request(self, request): summary("HandoverServer - request received") try: - print "Parsed handover request: " + request.pretty() - except Exception, e: - print e - print str(request).encode("hex") + print("Parsed handover request: " + request.pretty()) + except Exception as e: + print(e) + print(str(request).encode("hex")) sel = nfc.ndef.HandoverSelectMessage(version="1.2") for carrier in request.carriers: - print "Remote carrier type: " + carrier.type + print("Remote carrier type: " + carrier.type) if carrier.type == "application/vnd.wfa.wsc": summary("WPS carrier type match - add WPS carrier record") data = wpas_get_handover_sel() @@ -148,8 +148,8 @@ if data is None: summary("Could not get handover select carrier record from hostapd") continue - print "Handover select carrier record from hostapd:" - print data.encode("hex") + print("Handover select carrier record from hostapd:") + print(data.encode("hex")) if "OK" in wpas_report_handover(carrier.record, data): success_report("Handover reported successfully") else: @@ -158,12 +158,12 @@ message = nfc.ndef.Message(data); sel.add_carrier(message[0], "active", message[1:]) - print "Handover select:" + print("Handover select:") try: - print sel.pretty() - except Exception, e: - print e - print str(sel).encode("hex") + print(sel.pretty()) + except Exception as e: + print(e) + print(str(sel).encode("hex")) summary("Sending handover select") self.success = True @@ -174,7 +174,7 @@ success = False if len(tag.ndef.message): for record in tag.ndef.message: - print "record type " + record.type + print("record type " + record.type) if record.type == "application/vnd.wfa.wsc": summary("WPS tag - send to hostapd") success = wpas_tag_read(tag.ndef.message) @@ -193,7 +193,7 @@ global write_data tag.ndef.message = str(write_data) success_report("Tag write succeeded") - print "Done - remove tag" + print("Done - remove tag") global only_one if only_one: global continue_loop @@ -211,7 +211,7 @@ summary("Could not get WPS config token from hostapd") return - print "Touch an NFC tag" + print("Touch an NFC tag") clf.connect(rdwr={'on-connect': rdwr_connected_write}) @@ -224,7 +224,7 @@ summary("Could not get WPS password token from hostapd") return - print "Touch an NFC tag" + print("Touch an NFC tag") clf.connect(rdwr={'on-connect': rdwr_connected_write}) @@ -233,11 +233,11 @@ summary("Tag connected: " + str(tag)) if tag.ndef: - print "NDEF tag: " + tag.type + print("NDEF tag: " + tag.type) try: - print tag.ndef.message.pretty() - except Exception, e: - print e + print(tag.ndef.message.pretty()) + except Exception as e: + print(e) success = wps_tag_read(tag) if only_one and success: global continue_loop @@ -250,13 +250,13 @@ def llcp_startup(clf, llc): - print "Start LLCP server" + print("Start LLCP server") global srv srv = HandoverServer(llc) return llc def llcp_connected(llc): - print "P2P LLCP connected" + print("P2P LLCP connected") global wait_connection wait_connection = False global srv @@ -304,7 +304,7 @@ try: if not clf.open("usb"): - print "Could not open connection with an NFC device" + print("Could not open connection with an NFC device") raise SystemExit if args.command == "write-config": @@ -317,7 +317,7 @@ global continue_loop while continue_loop: - print "Waiting for a tag or peer to be touched" + print("Waiting for a tag or peer to be touched") wait_connection = True try: if not clf.connect(rdwr={'on-connect': rdwr_connected}, @@ -324,8 +324,8 @@ llcp={'on-startup': llcp_startup, 'on-connect': llcp_connected}): break - except Exception, e: - print "clf.connect failed" + except Exception as e: + print("clf.connect failed") global srv if only_one and srv and srv.success: --- contrib/wpa/hs20/client/Android.mk.orig +++ contrib/wpa/hs20/client/Android.mk @@ -4,7 +4,6 @@ INCLUDES += $(LOCAL_PATH)/../../src/utils INCLUDES += $(LOCAL_PATH)/../../src/common INCLUDES += $(LOCAL_PATH)/../../src -INCLUDES += external/openssl/include INCLUDES += external/libxml2/include INCLUDES += external/curl/include INCLUDES += external/webkit/Source/WebKit/gtk @@ -55,6 +54,7 @@ OBJS += ../../src/crypto/md5-internal.c OBJS += ../../src/crypto/sha1-internal.c OBJS += ../../src/crypto/sha256-internal.c +OBJS += ../../src/crypto/tls_openssl_ocsp.c L_CFLAGS += -DEAP_TLS_OPENSSL --- contrib/wpa/hs20/client/Makefile.orig +++ contrib/wpa/hs20/client/Makefile @@ -8,12 +8,17 @@ LDO=$(CC) endif +ifeq ($(QUIET), 1) Q=@ +E=true +else +Q=@ E=echo ifeq ($(V), 1) Q= E=true endif +endif ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g @@ -76,6 +81,7 @@ endif CFLAGS += -DEAP_TLS_OPENSSL +OBJS += ../../src/crypto/tls_openssl_ocsp.o LIBS += -lssl -lcrypto hs20-osu-client: $(OBJS) --- contrib/wpa/hs20/client/est.c.orig +++ contrib/wpa/hs20/client/est.c @@ -16,6 +16,10 @@ #include #include #include +#include +#ifdef OPENSSL_IS_BORINGSSL +#include +#endif /* OPENSSL_IS_BORINGSSL */ #include "common.h" #include "utils/base64.h" @@ -27,12 +31,28 @@ static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7, size_t len, char *pem_file, char *der_file) { +#ifdef OPENSSL_IS_BORINGSSL + CBS pkcs7_cbs; +#else /* OPENSSL_IS_BORINGSSL */ PKCS7 *p7 = NULL; const unsigned char *p = pkcs7; +#endif /* OPENSSL_IS_BORINGSSL */ STACK_OF(X509) *certs; int i, num, ret = -1; BIO *out = NULL; +#ifdef OPENSSL_IS_BORINGSSL + certs = sk_X509_new_null(); + if (!certs) + goto fail; + CBS_init(&pkcs7_cbs, pkcs7, len); + if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) { + wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s", + ERR_error_string(ERR_get_error(), NULL)); + write_result(ctx, "Could not parse PKCS#7 object from EST"); + goto fail; + } +#else /* OPENSSL_IS_BORINGSSL */ p7 = d2i_PKCS7(NULL, &p, len); if (p7 == NULL) { wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s", @@ -52,6 +72,7 @@ certs = NULL; break; } +#endif /* OPENSSL_IS_BORINGSSL */ if (!certs || ((num = sk_X509_num(certs)) == 0)) { wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object"); @@ -84,7 +105,12 @@ ret = 0; fail: +#ifdef OPENSSL_IS_BORINGSSL + if (certs) + sk_X509_pop_free(certs, X509_free); +#else /* OPENSSL_IS_BORINGSSL */ PKCS7_free(p7); +#endif /* OPENSSL_IS_BORINGSSL */ if (out) BIO_free_all(out); @@ -194,6 +220,10 @@ } d; } AttrOrOID; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) +DEFINE_STACK_OF(AttrOrOID) +#endif + typedef struct { int type; STACK_OF(AttrOrOID) *attrs; @@ -310,9 +340,34 @@ if (!csrattrs || ! csrattrs->attrs) return; +#ifdef OPENSSL_IS_BORINGSSL + num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *, + csrattrs->attrs)); + for (i = 0; i < num; i++) { + AttrOrOID *ao = sk_value( + CHECKED_CAST(_STACK *, const STACK_OF(AttrOrOID) *, + csrattrs->attrs), i); + switch (ao->type) { + case 0: + add_csrattrs_oid(ctx, ao->d.oid, exts); + break; + case 1: + add_csrattrs_attr(ctx, ao->d.attribute, exts); + break; + } + } +#else /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) + num = sk_AttrOrOID_num(csrattrs->attrs); +#else num = SKM_sk_num(AttrOrOID, csrattrs->attrs); +#endif for (i = 0; i < num; i++) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) + AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i); +#else AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i); +#endif switch (ao->type) { case 0: add_csrattrs_oid(ctx, ao->d.oid, exts); @@ -322,6 +377,7 @@ break; } } +#endif /* OPENSSL_IS_BORINGSSL */ } @@ -340,6 +396,7 @@ STACK_OF(X509_EXTENSION) *exts = NULL; X509_EXTENSION *ex; BIO *out; + CONF *ctmp = NULL; wpa_printf(MSG_INFO, "Generate RSA private key"); write_summary(ctx, "Generate RSA private key"); @@ -421,20 +478,20 @@ if (!exts) goto fail; - ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, - "CA:FALSE"); + ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints, + "CA:FALSE"); if (ex == NULL || !sk_X509_EXTENSION_push(exts, ex)) goto fail; - ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, - "nonRepudiation,digitalSignature,keyEncipherment"); + ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage, + "nonRepudiation,digitalSignature,keyEncipherment"); if (ex == NULL || !sk_X509_EXTENSION_push(exts, ex)) goto fail; - ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, - "1.3.6.1.4.1.40808.1.1.2"); + ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage, + "1.3.6.1.4.1.40808.1.1.2"); if (ex == NULL || !sk_X509_EXTENSION_push(exts, ex)) goto fail; @@ -454,7 +511,9 @@ char *txt; size_t rlen; +#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL) X509_REQ_print(out, req); +#endif rlen = BIO_ctrl_pending(out); txt = os_malloc(rlen + 1); if (txt) { @@ -473,7 +532,9 @@ FILE *f = fopen(csr_pem, "w"); if (f == NULL) goto fail; +#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL) X509_REQ_print_fp(f, req); +#endif if (!PEM_write_X509_REQ(f, req)) { fclose(f); goto fail; @@ -618,7 +679,6 @@ char *buf, *resp, *req, *req2; size_t buflen, resp_len, len, pkcs7_len; unsigned char *pkcs7; - FILE *f; char client_cert_buf[200]; char client_key_buf[200]; const char *client_cert = NULL, *client_key = NULL; @@ -673,11 +733,6 @@ return -1; } wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp); - f = fopen("Cert/est-resp.raw", "w"); - if (f) { - fwrite(resp, resp_len, 1, f); - fclose(f); - } pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len); if (pkcs7 == NULL) { --- contrib/wpa/hs20/client/oma_dm_client.c.orig +++ contrib/wpa/hs20/client/oma_dm_client.c @@ -111,6 +111,12 @@ xml_node_t *syncml, *synchdr; xml_namespace_t *ns; + if (!ctx->devid) { + wpa_printf(MSG_ERROR, + "DevId from devinfo.xml is not available - cannot use OMA DM"); + return NULL; + } + syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns, "SyncML"); --- contrib/wpa/hs20/client/osu_client.c.orig +++ contrib/wpa/hs20/client/osu_client.c @@ -105,6 +105,35 @@ } +static int android_update_permission(const char *path, mode_t mode) +{ +#ifdef ANDROID + /* we need to change file/folder permission for Android */ + + if (!path) { + wpa_printf(MSG_ERROR, "file path null"); + return -1; + } + + /* Allow processes running with Group ID as AID_WIFI, + * to read files from SP, SP/, Cert and osu-info directories */ + if (lchown(path, -1, AID_WIFI)) { + wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s", + strerror(errno)); + return -1; + } + + if (chmod(path, mode) < 0) { + wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", + strerror(errno)); + return -1; + } +#endif /* ANDROID */ + + return 0; +} + + int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert) { xml_node_t *node; @@ -169,6 +198,8 @@ } mkdir("Cert", S_IRWXU); + android_update_permission("Cert", S_IRWXU | S_IRWXG); + if (est_load_cacerts(ctx, url) < 0 || est_build_csr(ctx, url) < 0 || est_simple_enroll(ctx, url, user, pw) < 0) @@ -262,7 +293,6 @@ unlink("Cert/est-req.b64"); unlink("Cert/est-req.pem"); - unlink("Cert/est-resp.raw"); rmdir("Cert"); return 0; @@ -406,7 +436,7 @@ if (node == NULL) { wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS"); xml_node_free(ctx->xml, pps); - return -1; + return -2; } ret = download_cert(ctx, node, ca_fname); @@ -433,7 +463,7 @@ if (node == NULL) { wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS"); xml_node_free(ctx->xml, pps); - return -1; + return -2; } aaa = xml_node_first_child(ctx->xml, node); @@ -455,7 +485,7 @@ { char *dir, *pos; char fname[300]; - int ret; + int ret, ret1; dir = os_strdup(pps_fname); if (dir == NULL) @@ -470,9 +500,13 @@ snprintf(fname, sizeof(fname), "%s/ca.pem", dir); ret = cmd_dl_osu_ca(ctx, pps_fname, fname); snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir); - cmd_dl_polupd_ca(ctx, pps_fname, fname); + ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname); + if (ret == 0 && ret1 == -1) + ret = -1; snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir); - cmd_dl_aaa_ca(ctx, pps_fname, fname); + ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname); + if (ret == 0 && ret1 == -1) + ret = -1; os_free(dir); @@ -578,20 +612,8 @@ } } -#ifdef ANDROID - /* Allow processes running with Group ID as AID_WIFI, - * to read files from SP/ directory */ - if (chown(fname, -1, AID_WIFI)) { - wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s", - strerror(errno)); - /* Try to continue anyway */ - } - if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) { - wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", - strerror(errno)); - /* Try to continue anyway */ - } -#endif /* ANDROID */ + android_update_permission("SP", S_IRWXU | S_IRWXG); + android_update_permission(fname, S_IRWXU | S_IRWXG); snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn); @@ -1213,8 +1235,7 @@ homeoi) < 0) wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium"); } else { - if (set_cred_quoted(ctx->ifname, id, "roaming_consortium", - homeoi) < 0) + if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0) wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium"); } @@ -1289,7 +1310,9 @@ if (str == NULL) return; wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str); - /* TODO: Set to wpa_supplicant */ + if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums", + str) < 0) + wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums"); xml_node_get_text_free(ctx->xml, str); } @@ -1442,10 +1465,92 @@ } +static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx, + int id, xml_node_t *node) +{ + char *str = xml_node_get_text(ctx->xml, node); + int type; + const char *eap_method = NULL; + + if (!str) + return; + wpa_printf(MSG_INFO, + "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str); + type = atoi(str); + switch (type) { + case EAP_TYPE_TLS: + eap_method = "TLS"; + break; + case EAP_TYPE_TTLS: + eap_method = "TTLS"; + break; + case EAP_TYPE_PEAP: + eap_method = "PEAP"; + break; + case EAP_TYPE_PWD: + eap_method = "PWD"; + break; + } + xml_node_get_text_free(ctx->xml, str); + if (!eap_method) { + wpa_printf(MSG_INFO, "Unknown EAPType value"); + return; + } + + if (set_cred(ctx->ifname, id, "eap", eap_method) < 0) + wpa_printf(MSG_INFO, "Failed to set cred eap"); +} + + +static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx, + int id, xml_node_t *node) +{ + char *str = xml_node_get_text(ctx->xml, node); + const char *phase2 = NULL; + + if (!str) + return; + wpa_printf(MSG_INFO, + "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s", + str); + if (os_strcmp(str, "PAP") == 0) + phase2 = "auth=PAP"; + else if (os_strcmp(str, "CHAP") == 0) + phase2 = "auth=CHAP"; + else if (os_strcmp(str, "MS-CHAP") == 0) + phase2 = "auth=MSCHAP"; + else if (os_strcmp(str, "MS-CHAP-V2") == 0) + phase2 = "auth=MSCHAPV2"; + xml_node_get_text_free(ctx->xml, str); + if (!phase2) { + wpa_printf(MSG_INFO, "Unknown InnerMethod value"); + return; + } + + if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0) + wpa_printf(MSG_INFO, "Failed to set cred phase2"); +} + + static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id, xml_node_t *node) { - wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO"); + xml_node_t *child; + const char *name; + + wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod"); + + xml_node_for_each_child(ctx->xml, child, node) { + xml_node_for_each_check(ctx->xml, child); + name = xml_node_get_localname(ctx->xml, child); + if (os_strcasecmp(name, "EAPType") == 0) + set_pps_cred_eap_method_eap_type(ctx, id, child); + else if (os_strcasecmp(name, "InnerMethod") == 0) + set_pps_cred_eap_method_inner_method(ctx, id, child); + else + wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'", + name); + } } @@ -1884,7 +1989,9 @@ char url[256]; unsigned int methods; char osu_ssid[33]; + char osu_ssid2[33]; char osu_nai[256]; + char osu_nai2[256]; struct osu_lang_text friendly_name[MAX_OSU_VALS]; size_t friendly_name_count; struct osu_lang_text serv_desc[MAX_OSU_VALS]; @@ -1943,6 +2050,12 @@ continue; } + if (strncmp(buf, "osu_ssid2=", 10) == 0) { + snprintf(last->osu_ssid2, sizeof(last->osu_ssid2), + "%s", buf + 10); + continue; + } + if (os_strncmp(buf, "osu_nai=", 8) == 0) { os_snprintf(last->osu_nai, sizeof(last->osu_nai), "%s", buf + 8); @@ -1949,6 +2062,12 @@ continue; } + if (os_strncmp(buf, "osu_nai2=", 9) == 0) { + os_snprintf(last->osu_nai2, sizeof(last->osu_nai2), + "%s", buf + 9); + continue; + } + if (strncmp(buf, "friendly_name=", 14) == 0) { struct osu_lang_text *txt; if (last->friendly_name_count == MAX_OSU_VALS) @@ -2024,9 +2143,9 @@ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, - const char *ssid, const char *url, + const char *ssid, const char *ssid2, const char *url, unsigned int methods, int no_prod_assoc, - const char *osu_nai) + const char *osu_nai, const char *osu_nai2) { int id; const char *ifname = ctx->ifname; @@ -2034,11 +2153,32 @@ struct wpa_ctrl *mon; int res; + if (ssid2 && ssid2[0] == '\0') + ssid2 = NULL; + + if (ctx->osu_ssid) { + if (os_strcmp(ssid, ctx->osu_ssid) == 0) { + wpa_printf(MSG_DEBUG, + "Enforced OSU SSID matches ANQP info"); + ssid2 = NULL; + } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) { + wpa_printf(MSG_DEBUG, + "Enforced OSU SSID matches RSN[OSEN] info"); + ssid = ssid2; + } else { + wpa_printf(MSG_INFO, "Enforced OSU SSID did not match"); + write_summary(ctx, "Enforced OSU SSID did not match"); + return -1; + } + } + id = add_network(ifname); if (id < 0) return -1; if (set_network_quoted(ifname, id, "ssid", ssid) < 0) return -1; + if (ssid2) + osu_nai = osu_nai2; if (osu_nai && os_strlen(osu_nai) > 0) { char dir[255], fname[300]; if (getcwd(dir, sizeof(dir)) == NULL) @@ -2045,15 +2185,22 @@ return -1; os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir); + if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0) + return -1; + if (set_network(ifname, id, "proto", "OSEN") < 0 || set_network(ifname, id, "key_mgmt", "OSEN") < 0 || set_network(ifname, id, "pairwise", "CCMP") < 0 || - set_network(ifname, id, "group", "GTK_NOT_USED") < 0 || + set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 || set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 || set_network(ifname, id, "ocsp", "2") < 0 || set_network_quoted(ifname, id, "identity", osu_nai) < 0 || set_network_quoted(ifname, id, "ca_cert", fname) < 0) return -1; + } else if (ssid2) { + wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]"); + write_summary(ctx, "No OSU_NAI set for RSN[OSEN]"); + return -1; } else { if (set_network(ifname, id, "key_mgmt", "NONE") < 0) return -1; @@ -2134,7 +2281,7 @@ char fname[255]; FILE *f; struct osu_data *osu = NULL, *last = NULL; - size_t osu_count, i, j; + size_t osu_count = 0, i, j; int ret; write_summary(ctx, "OSU provider selection"); @@ -2229,8 +2376,12 @@ fprintf(f, "
BSSID: %s
\n" "SSID: %s
\n", last->bssid, last->osu_ssid); - if (last->osu_nai) + if (last->osu_ssid2[0]) + fprintf(f, "SSID2: %s
\n", last->osu_ssid2); + if (last->osu_nai[0]) fprintf(f, "NAI: %s
\n", last->osu_nai); + if (last->osu_nai2[0]) + fprintf(f, "NAI2: %s
\n", last->osu_nai2); fprintf(f, "URL: %s
\n" "methods:%s%s
\n" "

\n", @@ -2257,6 +2408,8 @@ ret = 0; wpa_printf(MSG_INFO, "BSSID: %s", last->bssid); wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid); + if (last->osu_ssid2[0]) + wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2); wpa_printf(MSG_INFO, "URL: %s", last->url); write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s", ret, last->bssid, last->osu_ssid, last->url); @@ -2311,10 +2464,13 @@ "No supported OSU provisioning method"); ret = -1; } - } else if (connect) + } else if (connect) { ret = osu_connect(ctx, last->bssid, last->osu_ssid, + last->osu_ssid2, last->url, last->methods, - no_prod_assoc, last->osu_nai); + no_prod_assoc, last->osu_nai, + last->osu_nai2); + } } else ret = -1; @@ -2339,12 +2495,15 @@ return -1; snprintf(fname, sizeof(fname), "%s/osu-info", dir); - if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) { + if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && + errno != EEXIST) { wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", fname, strerror(errno)); return -1; } + android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); if (wpa_command(ifname, buf) < 0) { wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant"); @@ -2559,7 +2718,7 @@ if (!pps_fname) { char buf[256]; wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); - if (os_strncmp(address, "fqdn=", 5) == 0) { + if (address && os_strncmp(address, "fqdn=", 5) == 0) { wpa_printf(MSG_INFO, "Use requested FQDN from command line"); os_snprintf(buf, sizeof(buf), "%s", address + 5); address = NULL; @@ -2909,26 +3068,19 @@ return -1; devinfo = node_from_file(ctx->xml, "devinfo.xml"); - if (!devinfo) { - wpa_printf(MSG_ERROR, "devinfo.xml not found"); - return -1; - } + if (devinfo) { + devid = get_node(ctx->xml, devinfo, "DevId"); + if (devid) { + char *tmp = xml_node_get_text(ctx->xml, devid); - devid = get_node(ctx->xml, devinfo, "DevId"); - if (devid) { - char *tmp = xml_node_get_text(ctx->xml, devid); - if (tmp) { - ctx->devid = os_strdup(tmp); - xml_node_get_text_free(ctx->xml, tmp); + if (tmp) { + ctx->devid = os_strdup(tmp); + xml_node_get_text_free(ctx->xml, tmp); + } } + xml_node_free(ctx->xml, devinfo); } - xml_node_free(ctx->xml, devinfo); - if (ctx->devid == NULL) { - wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml"); - return -1; - } - ctx->http = http_init_ctx(ctx, ctx->xml); if (ctx->http == NULL) { xml_node_deinit_ctx(ctx->xml); @@ -3029,7 +3181,7 @@ return -1; for (;;) { - c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:"); + c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:"); if (c < 0) break; switch (c) { @@ -3046,6 +3198,9 @@ case 'N': no_prod_assoc = 1; break; + case 'o': + ctx.osu_ssid = optarg; + break; case 'O': friendly_name = optarg; break; @@ -3122,20 +3277,12 @@ usage(); exit(0); } - if (argc - optind < 2) - wpa_printf(MSG_ERROR, "Server URL missing from command line"); - else - ret = cmd_sub_rem(&ctx, argv[optind + 1], - argc > optind + 2 ? - argv[optind + 2] : NULL, - argc > optind + 3 ? - argv[optind + 3] : NULL); + ret = cmd_sub_rem(&ctx, argv[optind + 1], + argc > optind + 2 ? argv[optind + 2] : NULL, + argc > optind + 3 ? argv[optind + 3] : NULL); } else if (strcmp(argv[optind], "pol_upd") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL, + ret = cmd_pol_upd(&ctx, + argc > optind + 1 ? argv[optind + 1] : NULL, argc > optind + 2 ? argv[optind + 2] : NULL, argc > optind + 3 ? argv[optind + 3] : NULL); } else if (strcmp(argv[optind], "prov") == 0) { --- contrib/wpa/hs20/client/osu_client.h.orig +++ contrib/wpa/hs20/client/osu_client.h @@ -47,6 +47,7 @@ int client_cert_present; char **server_dnsname; size_t server_dnsname_count; + const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */ #define WORKAROUND_OCSP_OPTIONAL 0x00000001 unsigned long int workarounds; }; --- contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch.orig +++ contrib/wpa/patches/openssl-0.9.8za-tls-extensions.patch @@ -1,397 +0,0 @@ -This patch adds support for TLS SessionTicket extension (RFC 5077) for -the parts used by EAP-FAST (RFC 4851). - -This is based on the patch from Alexey Kobozev -(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). - -OpenSSL 0.9.8za does not enable TLS extension support by default, so it -will need to be enabled by adding enable-tlsext to config script -command line. - - -diff -upr openssl-0.9.8za.orig/ssl/s3_clnt.c openssl-0.9.8za/ssl/s3_clnt.c ---- openssl-0.9.8za.orig/ssl/s3_clnt.c 2014-06-05 11:09:26.000000000 +0300 -+++ openssl-0.9.8za/ssl/s3_clnt.c 2014-06-05 20:37:09.221387312 +0300 -@@ -767,6 +767,22 @@ int ssl3_get_server_hello(SSL *s) - goto f_err; - } - -+#ifndef OPENSSL_NO_TLSEXT -+ /* check if we want to resume the session based on external pre-shared secret */ -+ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) -+ { -+ SSL_CIPHER *pref_cipher=NULL; -+ s->session->master_key_length=sizeof(s->session->master_key); -+ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, -+ NULL, &pref_cipher, s->tls_session_secret_cb_arg)) -+ { -+ s->session->cipher=pref_cipher ? -+ pref_cipher : ssl_get_cipher_by_char(s,p+j); -+ s->s3->flags |= SSL3_FLAGS_CCS_OK; -+ } -+ } -+#endif /* OPENSSL_NO_TLSEXT */ -+ - if (j != 0 && j == s->session->session_id_length - && memcmp(p,s->session->session_id,j) == 0) - { -@@ -2745,11 +2760,8 @@ int ssl3_check_finished(SSL *s) - { - int ok; - long n; -- /* If we have no ticket or session ID is non-zero length (a match of -- * a non-zero session length would never reach here) it cannot be a -- * resumed session. -- */ -- if (!s->session->tlsext_tick || s->session->session_id_length) -+ /* If we have no ticket it cannot be a resumed session. */ -+ if (!s->session->tlsext_tick) - return 1; - /* this function is called when we really expect a Certificate - * message, so permit appropriate message length */ -diff -upr openssl-0.9.8za.orig/ssl/s3_srvr.c openssl-0.9.8za/ssl/s3_srvr.c ---- openssl-0.9.8za.orig/ssl/s3_srvr.c 2014-06-05 11:09:26.000000000 +0300 -+++ openssl-0.9.8za/ssl/s3_srvr.c 2014-06-05 20:37:09.225387312 +0300 -@@ -1011,6 +1011,59 @@ int ssl3_get_client_hello(SSL *s) - SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT); - goto err; - } -+ -+ /* Check if we want to use external pre-shared secret for this -+ * handshake for not reused session only. We need to generate -+ * server_random before calling tls_session_secret_cb in order to allow -+ * SessionTicket processing to use it in key derivation. */ -+ { -+ unsigned long Time; -+ unsigned char *pos; -+ Time=(unsigned long)time(NULL); /* Time */ -+ pos=s->s3->server_random; -+ l2n(Time,pos); -+ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0) -+ { -+ al=SSL_AD_INTERNAL_ERROR; -+ goto f_err; -+ } -+ } -+ -+ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) -+ { -+ SSL_CIPHER *pref_cipher=NULL; -+ -+ s->session->master_key_length=sizeof(s->session->master_key); -+ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, -+ ciphers, &pref_cipher, s->tls_session_secret_cb_arg)) -+ { -+ s->hit=1; -+ s->session->ciphers=ciphers; -+ s->session->verify_result=X509_V_OK; -+ -+ ciphers=NULL; -+ -+ /* check if some cipher was preferred by call back */ -+ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s)); -+ if (pref_cipher == NULL) -+ { -+ al=SSL_AD_HANDSHAKE_FAILURE; -+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER); -+ goto f_err; -+ } -+ -+ s->session->cipher=pref_cipher; -+ -+ if (s->cipher_list) -+ sk_SSL_CIPHER_free(s->cipher_list); -+ -+ if (s->cipher_list_by_id) -+ sk_SSL_CIPHER_free(s->cipher_list_by_id); -+ -+ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); -+ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); -+ } -+ } - #endif - /* Worst case, we will use the NULL compression, but if we have other - * options, we will now look for them. We have i-1 compression -@@ -1161,16 +1214,22 @@ int ssl3_send_server_hello(SSL *s) - unsigned char *buf; - unsigned char *p,*d; - int i,sl; -- unsigned long l,Time; -+ unsigned long l; -+#ifdef OPENSSL_NO_TLSEXT -+ unsigned long Time; -+#endif - - if (s->state == SSL3_ST_SW_SRVR_HELLO_A) - { - buf=(unsigned char *)s->init_buf->data; -+#ifdef OPENSSL_NO_TLSEXT - p=s->s3->server_random; -+ /* Generate server_random if it was not needed previously */ - Time=(unsigned long)time(NULL); /* Time */ - l2n(Time,p); - if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) - return -1; -+#endif - /* Do the message type and length last */ - d=p= &(buf[4]); - -diff -upr openssl-0.9.8za.orig/ssl/ssl_err.c openssl-0.9.8za/ssl/ssl_err.c ---- openssl-0.9.8za.orig/ssl/ssl_err.c 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/ssl_err.c 2014-06-05 20:37:09.225387312 +0300 -@@ -265,6 +265,7 @@ static ERR_STRING_DATA SSL_str_functs[]= - {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, - {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, - {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, -+{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, - {0,NULL} - }; - -diff -upr openssl-0.9.8za.orig/ssl/ssl.h openssl-0.9.8za/ssl/ssl.h ---- openssl-0.9.8za.orig/ssl/ssl.h 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/ssl.h 2014-06-05 20:37:09.229387312 +0300 -@@ -344,6 +344,7 @@ extern "C" { - * 'struct ssl_st *' function parameters used to prototype callbacks - * in SSL_CTX. */ - typedef struct ssl_st *ssl_crock_st; -+typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; - - /* used to hold info on the particular ciphers used */ - typedef struct ssl_cipher_st -@@ -362,6 +363,9 @@ typedef struct ssl_cipher_st - - DECLARE_STACK_OF(SSL_CIPHER) - -+typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg); -+typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg); -+ - /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ - typedef struct ssl_method_st - { -@@ -1053,6 +1057,18 @@ struct ssl_st - - /* RFC4507 session ticket expected to be received or sent */ - int tlsext_ticket_expected; -+ -+ /* TLS Session Ticket extension override */ -+ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; -+ -+ /* TLS Session Ticket extension callback */ -+ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; -+ void *tls_session_ticket_ext_cb_arg; -+ -+ /* TLS pre-shared secret session resumption */ -+ tls_session_secret_cb_fn tls_session_secret_cb; -+ void *tls_session_secret_cb_arg; -+ - SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ - #define session_ctx initial_ctx - #else -@@ -1668,6 +1684,15 @@ void *SSL_COMP_get_compression_methods(v - int SSL_COMP_add_compression_method(int id,void *cm); - #endif - -+/* TLS extensions functions */ -+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); -+ -+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, -+ void *arg); -+ -+/* Pre-shared secret session resumption functions */ -+int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg); -+ - /* BEGIN ERROR CODES */ - /* The following lines are auto generated by the script mkerr.pl. Any changes - * made after this point may be overwritten when the script is next run. -@@ -1872,6 +1897,7 @@ void ERR_load_SSL_strings(void); - #define SSL_F_TLS1_ENC 210 - #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 - #define SSL_F_WRITE_PENDING 212 -+#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 - - /* Reason codes. */ - #define SSL_R_APP_DATA_IN_HANDSHAKE 100 -diff -upr openssl-0.9.8za.orig/ssl/ssl_sess.c openssl-0.9.8za/ssl/ssl_sess.c ---- openssl-0.9.8za.orig/ssl/ssl_sess.c 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/ssl_sess.c 2014-06-05 20:37:09.229387312 +0300 -@@ -712,6 +712,61 @@ long SSL_CTX_get_timeout(const SSL_CTX * - return(s->session_timeout); - } - -+#ifndef OPENSSL_NO_TLSEXT -+int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, -+ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg) -+ { -+ if (s == NULL) return(0); -+ s->tls_session_secret_cb = tls_session_secret_cb; -+ s->tls_session_secret_cb_arg = arg; -+ return(1); -+ } -+ -+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, -+ void *arg) -+ { -+ if (s == NULL) return(0); -+ s->tls_session_ticket_ext_cb = cb; -+ s->tls_session_ticket_ext_cb_arg = arg; -+ return(1); -+ } -+ -+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) -+ { -+ if (s->version >= TLS1_VERSION) -+ { -+ if (s->tlsext_session_ticket) -+ { -+ OPENSSL_free(s->tlsext_session_ticket); -+ s->tlsext_session_ticket = NULL; -+ } -+ -+ s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len); -+ if (!s->tlsext_session_ticket) -+ { -+ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); -+ return 0; -+ } -+ -+ if (ext_data) -+ { -+ s->tlsext_session_ticket->length = ext_len; -+ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; -+ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); -+ } -+ else -+ { -+ s->tlsext_session_ticket->length = 0; -+ s->tlsext_session_ticket->data = NULL; -+ } -+ -+ return 1; -+ } -+ -+ return 0; -+ } -+#endif /* OPENSSL_NO_TLSEXT */ -+ - typedef struct timeout_param_st - { - SSL_CTX *ctx; -diff -upr openssl-0.9.8za.orig/ssl/t1_lib.c openssl-0.9.8za/ssl/t1_lib.c ---- openssl-0.9.8za.orig/ssl/t1_lib.c 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/t1_lib.c 2014-06-05 20:37:09.229387312 +0300 -@@ -106,6 +106,12 @@ int tls1_new(SSL *s) - - void tls1_free(SSL *s) - { -+#ifndef OPENSSL_NO_TLSEXT -+ if (s->tlsext_session_ticket) -+ { -+ OPENSSL_free(s->tlsext_session_ticket); -+ } -+#endif - ssl3_free(s); - } - -@@ -206,8 +212,23 @@ unsigned char *ssl_add_clienthello_tlsex - int ticklen; - if (!s->new_session && s->session && s->session->tlsext_tick) - ticklen = s->session->tlsext_ticklen; -+ else if (s->session && s->tlsext_session_ticket && -+ s->tlsext_session_ticket->data) -+ { -+ ticklen = s->tlsext_session_ticket->length; -+ s->session->tlsext_tick = OPENSSL_malloc(ticklen); -+ if (!s->session->tlsext_tick) -+ return NULL; -+ memcpy(s->session->tlsext_tick, -+ s->tlsext_session_ticket->data, -+ ticklen); -+ s->session->tlsext_ticklen = ticklen; -+ } - else - ticklen = 0; -+ if (ticklen == 0 && s->tlsext_session_ticket && -+ s->tlsext_session_ticket->data == NULL) -+ goto skip_ext; - /* Check for enough room 2 for extension type, 2 for len - * rest for ticket - */ -@@ -221,6 +242,7 @@ unsigned char *ssl_add_clienthello_tlsex - ret += ticklen; - } - } -+ skip_ext: - - if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && - s->version != DTLS1_VERSION) -@@ -574,6 +596,15 @@ int ssl_parse_clienthello_tlsext(SSL *s, - return 0; - renegotiate_seen = 1; - } -+ else if (type == TLSEXT_TYPE_session_ticket) -+ { -+ if (s->tls_session_ticket_ext_cb && -+ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) -+ { -+ *al = TLS1_AD_INTERNAL_ERROR; -+ return 0; -+ } -+ } - else if (type == TLSEXT_TYPE_status_request && - s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) - { -@@ -751,6 +782,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, - } - else if (type == TLSEXT_TYPE_session_ticket) - { -+ if (s->tls_session_ticket_ext_cb && -+ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg)) -+ { -+ *al = TLS1_AD_INTERNAL_ERROR; -+ return 0; -+ } - if ((SSL_get_options(s) & SSL_OP_NO_TICKET) - || (size > 0)) - { -@@ -1043,6 +1080,15 @@ int tls1_process_ticket(SSL *s, unsigned - s->tlsext_ticket_expected = 1; - return 0; /* Cache miss */ - } -+ if (s->tls_session_secret_cb) -+ { -+ /* Indicate cache miss here and instead of -+ * generating the session from ticket now, -+ * trigger abbreviated handshake based on -+ * external mechanism to calculate the master -+ * secret later. */ -+ return 0; -+ } - return tls_decrypt_ticket(s, p, size, session_id, len, - ret); - } -diff -upr openssl-0.9.8za.orig/ssl/tls1.h openssl-0.9.8za/ssl/tls1.h ---- openssl-0.9.8za.orig/ssl/tls1.h 2014-06-05 11:09:08.000000000 +0300 -+++ openssl-0.9.8za/ssl/tls1.h 2014-06-05 20:37:09.229387312 +0300 -@@ -415,6 +415,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T - #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ - #endif - -+/* TLS extension struct */ -+struct tls_session_ticket_ext_st -+ { -+ unsigned short length; -+ void *data; -+ }; -+ - #ifdef __cplusplus - } - #endif -diff -upr openssl-0.9.8za.orig/util/ssleay.num openssl-0.9.8za/util/ssleay.num ---- openssl-0.9.8za.orig/util/ssleay.num 2014-06-05 12:38:45.000000000 +0300 -+++ openssl-0.9.8za/util/ssleay.num 2014-06-05 20:37:09.229387312 +0300 -@@ -242,3 +242,5 @@ SSL_set_SSL_CTX - SSL_get_servername 291 EXIST::FUNCTION:TLSEXT - SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT - SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE -+SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT -+SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT --- contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch.orig +++ contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch @@ -1,398 +0,0 @@ -This patch adds support for TLS SessionTicket extension (RFC 5077) for -the parts used by EAP-FAST (RFC 4851). - -This is based on the patch from Alexey Kobozev -(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). - -OpenSSL 0.9.8zf does not enable TLS extension support by default, so it -will need to be enabled by adding enable-tlsext to config script -command line. - - -diff -upr openssl-0.9.8zf.orig/ssl/s3_clnt.c openssl-0.9.8zf/ssl/s3_clnt.c ---- openssl-0.9.8zf.orig/ssl/s3_clnt.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/s3_clnt.c 2015-03-24 16:19:14.043911769 +0200 -@@ -760,6 +760,23 @@ int ssl3_get_server_hello(SSL *s) - goto f_err; - } - -+#ifndef OPENSSL_NO_TLSEXT -+ /* check if we want to resume the session based on external pre-shared secret */ -+ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) { -+ SSL_CIPHER *pref_cipher = NULL; -+ -+ s->session->master_key_length = sizeof(s->session->master_key); -+ if (s->tls_session_secret_cb(s, s->session->master_key, -+ &s->session->master_key_length, -+ NULL, &pref_cipher, -+ s->tls_session_secret_cb_arg)) { -+ s->session->cipher = pref_cipher ? -+ pref_cipher : ssl_get_cipher_by_char(s, p + j); -+ s->s3->flags |= SSL3_FLAGS_CCS_OK; -+ } -+ } -+#endif /* OPENSSL_NO_TLSEXT */ -+ - if (j != 0 && j == s->session->session_id_length - && memcmp(p, s->session->session_id, j) == 0) { - if (s->sid_ctx_length != s->session->sid_ctx_length -@@ -2684,12 +2701,8 @@ int ssl3_check_finished(SSL *s) - { - int ok; - long n; -- /* -- * If we have no ticket or session ID is non-zero length (a match of a -- * non-zero session length would never reach here) it cannot be a resumed -- * session. -- */ -- if (!s->session->tlsext_tick || s->session->session_id_length) -+ /* If we have no ticket it cannot be a resumed session. */ -+ if (!s->session->tlsext_tick) - return 1; - /* - * this function is called when we really expect a Certificate message, -diff -upr openssl-0.9.8zf.orig/ssl/s3_srvr.c openssl-0.9.8zf/ssl/s3_srvr.c ---- openssl-0.9.8zf.orig/ssl/s3_srvr.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/s3_srvr.c 2015-03-24 16:23:34.567909681 +0200 -@@ -999,6 +999,59 @@ int ssl3_get_client_hello(SSL *s) - SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT); - goto err; - } -+ -+ /* Check if we want to use external pre-shared secret for this -+ * handshake for not reused session only. We need to generate -+ * server_random before calling tls_session_secret_cb in order to allow -+ * SessionTicket processing to use it in key derivation. */ -+ { -+ unsigned long Time; -+ unsigned char *pos; -+ Time = (unsigned long)time(NULL); /* Time */ -+ pos = s->s3->server_random; -+ l2n(Time, pos); -+ if (RAND_pseudo_bytes(pos, SSL3_RANDOM_SIZE - 4) <= 0) { -+ al = SSL_AD_INTERNAL_ERROR; -+ goto f_err; -+ } -+ } -+ -+ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) { -+ SSL_CIPHER *pref_cipher = NULL; -+ -+ s->session->master_key_length = sizeof(s->session->master_key); -+ if (s->tls_session_secret_cb(s, s->session->master_key, -+ &s->session->master_key_length, -+ ciphers, &pref_cipher, -+ s->tls_session_secret_cb_arg)) { -+ s->hit = 1; -+ s->session->ciphers = ciphers; -+ s->session->verify_result = X509_V_OK; -+ -+ ciphers = NULL; -+ -+ /* check if some cipher was preferred by call back */ -+ pref_cipher = pref_cipher ? pref_cipher : -+ ssl3_choose_cipher(s, s->session->ciphers, -+ SSL_get_ciphers(s)); -+ if (pref_cipher == NULL) { -+ al = SSL_AD_HANDSHAKE_FAILURE; -+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER); -+ goto f_err; -+ } -+ -+ s->session->cipher = pref_cipher; -+ -+ if (s->cipher_list) -+ sk_SSL_CIPHER_free(s->cipher_list); -+ -+ if (s->cipher_list_by_id) -+ sk_SSL_CIPHER_free(s->cipher_list_by_id); -+ -+ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); -+ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); -+ } -+ } - #endif - /* - * Worst case, we will use the NULL compression, but if we have other -@@ -1143,15 +1196,21 @@ int ssl3_send_server_hello(SSL *s) - unsigned char *buf; - unsigned char *p, *d; - int i, sl; -- unsigned long l, Time; -+ unsigned long l; -+#ifdef OPENSSL_NO_TLSEXT -+ unsigned long Time; -+#endif - - if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { - buf = (unsigned char *)s->init_buf->data; -+#ifdef OPENSSL_NO_TLSEXT - p = s->s3->server_random; -+ /* Generate server_random if it was not needed previously */ - Time = (unsigned long)time(NULL); /* Time */ - l2n(Time, p); - if (RAND_pseudo_bytes(p, SSL3_RANDOM_SIZE - 4) <= 0) - return -1; -+#endif - /* Do the message type and length last */ - d = p = &(buf[4]); - -diff -upr openssl-0.9.8zf.orig/ssl/ssl_err.c openssl-0.9.8zf/ssl/ssl_err.c ---- openssl-0.9.8zf.orig/ssl/ssl_err.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/ssl_err.c 2015-03-24 16:35:58.627903717 +0200 -@@ -316,6 +316,7 @@ static ERR_STRING_DATA SSL_str_functs[] - {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, - {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, - {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, -+ {ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, - {0, NULL} - }; - -diff -upr openssl-0.9.8zf.orig/ssl/ssl.h openssl-0.9.8zf/ssl/ssl.h ---- openssl-0.9.8zf.orig/ssl/ssl.h 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/ssl.h 2015-03-24 16:25:44.339908641 +0200 -@@ -349,6 +349,7 @@ extern "C" { - * function parameters used to prototype callbacks in SSL_CTX. - */ - typedef struct ssl_st *ssl_crock_st; -+typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; - - /* used to hold info on the particular ciphers used */ - typedef struct ssl_cipher_st { -@@ -366,6 +367,12 @@ typedef struct ssl_cipher_st { - - DECLARE_STACK_OF(SSL_CIPHER) - -+typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, -+ int len, void *arg); -+typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, -+ STACK_OF(SSL_CIPHER) *peer_ciphers, -+ SSL_CIPHER **cipher, void *arg); -+ - /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ - typedef struct ssl_method_st { - int version; -@@ -1116,6 +1123,18 @@ struct ssl_st { - int tlsext_ocsp_resplen; - /* RFC4507 session ticket expected to be received or sent */ - int tlsext_ticket_expected; -+ -+ /* TLS Session Ticket extension override */ -+ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; -+ -+ /* TLS Session Ticket extension callback */ -+ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; -+ void *tls_session_ticket_ext_cb_arg; -+ -+ /* TLS pre-shared secret session resumption */ -+ tls_session_secret_cb_fn tls_session_secret_cb; -+ void *tls_session_secret_cb_arg; -+ - SSL_CTX *initial_ctx; /* initial ctx, used to store sessions */ - # define session_ctx initial_ctx - # else -@@ -1772,6 +1791,17 @@ void *SSL_COMP_get_compression_methods(v - int SSL_COMP_add_compression_method(int id, void *cm); - # endif - -+/* TLS extensions functions */ -+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); -+ -+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, -+ void *arg); -+ -+/* Pre-shared secret session resumption functions */ -+int SSL_set_session_secret_cb(SSL *s, -+ tls_session_secret_cb_fn tls_session_secret_cb, -+ void *arg); -+ - /* BEGIN ERROR CODES */ - /* - * The following lines are auto generated by the script mkerr.pl. Any changes -@@ -1977,6 +2007,7 @@ void ERR_load_SSL_strings(void); - # define SSL_F_TLS1_ENC 210 - # define SSL_F_TLS1_SETUP_KEY_BLOCK 211 - # define SSL_F_WRITE_PENDING 212 -+#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 - - /* Reason codes. */ - # define SSL_R_APP_DATA_IN_HANDSHAKE 100 -diff -upr openssl-0.9.8zf.orig/ssl/ssl_sess.c openssl-0.9.8zf/ssl/ssl_sess.c ---- openssl-0.9.8zf.orig/ssl/ssl_sess.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/ssl_sess.c 2015-03-24 16:28:04.819907515 +0200 -@@ -716,6 +716,61 @@ long SSL_CTX_get_timeout(const SSL_CTX * - return (s->session_timeout); - } - -+#ifndef OPENSSL_NO_TLSEXT -+int SSL_set_session_secret_cb( -+ SSL *s, -+ int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, -+ STACK_OF(SSL_CIPHER) *peer_ciphers, -+ SSL_CIPHER **cipher, void *arg), void *arg) -+{ -+ if (s == NULL) -+ return 0; -+ s->tls_session_secret_cb = tls_session_secret_cb; -+ s->tls_session_secret_cb_arg = arg; -+ return 1; -+} -+ -+int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, -+ void *arg) -+{ -+ if (s == NULL) -+ return 0; -+ s->tls_session_ticket_ext_cb = cb; -+ s->tls_session_ticket_ext_cb_arg = arg; -+ return 1; -+} -+ -+int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) -+{ -+ if (s->version >= TLS1_VERSION) { -+ if (s->tlsext_session_ticket) { -+ OPENSSL_free(s->tlsext_session_ticket); -+ s->tlsext_session_ticket = NULL; -+ } -+ -+ s->tlsext_session_ticket = OPENSSL_malloc( -+ sizeof(TLS_SESSION_TICKET_EXT) + ext_len); -+ if (!s->tlsext_session_ticket) { -+ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); -+ return 0; -+ } -+ -+ if (ext_data) { -+ s->tlsext_session_ticket->length = ext_len; -+ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; -+ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); -+ } else { -+ s->tlsext_session_ticket->length = 0; -+ s->tlsext_session_ticket->data = NULL; -+ } -+ -+ return 1; -+ } -+ -+ return 0; -+} -+#endif /* OPENSSL_NO_TLSEXT */ -+ - typedef struct timeout_param_st { - SSL_CTX *ctx; - long time; -diff -upr openssl-0.9.8zf.orig/ssl/t1_lib.c openssl-0.9.8zf/ssl/t1_lib.c ---- openssl-0.9.8zf.orig/ssl/t1_lib.c 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/t1_lib.c 2015-03-24 16:32:46.923905254 +0200 -@@ -108,6 +108,11 @@ int tls1_new(SSL *s) - - void tls1_free(SSL *s) - { -+#ifndef OPENSSL_NO_TLSEXT -+ if (s->tlsext_session_ticket) { -+ OPENSSL_free(s->tlsext_session_ticket); -+ } -+#endif - ssl3_free(s); - } - -@@ -206,8 +211,20 @@ unsigned char *ssl_add_clienthello_tlsex - int ticklen; - if (!s->new_session && s->session && s->session->tlsext_tick) - ticklen = s->session->tlsext_ticklen; -- else -+ else if (s->session && s->tlsext_session_ticket && -+ s->tlsext_session_ticket->data) { -+ ticklen = s->tlsext_session_ticket->length; -+ s->session->tlsext_tick = OPENSSL_malloc(ticklen); -+ if (!s->session->tlsext_tick) -+ return NULL; -+ memcpy(s->session->tlsext_tick, s->tlsext_session_ticket->data, -+ ticklen); -+ s->session->tlsext_ticklen = ticklen; -+ } else - ticklen = 0; -+ if (ticklen == 0 && s->tlsext_session_ticket && -+ s->tlsext_session_ticket->data == NULL) -+ goto skip_ext; - /* - * Check for enough room 2 for extension type, 2 for len rest for - * ticket -@@ -221,6 +238,7 @@ unsigned char *ssl_add_clienthello_tlsex - ret += ticklen; - } - } -+skip_ext: - - if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && - s->version != DTLS1_VERSION) { -@@ -560,6 +578,14 @@ int ssl_parse_clienthello_tlsext(SSL *s, - if (!ssl_parse_clienthello_renegotiate_ext(s, data, size, al)) - return 0; - renegotiate_seen = 1; -+ } else if (type == TLSEXT_TYPE_session_ticket) { -+ if (s->tls_session_ticket_ext_cb && -+ !s->tls_session_ticket_ext_cb(s, data, size, -+ s->tls_session_ticket_ext_cb_arg)) -+ { -+ *al = TLS1_AD_INTERNAL_ERROR; -+ return 0; -+ } - } else if (type == TLSEXT_TYPE_status_request && - s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) { - -@@ -710,6 +736,13 @@ int ssl_parse_serverhello_tlsext(SSL *s, - } - tlsext_servername = 1; - } else if (type == TLSEXT_TYPE_session_ticket) { -+ if (s->tls_session_ticket_ext_cb && -+ !s->tls_session_ticket_ext_cb( -+ s, data, size, -+ s->tls_session_ticket_ext_cb_arg)) { -+ *al = TLS1_AD_INTERNAL_ERROR; -+ return 0; -+ } - if ((SSL_get_options(s) & SSL_OP_NO_TICKET) - || (size > 0)) { - *al = TLS1_AD_UNSUPPORTED_EXTENSION; -@@ -993,6 +1026,14 @@ int tls1_process_ticket(SSL *s, unsigned - s->tlsext_ticket_expected = 1; - return 0; /* Cache miss */ - } -+ if (s->tls_session_secret_cb) { -+ /* Indicate cache miss here and instead of -+ * generating the session from ticket now, -+ * trigger abbreviated handshake based on -+ * external mechanism to calculate the master -+ * secret later. */ -+ return 0; -+ } - return tls_decrypt_ticket(s, p, size, session_id, len, ret); - } - p += size; -diff -upr openssl-0.9.8zf.orig/ssl/tls1.h openssl-0.9.8zf/ssl/tls1.h ---- openssl-0.9.8zf.orig/ssl/tls1.h 2015-03-19 15:46:46.000000000 +0200 -+++ openssl-0.9.8zf/ssl/tls1.h 2015-03-24 16:33:31.855904894 +0200 -@@ -460,6 +460,12 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T - # define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" - # endif - -+/* TLS extension struct */ -+struct tls_session_ticket_ext_st { -+ unsigned short length; -+ void *data; -+}; -+ - #ifdef __cplusplus - } - #endif -diff -upr openssl-0.9.8zf.orig/util/ssleay.num openssl-0.9.8zf/util/ssleay.num ---- openssl-0.9.8zf.orig/util/ssleay.num 2015-03-19 15:47:15.000000000 +0200 -+++ openssl-0.9.8zf/util/ssleay.num 2015-03-24 16:33:51.127904739 +0200 -@@ -242,3 +242,5 @@ SSL_set_SSL_CTX - SSL_get_servername 291 EXIST::FUNCTION:TLSEXT - SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT - SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE -+SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT -+SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT --- contrib/wpa/src/ap/accounting.c.orig +++ contrib/wpa/src/ap/accounting.c @@ -1,6 +1,6 @@ /* * hostapd / RADIUS Accounting - * Copyright (c) 2002-2009, 2012, Jouni Malinen + * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -41,6 +41,7 @@ size_t len; int i; struct wpabuf *b; + struct os_time now; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); @@ -49,27 +50,6 @@ return NULL; } - if (sta) { - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); - - if ((hapd->conf->wpa & 2) && - !hapd->conf->disable_pmksa_caching && - sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) { - os_snprintf(buf, sizeof(buf), "%08X+%08X", - sta->eapol_sm->acct_multi_session_id_hi, - sta->eapol_sm->acct_multi_session_id_lo); - if (!radius_msg_add_attr( - msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, - (u8 *) buf, os_strlen(buf))) { - wpa_printf(MSG_INFO, - "Could not add Acct-Multi-Session-Id"); - goto fail; - } - } - } else { - radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); - } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, status_type)) { wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); @@ -76,17 +56,18 @@ goto fail; } - if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_ACCT_AUTHENTIC) && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, - hapd->conf->ieee802_1x ? - RADIUS_ACCT_AUTHENTIC_RADIUS : - RADIUS_ACCT_AUTHENTIC_LOCAL)) { - wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); - goto fail; - } + if (sta) { + if (!hostapd_config_get_radius_attr( + hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_ACCT_AUTHENTIC) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, + hapd->conf->ieee802_1x ? + RADIUS_ACCT_AUTHENTIC_RADIUS : + RADIUS_ACCT_AUTHENTIC_LOCAL)) { + wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); + goto fail; + } - if (sta) { /* Use 802.1X identity if available */ val = ieee802_1x_get_identity(sta->eapol_sm, &len); @@ -147,8 +128,34 @@ wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); goto fail; } + + if (sta->ipaddr && + !radius_msg_add_attr_int32(msg, + RADIUS_ATTR_FRAMED_IP_ADDRESS, + be_to_host32(sta->ipaddr))) { + wpa_printf(MSG_ERROR, + "Could not add Framed-IP-Address"); + goto fail; + } } + os_get_time(&now); + if (now.sec > 1000000000 && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); + goto fail; + } + + /* + * Add Acct-Delay-Time with zero value for the first transmission. This + * will be updated within radius_client.c when retransmitting the frame. + */ + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) { + wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time"); + goto fail; + } + return msg; fail: @@ -164,19 +171,25 @@ if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) return -1; - if (sta->last_rx_bytes > data->rx_bytes) - sta->acct_input_gigawords++; - if (sta->last_tx_bytes > data->tx_bytes) - sta->acct_output_gigawords++; - sta->last_rx_bytes = data->rx_bytes; - sta->last_tx_bytes = data->tx_bytes; + if (!data->bytes_64bit) { + /* Extend 32-bit counters from the driver to 64-bit counters */ + if (sta->last_rx_bytes_lo > data->rx_bytes) + sta->last_rx_bytes_hi++; + sta->last_rx_bytes_lo = data->rx_bytes; + if (sta->last_tx_bytes_lo > data->tx_bytes) + sta->last_tx_bytes_hi++; + sta->last_tx_bytes_lo = data->tx_bytes; + } + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " - "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " - "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", - sta->last_rx_bytes, sta->acct_input_gigawords, - sta->last_tx_bytes, sta->acct_output_gigawords); + HOSTAPD_LEVEL_DEBUG, + "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d", + data->rx_bytes, sta->last_rx_bytes_hi, + sta->last_rx_bytes_lo, + data->tx_bytes, sta->last_tx_bytes_hi, + sta->last_tx_bytes_lo, + data->bytes_64bit); return 0; } @@ -217,12 +230,14 @@ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "starting accounting session %08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); + "starting accounting session %016llX", + (unsigned long long) sta->acct_session_id); os_get_reltime(&sta->acct_session_start); - sta->last_rx_bytes = sta->last_tx_bytes = 0; - sta->acct_input_gigawords = sta->acct_output_gigawords = 0; + sta->last_rx_bytes_hi = 0; + sta->last_rx_bytes_lo = 0; + sta->last_tx_bytes_hi = 0; + sta->last_tx_bytes_lo = 0; hostapd_drv_sta_clear_stats(hapd, sta->addr); if (!hapd->conf->radius->acct_server) @@ -251,8 +266,7 @@ int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; struct os_reltime now_r, diff; - struct os_time now; - u32 gigawords; + u64 bytes; if (!hapd->conf->radius->acct_server) return; @@ -266,7 +280,6 @@ } os_get_reltime(&now_r); - os_get_time(&now); os_reltime_sub(&now_r, &sta->acct_session_start, &diff); if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, diff.sec)) { @@ -287,48 +300,42 @@ wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); goto fail; } + if (data.bytes_64bit) + bytes = data.rx_bytes; + else + bytes = ((u64) sta->last_rx_bytes_hi << 32) | + sta->last_rx_bytes_lo; if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS, - data.rx_bytes)) { + (u32) bytes)) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); goto fail; } - gigawords = sta->acct_input_gigawords; -#if __WORDSIZE == 64 - gigawords += data.rx_bytes >> 32; -#endif - if (gigawords && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, - gigawords)) { + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, + (u32) (bytes >> 32))) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); goto fail; } + if (data.bytes_64bit) + bytes = data.tx_bytes; + else + bytes = ((u64) sta->last_tx_bytes_hi << 32) | + sta->last_tx_bytes_lo; if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS, - data.tx_bytes)) { + (u32) bytes)) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); goto fail; } - gigawords = sta->acct_output_gigawords; -#if __WORDSIZE == 64 - gigawords += data.tx_bytes >> 32; -#endif - if (gigawords && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, - gigawords)) { + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, + (u32) (bytes >> 32))) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); goto fail; } } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, - now.sec)) { - wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); - goto fail; - } - if (eloop_terminated()) cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; @@ -375,22 +382,17 @@ eloop_cancel_timeout(accounting_interim_update, hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "stopped accounting session %08X-%08X", - sta->acct_session_id_hi, - sta->acct_session_id_lo); + "stopped accounting session %016llX", + (unsigned long long) sta->acct_session_id); sta->acct_session_started = 0; } } -void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta) +int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { - sta->acct_session_id_lo = hapd->acct_session_id_lo++; - if (hapd->acct_session_id_lo == 0) { - hapd->acct_session_id_hi++; - } - sta->acct_session_id_hi = hapd->acct_session_id_hi; + return radius_gen_session_id((u8 *) &sta->acct_session_id, + sizeof(sta->acct_session_id)); } @@ -437,12 +439,14 @@ if (!msg) return; - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, - RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) - { - wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); - radius_msg_free(msg); - return; + if (hapd->acct_session_id) { + char buf[20]; + + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) hapd->acct_session_id); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) + wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); } if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) @@ -450,6 +454,63 @@ } +static void accounting_interim_error_cb(const u8 *addr, void *ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + unsigned int i, wait_time; + int res; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return; + sta->acct_interim_errors++; + if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) { + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " - too many errors, abandon this interim accounting update", + MAC2STR(addr)); + sta->acct_interim_errors = 0; + /* Next update will be tried after normal update interval */ + return; + } + + /* + * Use a shorter update interval as an improved retransmission mechanism + * for failed interim accounting updates. This allows the statistics to + * be updated for each retransmission. + * + * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT. + * Schedule the first retry attempt immediately and every following one + * with exponential backoff. + */ + if (sta->acct_interim_errors == 1) { + wait_time = 0; + } else { + wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */ + for (i = 1; i < sta->acct_interim_errors; i++) + wait_time *= 2; + } + res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update, + hapd, sta); + if (res == 1) + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u) - schedule next update in %u seconds", + MAC2STR(addr), sta->acct_interim_errors, wait_time); + else if (res == 0) + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u)", MAC2STR(addr), + sta->acct_interim_errors); + else + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u) - no timer found", MAC2STR(addr), + sta->acct_interim_errors); +} + + /** * accounting_init: Initialize accounting * @hapd: hostapd BSS data @@ -457,20 +518,15 @@ */ int accounting_init(struct hostapd_data *hapd) { - struct os_time now; + if (radius_gen_session_id((u8 *) &hapd->acct_session_id, + sizeof(hapd->acct_session_id)) < 0) + return -1; - /* Acct-Session-Id should be unique over reboots. Using a random number - * is preferred. If that is not available, take the current time. Mix - * in microseconds to make this more likely to be unique. */ - os_get_time(&now); - if (os_get_random((u8 *) &hapd->acct_session_id_hi, - sizeof(hapd->acct_session_id_hi)) < 0) - hapd->acct_session_id_hi = now.sec; - hapd->acct_session_id_hi ^= now.usec; - if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) return -1; + radius_client_set_interim_error_cb(hapd->radius, + accounting_interim_error_cb, hapd); accounting_report_state(hapd, 1); --- contrib/wpa/src/ap/accounting.h.orig +++ contrib/wpa/src/ap/accounting.h @@ -10,9 +10,10 @@ #define ACCOUNTING_H #ifdef CONFIG_NO_ACCOUNTING -static inline void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta) +static inline int accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) { + return 0; } static inline void accounting_sta_start(struct hostapd_data *hapd, @@ -34,7 +35,7 @@ { } #else /* CONFIG_NO_ACCOUNTING */ -void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); +int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); int accounting_init(struct hostapd_data *hapd); --- contrib/wpa/src/ap/acs.c.orig +++ contrib/wpa/src/ap/acs.c @@ -13,6 +13,7 @@ #include "utils/common.h" #include "utils/list.h" #include "common/ieee802_11_defs.h" +#include "common/hw_features_common.h" #include "common/wpa_ctrl.h" #include "drivers/driver.h" #include "hostapd.h" @@ -260,7 +261,7 @@ } -static void acs_cleanup(struct hostapd_iface *iface) +void acs_cleanup(struct hostapd_iface *iface) { int i; struct hostapd_channel_data *chan; @@ -314,7 +315,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)); @@ -331,12 +332,10 @@ long double int_factor = 0; unsigned count = 0; - if (dl_list_empty(&chan->survey_list)) + if (dl_list_empty(&chan->survey_list) || + (chan->flag & HOSTAPD_CHAN_DISABLED)) return; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - return; - chan->interference_factor = 0; dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) @@ -359,13 +358,12 @@ (unsigned long) survey->channel_time_rx); } - if (!count) - return; - chan->interference_factor /= count; + if (count) + chan->interference_factor /= count; } -static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) +static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan) { const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; @@ -379,7 +377,7 @@ } -static int acs_usable_vht80_chan(struct hostapd_channel_data *chan) +static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan) { const int allowed[] = { 36, 52, 100, 116, 132, 149 }; unsigned int i; @@ -392,6 +390,19 @@ } +static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan) +{ + const int allowed[] = { 36, 100 }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(allowed); i++) + if (chan->chan == allowed[i]) + return 1; + + return 0; +} + + static int acs_survey_is_sufficient(struct freq_survey *survey) { if (!(survey->filled & SURVEY_HAS_NF)) { @@ -450,13 +461,9 @@ for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - continue; - - if (!acs_survey_list_is_sufficient(chan)) - continue; - - valid++; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + acs_survey_list_is_sufficient(chan)) + valid++; } /* We need at least survey data for one channel */ @@ -466,13 +473,9 @@ static int acs_usable_chan(struct hostapd_channel_data *chan) { - if (dl_list_empty(&chan->survey_list)) - return 0; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - return 0; - if (!acs_survey_list_is_sufficient(chan)) - return 0; - return 1; + return !dl_list_empty(&chan->survey_list) && + !(chan->flag & HOSTAPD_CHAN_DISABLED) && + acs_survey_list_is_sufficient(chan); } @@ -576,6 +579,7 @@ long double factor, ideal_factor = 0; int i, j; int n_chans = 1; + u32 bw; unsigned int k; /* TODO: HT40- support */ @@ -590,18 +594,24 @@ iface->conf->secondary_channel) n_chans = 2; - if (iface->conf->ieee80211ac && - iface->conf->vht_oper_chwidth == 1) - n_chans = 4; + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + n_chans = 4; + break; + case VHT_CHANWIDTH_160MHZ: + n_chans = 8; + break; + } + } - /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ + bw = num_chan_to_bw(n_chans); - wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", - n_chans == 1 ? 20 : - n_chans == 2 ? 40 : - n_chans == 4 ? 80 : - -1); + /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */ + wpa_printf(MSG_DEBUG, + "ACS: Survey analysis for selected bandwidth %d MHz", bw); + for (i = 0; i < iface->current_mode->num_channels; i++) { double total_weight; struct acs_bias *bias, tmp_bias; @@ -608,12 +618,23 @@ chan = &iface->current_mode->channels[i]; - if (chan->flag & HOSTAPD_CHAN_DISABLED) + /* Since in the current ACS implementation the first channel is + * always a primary channel, skip channels not available as + * primary until more sophisticated channel selection is + * implemented. */ + if (!chan_pri_allowed(chan)) continue; if (!is_in_chanlist(iface, chan)) continue; + if (!chan_bw_allowed(chan, bw, 1, 1)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: BW %u is not supported", + chan->chan, bw); + continue; + } + /* HT40 on 5 GHz has a limited set of primary channels as per * 11n Annex J */ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && @@ -626,12 +647,24 @@ } if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && - iface->conf->ieee80211ac && - iface->conf->vht_oper_chwidth == 1 && - !acs_usable_vht80_chan(chan)) { - wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80", - chan->chan); - continue; + iface->conf->ieee80211ac) { + if (iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_80MHZ && + !acs_usable_vht80_chan(chan)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: not allowed as primary channel for VHT80", + chan->chan); + continue; + } + + if (iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_160MHZ && + !acs_usable_vht160_chan(chan)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: not allowed as primary channel for VHT160", + chan->chan); + continue; + } } factor = 0; @@ -644,6 +677,13 @@ if (!adj_chan) break; + if (!chan_bw_allowed(adj_chan, bw, 1, 0)) { + wpa_printf(MSG_DEBUG, + "ACS: PRI Channel %d: secondary channel %d BW %u is not supported", + chan->chan, adj_chan->chan, bw); + break; + } + if (acs_usable_chan(adj_chan)) { factor += adj_chan->interference_factor; total_weight += 1; @@ -756,10 +796,14 @@ case VHT_CHANWIDTH_80MHZ: offset = 6; break; + case VHT_CHANWIDTH_160MHZ: + offset = 14; + break; default: /* TODO: How can this be calculated? Adjust * acs_find_ideal_chan() */ - wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now"); + wpa_printf(MSG_INFO, + "ACS: Only VHT20/40/80/160 is supported now"); return; } @@ -789,10 +833,7 @@ static int acs_study_options(struct hostapd_iface *iface) { - int err; - - err = acs_study_survey_based(iface); - if (err == 0) + if (acs_study_survey_based(iface) == 0) return 0; /* TODO: If no surveys are available/sufficient this is a good @@ -921,22 +962,21 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface) { - int err; - wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) { wpa_printf(MSG_INFO, "ACS: Offloading to driver"); - err = hostapd_drv_do_acs(iface->bss[0]); - if (err) + if (hostapd_drv_do_acs(iface->bss[0])) return HOSTAPD_CHAN_INVALID; return HOSTAPD_CHAN_ACS; } + if (!iface->current_mode) + return HOSTAPD_CHAN_INVALID; + acs_cleanup(iface); - err = acs_request_scan(iface); - if (err < 0) + if (acs_request_scan(iface) < 0) return HOSTAPD_CHAN_INVALID; hostapd_set_state(iface, HAPD_IFACE_ACS); --- contrib/wpa/src/ap/acs.h.orig +++ contrib/wpa/src/ap/acs.h @@ -13,6 +13,7 @@ #ifdef CONFIG_ACS enum hostapd_chan_status acs_init(struct hostapd_iface *iface); +void acs_cleanup(struct hostapd_iface *iface); #else /* CONFIG_ACS */ @@ -22,6 +23,10 @@ return HOSTAPD_CHAN_INVALID; } +static inline void acs_cleanup(struct hostapd_iface *iface) +{ +} + #endif /* CONFIG_ACS */ #endif /* ACS_H */ --- contrib/wpa/src/ap/ap_config.c.orig +++ contrib/wpa/src/ap/ap_config.c @@ -10,9 +10,11 @@ #include "utils/common.h" #include "crypto/sha1.h" +#include "crypto/tls.h" #include "radius/radius_client.h" #include "common/ieee802_11_defs.h" #include "common/eapol_common.h" +#include "common/dhcp.h" #include "eap_common/eap_wsc_common.h" #include "eap_server/eap.h" #include "wpa_auth.h" @@ -36,8 +38,14 @@ } +#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES +#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0 +#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */ + void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) { + dl_list_init(&bss->anqp_elem); + bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; bss->logger_syslog = (unsigned int) -1; @@ -53,6 +61,10 @@ bss->wpa_group_rekey = 600; bss->wpa_gmk_rekey = 86400; + bss->wpa_group_update_count = 4; + bss->wpa_pairwise_update_count = 4; + bss->wpa_disable_eapol_key_retries = + DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES; bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = WPA_CIPHER_TKIP; bss->wpa_group = WPA_CIPHER_TKIP; @@ -63,6 +75,7 @@ bss->dtim_period = 2; bss->radius_server_auth_port = 1812; + bss->eap_sim_db_timeout = 1; bss->ap_max_inactivity = AP_MAX_INACTIVITY; bss->eapol_version = EAPOL_VERSION; @@ -85,13 +98,48 @@ /* Set to -1 as defaults depends on HT in setup */ bss->wmm_enabled = -1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP bss->ft_over_ds = 1; -#endif /* CONFIG_IEEE80211R */ + bss->rkh_pos_timeout = 86400; + bss->rkh_neg_timeout = 60; + bss->rkh_pull_timeout = 1000; + bss->rkh_pull_retries = 4; + bss->r0_key_lifetime = 1209600; +#endif /* CONFIG_IEEE80211R_AP */ bss->radius_das_time_window = 300; bss->sae_anti_clogging_threshold = 5; + bss->sae_sync = 5; + + bss->gas_frag_limit = 1400; + +#ifdef CONFIG_FILS + dl_list_init(&bss->fils_realms); + bss->fils_hlp_wait_time = 30; + bss->dhcp_server_port = DHCP_SERVER_PORT; + bss->dhcp_relay_port = DHCP_SERVER_PORT; +#endif /* CONFIG_FILS */ + + bss->broadcast_deauth = 1; + +#ifdef CONFIG_MBO + bss->mbo_cell_data_conn_pref = -1; +#endif /* CONFIG_MBO */ + + /* Disable TLS v1.3 by default for now to avoid interoperability issue. + * This can be enabled by default once the implementation has been fully + * completed and tested with other implementations. */ + bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3; + + bss->send_probe_response = 1; + +#ifdef CONFIG_HS20 + bss->hs20_release = (HS20_VERSION >> 4) + 1; +#endif /* CONFIG_HS20 */ + + /* Default to strict CRL checking. */ + bss->check_crl_strict = 1; } @@ -152,9 +200,8 @@ conf->num_bss = 1; conf->beacon_int = 100; - conf->rts_threshold = -1; /* use driver default: 2347 */ - conf->fragm_threshold = -1; /* user driver default: 2346 */ - conf->send_probe_response = 1; + conf->rts_threshold = -2; /* use driver default: 2347 */ + conf->fragm_threshold = -2; /* user driver default: 2346 */ /* Set to invalid value means do not add Power Constraint IE */ conf->local_pwr_constraint = -1; @@ -180,6 +227,7 @@ conf->ignore_assoc_probability = 0.0; conf->ignore_reassoc_probability = 0.0; conf->corrupt_gtk_rekey_mic_probability = 0.0; + conf->ecsa_ie_only = 0; #endif /* CONFIG_TESTING_OPTIONS */ conf->acs = 0; @@ -188,6 +236,14 @@ conf->acs_num_scans = 5; #endif /* CONFIG_ACS */ + /* The third octet of the country string uses an ASCII space character + * by default to indicate that the regulations encompass all + * environments for the current frequency band in the country. */ + conf->country[2] = ' '; + + conf->rssi_reject_assoc_rssi = 0; + conf->rssi_reject_assoc_timeout = 30; + return conf; } @@ -198,18 +254,17 @@ } -int hostapd_mac_comp_empty(const void *a) -{ - macaddr empty = { 0 }; - return os_memcmp(a, empty, sizeof(macaddr)); -} - - static int hostapd_config_read_wpa_psk(const char *fname, struct hostapd_ssid *ssid) { FILE *f; char buf[128], *pos; + const char *keyid; + char *context; + char *context2; + char *token; + char *name; + char *value; int line = 0, ret = 0, len, ok; u8 addr[ETH_ALEN]; struct hostapd_wpa_psk *psk; @@ -224,6 +279,8 @@ } while (fgets(buf, sizeof(buf), f)) { + int vlan_id = 0; + line++; if (buf[0] == '#') @@ -239,9 +296,39 @@ if (buf[0] == '\0') continue; - if (hwaddr_aton(buf, addr)) { + context = NULL; + keyid = NULL; + while ((token = str_token(buf, " ", &context))) { + if (!os_strchr(token, '=')) + break; + context2 = NULL; + name = str_token(token, "=", &context2); + if (!name) + break; + value = str_token(token, "", &context2); + if (!value) + value = ""; + if (!os_strcmp(name, "keyid")) { + keyid = value; + } else if (!os_strcmp(name, "vlanid")) { + vlan_id = atoi(value); + } else { + wpa_printf(MSG_ERROR, + "Unrecognized '%s=%s' on line %d in '%s'", + name, value, line, fname); + ret = -1; + break; + } + } + + if (ret == -1) + break; + + if (!token) + token = ""; + if (hwaddr_aton(token, addr)) { wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " - "line %d in '%s'", buf, line, fname); + "line %d in '%s'", token, line, fname); ret = -1; break; } @@ -252,13 +339,14 @@ ret = -1; break; } + psk->vlan_id = vlan_id; if (is_zero_ether_addr(addr)) psk->group = 1; else os_memcpy(psk->addr, addr, ETH_ALEN); - pos = buf + 17; - if (*pos == '\0') { + pos = str_token(buf, "", &context); + if (!pos) { wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", line, fname); os_free(psk); @@ -265,7 +353,6 @@ ret = -1; break; } - pos++; ok = 0; len = os_strlen(pos); @@ -284,6 +371,18 @@ break; } + if (keyid) { + len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid)); + if ((size_t) len >= sizeof(psk->keyid)) { + wpa_printf(MSG_ERROR, + "PSK keyid too long on line %d in '%s'", + line, fname); + os_free(psk); + ret = -1; + break; + } + } + psk->next = ssid->wpa_psk; ssid->wpa_psk = psk; } @@ -332,13 +431,7 @@ ssid->wpa_psk->group = 1; } - if (ssid->wpa_psk_file) { - if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, - &conf->ssid)) - return -1; - } - - return 0; + return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid); } @@ -383,10 +476,23 @@ hostapd_config_free_radius_attr(user->accept_attr); os_free(user->identity); bin_clear_free(user->password, user->password_len); + bin_clear_free(user->salt, user->salt_len); os_free(user); } +void hostapd_config_free_eap_users(struct hostapd_eap_user *user) +{ + struct hostapd_eap_user *prev_user; + + while (user) { + prev_user = user; + user = user->next; + hostapd_config_free_eap_user(prev_user); + } +} + + static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) { int i; @@ -410,10 +516,51 @@ } +static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf) +{ + struct anqp_element *elem; + + while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element, + list))) { + dl_list_del(&elem->list); + wpabuf_free(elem->payload); + os_free(elem); + } +} + + +static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf) +{ +#ifdef CONFIG_FILS + struct fils_realm *realm; + + while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm, + list))) { + dl_list_del(&realm->list); + os_free(realm); + } +#endif /* CONFIG_FILS */ +} + + +static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf) +{ + struct sae_password_entry *pw, *tmp; + + pw = conf->sae_passwords; + conf->sae_passwords = NULL; + while (pw) { + tmp = pw; + pw = pw->next; + str_clear_free(tmp->password); + os_free(tmp->identifier); + os_free(tmp); + } +} + + void hostapd_config_free_bss(struct hostapd_bss_config *conf) { - struct hostapd_eap_user *user, *prev_user; - if (conf == NULL) return; @@ -426,12 +573,7 @@ os_free(conf->ssid.vlan_tagged_interface); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - user = conf->eap_user; - while (user) { - prev_user = user; - user = user->next; - hostapd_config_free_eap_user(prev_user); - } + hostapd_config_free_eap_users(conf->eap_user); os_free(conf->eap_user_sqlite); os_free(conf->eap_req_id_text); @@ -453,9 +595,12 @@ os_free(conf->server_cert); os_free(conf->private_key); os_free(conf->private_key_passwd); + os_free(conf->check_cert_subject); os_free(conf->ocsp_stapling_response); + os_free(conf->ocsp_stapling_response_multi); os_free(conf->dh_file); os_free(conf->openssl_ciphers); + os_free(conf->openssl_ecdh_curves); os_free(conf->pac_opaque_encr_key); os_free(conf->eap_fast_a_id); os_free(conf->eap_fast_a_id_info); @@ -466,7 +611,7 @@ hostapd_config_free_vlan(conf); os_free(conf->time_zone); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP { struct ft_remote_r0kh *r0kh, *r0kh_prev; struct ft_remote_r1kh *r1kh, *r1kh_prev; @@ -487,7 +632,7 @@ os_free(r1kh_prev); } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_WPS os_free(conf->wps_pin_requests); @@ -500,6 +645,8 @@ os_free(conf->ap_pin); os_free(conf->extra_cred); os_free(conf->ap_settings); + hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk); + str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase); os_free(conf->upnp_iface); os_free(conf->friendly_name); os_free(conf->manufacturer_url); @@ -519,10 +666,12 @@ 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); os_free(conf->domain_name); + hostapd_config_free_anqp_elem(conf); #ifdef CONFIG_RADIUS_TEST os_free(conf->dump_msk_file); @@ -547,16 +696,31 @@ os_free(p->icons[j]); os_free(p->icons); os_free(p->osu_nai); + os_free(p->osu_nai2); os_free(p->service_desc); } os_free(conf->hs20_osu_providers); } + if (conf->hs20_operator_icon) { + size_t i; + + for (i = 0; i < conf->hs20_operator_icon_count; i++) + os_free(conf->hs20_operator_icon[i]); + os_free(conf->hs20_operator_icon); + } os_free(conf->subscr_remediation_url); + os_free(conf->hs20_sim_provisioning_url); + os_free(conf->t_c_filename); + os_free(conf->t_c_server_url); #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); + wpabuf_free(conf->assocresp_elements); os_free(conf->sae_groups); +#ifdef CONFIG_OWE + os_free(conf->owe_groups); +#endif /* CONFIG_OWE */ os_free(conf->wowlan_triggers); @@ -564,11 +728,22 @@ #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(conf->own_ie_override); + wpabuf_free(conf->sae_commit_override); #endif /* CONFIG_TESTING_OPTIONS */ os_free(conf->no_probe_resp_if_seen_on); os_free(conf->no_auth_if_seen_on); + hostapd_config_free_fils_realms(conf); + +#ifdef CONFIG_DPP + os_free(conf->dpp_connector); + wpabuf_free(conf->dpp_netaccesskey); + wpabuf_free(conf->dpp_csign); +#endif /* CONFIG_DPP */ + + hostapd_config_free_sae_passwords(conf); + os_free(conf); } @@ -594,6 +769,8 @@ #ifdef CONFIG_ACS os_free(conf->acs_chan_bias); #endif /* CONFIG_ACS */ + wpabuf_free(conf->lci); + wpabuf_free(conf->civic); os_free(conf); } @@ -610,7 +787,7 @@ * Perform a binary search for given MAC address from a pre-sorted list. */ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, - const u8 *addr, int *vlan_id) + const u8 *addr, struct vlan_description *vlan_id) { int start, end, middle, res; @@ -650,11 +827,26 @@ } -int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id) +int hostapd_vlan_valid(struct hostapd_vlan *vlan, + struct vlan_description *vlan_desc) { struct hostapd_vlan *v = vlan; + int i; + + if (!vlan_desc->notempty || vlan_desc->untagged < 0 || + vlan_desc->untagged > MAX_VLAN_ID) + return 0; + for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { + if (vlan_desc->tagged[i] < 0 || + vlan_desc->tagged[i] > MAX_VLAN_ID) + return 0; + } + if (!vlan_desc->untagged && !vlan_desc->tagged[0]) + return 0; + while (v) { - if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + if (!vlan_compare(&v->vlan_desc, vlan_desc) || + v->vlan_id == VLAN_ID_WILDCARD) return 1; v = v->next; } @@ -676,11 +868,14 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk) + const u8 *prev_psk, int *vlan_id) { struct hostapd_wpa_psk *psk; int next_ok = prev_psk == NULL; + if (vlan_id) + *vlan_id = 0; + if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) { wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR " p2p_dev_addr=" MACSTR " prev_psk=%p", @@ -698,8 +893,11 @@ (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) || (!addr && p2p_dev_addr && os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == - 0))) + 0))) { + if (vlan_id) + *vlan_id = psk->vlan_id; return psk->psk; + } if (psk->psk == prev_psk) next_ok = 1; @@ -756,7 +954,7 @@ return -1; } - if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) { + if (full_config && !is_zero_ether_addr(bss->bssid)) { size_t i; for (i = 0; i < conf->num_bss; i++) { @@ -772,7 +970,7 @@ } } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && (bss->nas_identifier == NULL || os_strlen(bss->nas_identifier) < 1 || @@ -782,7 +980,7 @@ "string"); return -1; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211N if (full_config && conf->ieee80211n && @@ -811,6 +1009,25 @@ } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (full_config && conf->ieee80211ac && + bss->ssid.security_policy == SECURITY_STATIC_WEP) { + bss->disable_11ac = 1; + wpa_printf(MSG_ERROR, + "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities"); + } + + if (full_config && conf->ieee80211ac && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) + { + bss->disable_11ac = 1; + wpa_printf(MSG_ERROR, + "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities"); + } +#endif /* CONFIG_IEEE80211AC */ + #ifdef CONFIG_WPS if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " @@ -827,7 +1044,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; @@ -847,6 +1066,24 @@ } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (full_config && bss->mbo_enabled && (bss->wpa & 2) && + bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_ERROR, + "MBO: PMF needs to be enabled whenever using WPA2 with MBO"); + return -1; + } +#endif /* CONFIG_MBO */ + +#ifdef CONFIG_OCV + if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION && + bss->ocv) { + wpa_printf(MSG_ERROR, + "OCV: PMF needs to be enabled whenever using OCV"); + return -1; + } +#endif /* CONFIG_OCV */ + return 0; } @@ -928,8 +1165,15 @@ if ((bss->wpa & 2) && bss->rsn_pairwise == 0) bss->rsn_pairwise = bss->wpa_pairwise; - bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, - bss->rsn_pairwise); + if (bss->group_cipher) + bss->wpa_group = bss->group_cipher; + else + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, + bss->wpa_pairwise, + bss->rsn_pairwise); + if (!bss->wpa_group_rekey_set) + bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ? + 600 : 86400; if (full_config) { bss->radius->auth_server = bss->radius->auth_servers; @@ -983,3 +1227,26 @@ } } } + + +int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf) +{ + int with_id = 0, without_id = 0; + struct sae_password_entry *pw; + + if (conf->ssid.wpa_passphrase) + without_id = 1; + + for (pw = conf->sae_passwords; pw; pw = pw->next) { + if (pw->identifier) + with_id = 1; + else + without_id = 1; + if (with_id && without_id) + break; + } + + if (with_id && !without_id) + return 2; + return with_id; +} --- contrib/wpa/src/ap/ap_config.h.orig +++ contrib/wpa/src/ap/ap_config.h @@ -10,6 +10,7 @@ #define HOSTAPD_CONFIG_H #include "common/defs.h" +#include "utils/list.h" #include "ip_addr.h" #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" @@ -16,6 +17,7 @@ #include "common/ieee802_11_common.h" #include "wps/wps.h" #include "fst/fst.h" +#include "vlan.h" /** * mesh_conf - local MBSS state and settings @@ -39,6 +41,11 @@ #define MESH_CONF_SEC_AUTH BIT(1) #define MESH_CONF_SEC_AMPE BIT(2) unsigned int security; + enum mfp_options ieee80211w; + int ocv; + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int mgmt_group_cipher; int dot11MeshMaxRetries; int dot11MeshRetryTimeout; /* msec */ int dot11MeshConfirmTimeout; /* msec */ @@ -52,7 +59,7 @@ struct mac_acl_entry { macaddr addr; - int vlan_id; + struct vlan_description vlan_id; }; struct hostapd_radius_servers; @@ -102,6 +109,7 @@ #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 #define DYNAMIC_VLAN_NAMING_END 2 int vlan_naming; + int per_sta_vif; #ifdef CONFIG_FULL_DYNAMIC_VLAN char *vlan_tagged_interface; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -113,7 +121,9 @@ struct hostapd_vlan { struct hostapd_vlan *next; int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + struct vlan_description vlan_desc; char ifname[IFNAMSIZ + 1]; + char bridge[IFNAMSIZ + 1]; int configured; int dynamic_vlan; #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -124,17 +134,25 @@ }; #define PMK_LEN 32 +#define KEYID_LEN 32 +#define MIN_PASSPHRASE_LEN 8 +#define MAX_PASSPHRASE_LEN 63 struct hostapd_sta_wpa_psk_short { struct hostapd_sta_wpa_psk_short *next; + unsigned int is_passphrase:1; u8 psk[PMK_LEN]; + char passphrase[MAX_PASSPHRASE_LEN + 1]; + int ref; /* (number of references held) - 1 */ }; struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; int group; + char keyid[KEYID_LEN]; u8 psk[PMK_LEN]; u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + int vlan_id; }; struct hostapd_eap_user { @@ -147,6 +165,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; @@ -156,6 +176,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 { @@ -188,6 +209,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 @@ -205,6 +232,26 @@ } eap_method[MAX_NAI_EAP_METHODS]; }; +struct anqp_element { + struct dl_list list; + u16 infoid; + struct wpabuf *payload; +}; + +struct fils_realm { + struct dl_list list; + u8 hash[2]; + char realm[]; +}; + +struct sae_password_entry { + struct sae_password_entry *next; + char *password; + char *identifier; + u8 peer_addr[ETH_ALEN]; + int vlan_id; +}; + /** * struct hostapd_bss_config - Per-BSS configuration */ @@ -222,7 +269,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; @@ -231,6 +279,7 @@ struct hostapd_eap_user *eap_user; char *eap_user_sqlite; char *eap_sim_db; + unsigned int eap_sim_db_timeout; int eap_server_erp; /* Whether ERP is enabled on internal EAP server */ struct hostapd_ip_addr own_ip_addr; char *nas_identifier; @@ -242,6 +291,7 @@ int radius_das_port; unsigned int radius_das_time_window; int radius_das_require_event_timestamp; + int radius_das_require_message_authenticator; struct hostapd_ip_addr radius_das_client_addr; u8 *radius_das_shared_secret; size_t radius_das_shared_secret_len; @@ -265,7 +315,7 @@ char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast * frames */ - enum { + enum macaddr_acl { ACCEPT_UNLESS_DENIED = 0, DENY_UNLESS_ACCEPTED = 1, USE_EXTERNAL_RADIUS_AUTH = 2 @@ -291,6 +341,9 @@ /* dot11AssociationSAQueryRetryTimeout (in TUs) */ int assoc_sa_query_retry_timeout; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + int ocv; /* Operating Channel Validation */ +#endif /* CONFIG_OCV */ enum { PSK_RADIUS_IGNORED = 0, PSK_RADIUS_ACCEPTED = 1, @@ -297,27 +350,37 @@ PSK_RADIUS_REQUIRED = 2 } wpa_psk_radius; int wpa_pairwise; + int group_cipher; /* wpa_group value override from configuation */ int wpa_group; int wpa_group_rekey; + int wpa_group_rekey_set; int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; + u32 wpa_group_update_count; + u32 wpa_pairwise_update_count; + int wpa_disable_eapol_key_retries; int rsn_pairwise; int rsn_preauth; char *rsn_preauth_interfaces; - int peerkey; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP /* IEEE 802.11r - Fast BSS Transition */ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r1_key_holder[FT_R1KH_ID_LEN]; - u32 r0_key_lifetime; + u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */ + int rkh_pos_timeout; + int rkh_neg_timeout; + int rkh_pull_timeout; /* ms */ + int rkh_pull_retries; u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; int ft_over_ds; -#endif /* CONFIG_IEEE80211R */ + int ft_psk_generate_local; + int r1_max_key_lifetime; +#endif /* CONFIG_IEEE80211R_AP */ char *ctrl_interface; /* directory for UNIX domain sockets */ #ifndef CONFIG_NATIVE_WINDOWS @@ -329,11 +392,17 @@ char *server_cert; char *private_key; char *private_key_passwd; + char *check_cert_subject; int check_crl; + int check_crl_strict; + unsigned int crl_reload_interval; unsigned int tls_session_lifetime; + unsigned int tls_flags; char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; char *dh_file; char *openssl_ciphers; + char *openssl_ecdh_curves; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -358,6 +427,7 @@ int ap_max_inactivity; int ignore_broadcast_ssid; + int no_probe_resp_if_max_sta; int wmm_enabled; int wmm_uapsd; @@ -395,9 +465,11 @@ u8 *extra_cred; size_t extra_cred_len; int wps_cred_processing; + int wps_cred_add_sae; int force_per_enrollee_psk; u8 *ap_settings; size_t ap_settings_len; + struct hostapd_ssid multi_ap_backhaul_ssid; char *upnp_iface; char *friendly_name; char *manufacturer_url; @@ -440,6 +512,7 @@ int time_advertisement; char *time_zone; int wnm_sleep_mode; + int wnm_sleep_mode_no_keys; int bss_transition; /* IEEE 802.11u - Interworking */ @@ -462,6 +535,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; @@ -481,8 +558,11 @@ unsigned int nai_realm_count; struct hostapd_nai_realm_data *nai_realm_data; + struct dl_list anqp_elem; /* list of struct anqp_element */ + u16 gas_comeback_delay; - int gas_frag_limit; + size_t gas_frag_limit; + int gas_address3; u8 qos_map_set[16 + 2 * 21]; unsigned int qos_map_set_len; @@ -492,6 +572,7 @@ int na_mcast_to_ucast; #ifdef CONFIG_HS20 int hs20; + int hs20_release; int disable_dgaf; u16 anqp_domain_id; unsigned int hs20_oper_friendly_name_count; @@ -520,13 +601,21 @@ char **icons; size_t icons_count; char *osu_nai; + char *osu_nai2; unsigned int service_desc_count; struct hostapd_lang_string *service_desc; } *hs20_osu_providers, *last_osu; size_t hs20_osu_providers_count; + size_t hs20_osu_providers_nai_count; + char **hs20_operator_icon; + size_t hs20_operator_icon_count; unsigned int hs20_deauth_req_timeout; char *subscr_remediation_url; u8 subscr_remediation_method; + char *hs20_sim_provisioning_url; + 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_*) */ @@ -536,9 +625,13 @@ #endif /* CONFIG_RADIUS_TEST */ struct wpabuf *vendor_elements; + struct wpabuf *assocresp_elements; unsigned int sae_anti_clogging_threshold; + unsigned int sae_sync; + int sae_require_mfp; int *sae_groups; + struct sae_password_entry *sae_passwords; char *wowlan_triggers; /* Wake-on-WLAN triggers */ @@ -546,21 +639,98 @@ u8 bss_load_test[5]; u8 bss_load_test_set; struct wpabuf *own_ie_override; + int sae_reflection_attack; + struct wpabuf *sae_commit_override; #endif /* CONFIG_TESTING_OPTIONS */ #define MESH_ENABLED BIT(0) int mesh; - int radio_measurements; + u8 radio_measurements[RRM_CAPABILITIES_IE_LEN]; int vendor_vht; + int use_sta_nsts; char *no_probe_resp_if_seen_on; char *no_auth_if_seen_on; + + int pbss; + +#ifdef CONFIG_MBO + int mbo_enabled; + /** + * oce - Enable OCE in AP and/or STA-CFON mode + * - BIT(0) is Reserved + * - Set BIT(1) to enable OCE in STA-CFON mode + * - Set BIT(2) to enable OCE in AP mode + */ + unsigned int oce; + int mbo_cell_data_conn_pref; +#endif /* CONFIG_MBO */ + + int ftm_responder; + int ftm_initiator; + +#ifdef CONFIG_FILS + u8 fils_cache_id[FILS_CACHE_ID_LEN]; + int fils_cache_id_set; + struct dl_list fils_realms; /* list of struct fils_realm */ + int fils_dh_group; + struct hostapd_ip_addr dhcp_server; + int dhcp_rapid_commit_proxy; + unsigned int fils_hlp_wait_time; + u16 dhcp_server_port; + u16 dhcp_relay_port; +#endif /* CONFIG_FILS */ + + int multicast_to_unicast; + + int broadcast_deauth; + +#ifdef CONFIG_DPP + char *dpp_connector; + struct wpabuf *dpp_netaccesskey; + unsigned int dpp_netaccesskey_expiry; + struct wpabuf *dpp_csign; +#endif /* CONFIG_DPP */ + +#ifdef CONFIG_OWE + macaddr owe_transition_bssid; + u8 owe_transition_ssid[SSID_MAX_LEN]; + size_t owe_transition_ssid_len; + char owe_transition_ifname[IFNAMSIZ + 1]; + int *owe_groups; +#endif /* CONFIG_OWE */ + + int coloc_intf_reporting; + + u8 send_probe_response; + +#define BACKHAUL_BSS 1 +#define FRONTHAUL_BSS 2 + int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */ }; +/** + * struct he_phy_capabilities_info - HE PHY capabilities + */ +struct he_phy_capabilities_info { + Boolean he_su_beamformer; + Boolean he_su_beamformee; + Boolean he_mu_beamformer; +}; /** + * struct he_operation - HE operation + */ +struct he_operation { + u8 he_bss_color; + u8 he_default_pe_duration; + u8 he_twt_required; + u8 he_rts_threshold; +}; + +/** * struct hostapd_config - Per-radio interface configuration */ struct hostapd_config { @@ -570,10 +740,10 @@ u16 beacon_int; int rts_threshold; int fragm_threshold; - u8 send_probe_response; u8 channel; u8 acs; struct wpa_freq_range_list acs_ch_list; + int acs_exclude_dfs; enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ enum { LONG_PREAMBLE = 0, @@ -582,6 +752,8 @@ int *supported_rates; int *basic_rates; + unsigned int beacon_rate; + enum beacon_rate_type rate_type; const struct wpa_driver_ops *driver; char *driver_params; @@ -597,6 +769,9 @@ * ' ' (ascii 32): all environments * 'O': Outdoor environemnt only * 'I': Indoor environment only + * 'X': Used with noncountry entity ("XXX") + * 0x00..0x31: identifying IEEE 802.11 standard + * Annex E table (0x04 = global table) */ int ieee80211d; @@ -637,7 +812,11 @@ u8 vht_oper_chwidth; u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; + u8 ht40_plus_minus_allowed; + /* Use driver-generated interface addresses when adding multiple BSSs */ + u8 use_driver_iface_addr; + #ifdef CONFIG_FST struct fst_iface_cfg fst_cfg; #endif /* CONFIG_FST */ @@ -652,6 +831,7 @@ double ignore_assoc_probability; double ignore_reassoc_probability; double corrupt_gtk_rekey_mic_probability; + int ecsa_ie_only; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_ACS @@ -662,25 +842,45 @@ } *acs_chan_bias; unsigned int num_acs_chan_bias; #endif /* CONFIG_ACS */ + + struct wpabuf *lci; + struct wpabuf *civic; + int stationary_ap; + + int ieee80211ax; +#ifdef CONFIG_IEEE80211AX + struct he_phy_capabilities_info he_phy_capab; + struct he_operation he_op; + struct ieee80211_he_mu_edca_parameter_set he_mu_edca; +#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; + + int rssi_reject_assoc_rssi; + int rssi_reject_assoc_timeout; }; int hostapd_mac_comp(const void *a, const void *b); -int hostapd_mac_comp_empty(const void *a); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); void hostapd_config_free_eap_user(struct hostapd_eap_user *user); +void hostapd_config_free_eap_users(struct hostapd_eap_user *user); void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); void hostapd_config_free_bss(struct hostapd_bss_config *conf); void hostapd_config_free(struct hostapd_config *conf); int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, - const u8 *addr, int *vlan_id); + const u8 *addr, struct vlan_description *vlan_id); int hostapd_rate_found(int *list, int rate); const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk); + const u8 *prev_psk, int *vlan_id); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); -int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id); +int hostapd_vlan_valid(struct hostapd_vlan *vlan, + struct vlan_description *vlan_desc); const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id); struct hostapd_radius_attr * @@ -688,5 +888,6 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config); void hostapd_set_security_params(struct hostapd_bss_config *bss, int full_config); +int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf); #endif /* HOSTAPD_CONFIG_H */ --- contrib/wpa/src/ap/ap_drv_ops.c.orig +++ contrib/wpa/src/ap/ap_drv_ops.c @@ -19,6 +19,7 @@ #include "ap_config.h" #include "p2p_hostapd.h" #include "hs20.h" +#include "wpa_auth.h" #include "ap_drv_ops.h" @@ -33,10 +34,36 @@ res |= WPA_STA_SHORT_PREAMBLE; if (flags & WLAN_STA_MFP) res |= WPA_STA_MFP; + if (flags & WLAN_STA_AUTH) + res |= WPA_STA_AUTHENTICATED; + if (flags & WLAN_STA_ASSOC) + res |= WPA_STA_ASSOCIATED; return res; } +static int add_buf(struct wpabuf **dst, const struct wpabuf *src) +{ + if (!src) + return 0; + if (wpabuf_resize(dst, wpabuf_len(src)) != 0) + return -1; + wpabuf_put_buf(*dst, src); + return 0; +} + + +static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len) +{ + if (!data || !len) + return 0; + if (wpabuf_resize(dst, len) != 0) + return -1; + wpabuf_put_data(*dst, data, len); + return 0; +} + + int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf **beacon_ret, struct wpabuf **proberesp_ret, @@ -49,82 +76,45 @@ pos = buf; pos = hostapd_eid_time_adv(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); - } + if (add_buf_data(&beacon, buf, pos - buf) < 0) + goto fail; pos = hostapd_eid_time_zone(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } + if (add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; pos = buf; pos = hostapd_eid_ext_capab(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&assocresp, pos - buf) != 0) - goto fail; - wpabuf_put_data(assocresp, buf, pos - buf); - } + if (add_buf_data(&assocresp, buf, pos - buf) < 0) + goto fail; pos = hostapd_eid_interworking(hapd, pos); pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } - #ifdef CONFIG_FST - if (hapd->iface->fst_ies) { - size_t add = wpabuf_len(hapd->iface->fst_ies); - - if (wpabuf_resize(&beacon, add) < 0) - goto fail; - wpabuf_put_buf(beacon, hapd->iface->fst_ies); - if (wpabuf_resize(&proberesp, add) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->iface->fst_ies); - if (wpabuf_resize(&assocresp, add) < 0) - goto fail; - wpabuf_put_buf(assocresp, hapd->iface->fst_ies); - } + if (add_buf(&beacon, hapd->iface->fst_ies) < 0 || + add_buf(&proberesp, hapd->iface->fst_ies) < 0 || + add_buf(&assocresp, hapd->iface->fst_ies) < 0) + goto fail; #endif /* CONFIG_FST */ - if (hapd->wps_beacon_ie) { - if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < - 0) - goto fail; - wpabuf_put_buf(beacon, hapd->wps_beacon_ie); - } +#ifdef CONFIG_FILS + pos = hostapd_eid_fils_indic(hapd, buf, 0); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_FILS */ - if (hapd->wps_probe_resp_ie) { - if (wpabuf_resize(&proberesp, - wpabuf_len(hapd->wps_probe_resp_ie)) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); - } + if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 || + add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0) + goto fail; #ifdef CONFIG_P2P - if (hapd->p2p_beacon_ie) { - if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < - 0) - goto fail; - wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); - } - - if (hapd->p2p_probe_resp_ie) { - if (wpabuf_resize(&proberesp, - wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); - } + if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 || + add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0) + goto fail; #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER @@ -148,8 +138,7 @@ #ifdef CONFIG_WPS if (hapd->conf->wps_state) { struct wpabuf *a = wps_build_assoc_resp_ie(); - if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) - wpabuf_put_buf(assocresp, a); + add_buf(&assocresp, a); wpabuf_free(a); } #endif /* CONFIG_WPS */ @@ -169,45 +158,45 @@ if (hapd->p2p_group) { struct wpabuf *a; a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); - if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) - wpabuf_put_buf(assocresp, a); + add_buf(&assocresp, a); wpabuf_free(a); } #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_HS20 - pos = buf; - pos = hostapd_eid_hs20_indication(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); + pos = hostapd_eid_hs20_indication(hapd, buf); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } - pos = hostapd_eid_osen(hapd, buf); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_HS20 */ - if (wpabuf_resize(&proberesp, pos - buf) != 0) +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled || + OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { + pos = hostapd_eid_mbo(hapd, buf, sizeof(buf)); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0 || + add_buf_data(&assocresp, buf, pos - buf) < 0) goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); } -#endif /* CONFIG_HS20 */ +#endif /* CONFIG_MBO */ - if (hapd->conf->vendor_elements) { - size_t add = wpabuf_len(hapd->conf->vendor_elements); - if (wpabuf_resize(&beacon, add) == 0) - wpabuf_put_buf(beacon, hapd->conf->vendor_elements); - if (wpabuf_resize(&proberesp, add) == 0) - wpabuf_put_buf(proberesp, hapd->conf->vendor_elements); - } +#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); + *beacon_ret = beacon; *proberesp_ret = proberesp; *assocresp_ret = assocresp; @@ -367,10 +356,44 @@ int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, u16 seq, u16 status, const u8 *ie, size_t len) { + struct wpa_driver_sta_auth_params params; +#ifdef CONFIG_FILS + struct sta_info *sta; +#endif /* CONFIG_FILS */ + if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) return 0; - return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, - seq, status, ie, len); + + os_memset(¶ms, 0, sizeof(params)); + +#ifdef CONFIG_FILS + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for sta_auth processing", + MAC2STR(addr)); + return 0; + } + + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + params.fils_auth = 1; + wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce, + params.fils_snonce, + params.fils_kek, + ¶ms.fils_kek_len); + } +#endif /* CONFIG_FILS */ + + params.own_addr = hapd->own_addr; + params.addr = addr; + params.seq = seq; + params.status = status; + params.ie = ie; + params.len = len; + + return hapd->driver->sta_auth(hapd->drv_priv, ¶ms); } @@ -390,7 +413,8 @@ u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u32 flags, u8 qosinfo, u8 vht_opmode) + u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, + int set) { struct hostapd_sta_add_params params; @@ -412,6 +436,8 @@ params.vht_opmode = vht_opmode; params.flags = hostapd_sta_flags_to_drv(flags); params.qosinfo = qosinfo; + params.support_p2p_ps = supp_p2p_ps; + params.set = set; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); } @@ -468,7 +494,7 @@ return -1; return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, bss_ctx, drv_priv, force_ifname, if_addr, - bridge, use_existing); + bridge, use_existing, 1); } @@ -578,13 +604,13 @@ struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, - u16 *flags) + u16 *flags, u8 *dfs_domain) { if (hapd->driver == NULL || hapd->driver->get_hw_feature_data == NULL) return NULL; return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, - flags); + flags, dfs_domain); } @@ -647,9 +673,21 @@ int hostapd_drv_send_mlme(struct hostapd_data *hapd, const void *msg, size_t len, int noack) { + if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv) + return 0; + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, + NULL, 0); +} + + +int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd, + const void *msg, size_t len, int noack, + const u16 *csa_offs, size_t csa_offs_len) +{ if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0); + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, + csa_offs, csa_offs_len); } @@ -656,7 +694,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason) { - if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv) return 0; return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, reason); @@ -666,7 +704,7 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) { - if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv) return 0; return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, reason); @@ -687,6 +725,45 @@ unsigned int wait, const u8 *dst, const u8 *data, size_t len) { + const u8 *bssid; + const u8 wildcard_bssid[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv) + return 0; + bssid = hapd->own_addr; + if (!is_multicast_ether_addr(dst) && + len > 0 && data[0] == WLAN_ACTION_PUBLIC) { + struct sta_info *sta; + + /* + * Public Action frames to a STA that is not a member of the BSS + * shall use wildcard BSSID value. + */ + 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); +} + + +int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, + unsigned int freq, + unsigned int wait, const u8 *dst, + const u8 *data, size_t len) +{ if (hapd->driver == NULL || hapd->driver->send_action == NULL) return 0; return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, @@ -736,7 +813,7 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len) { - if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) + if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv) return 0; return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, qos_map_set_len); @@ -756,12 +833,28 @@ if ((acs_ch_list_all || freq_range_list_includes(&hapd->iface->conf->acs_ch_list, chan->chan)) && - !(chan->flag & HOSTAPD_CHAN_DISABLED)) + !(chan->flag & HOSTAPD_CHAN_DISABLED) && + !(hapd->iface->conf->acs_exclude_dfs && + (chan->flag & HOSTAPD_CHAN_RADAR))) int_array_add_unique(freq_list, chan->freq); } } +void hostapd_get_ext_capa(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + + if (!hapd->driver || !hapd->driver->get_ext_capab) + return; + + hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS, + &iface->extended_capa, + &iface->extended_capa_mask, + &iface->extended_capa_len); +} + + int hostapd_drv_do_acs(struct hostapd_data *hapd) { struct drv_acs_params params; @@ -797,6 +890,9 @@ &hapd->iface->conf->acs_ch_list, chan->chan)) continue; + if (hapd->iface->conf->acs_exclude_dfs && + (chan->flag & HOSTAPD_CHAN_RADAR)) + continue; if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) { channels[num_channels++] = chan->chan; int_array_add_unique(&freq_list, chan->freq); --- contrib/wpa/src/ap/ap_drv_ops.h.orig +++ contrib/wpa/src/ap/ap_drv_ops.h @@ -41,7 +41,8 @@ u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u32 flags, u8 qosinfo, u8 vht_opmode); + u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, + int set); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); @@ -71,7 +72,7 @@ int cw_min, int cw_max, int burst_time); struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, - u16 *flags); + u16 *flags, u8 *dfs_domain); int hostapd_driver_commit(struct hostapd_data *hapd); int hostapd_drv_none(struct hostapd_data *hapd); int hostapd_driver_scan(struct hostapd_data *hapd, @@ -88,6 +89,9 @@ const u8 *key, size_t key_len); int hostapd_drv_send_mlme(struct hostapd_data *hapd, const void *msg, size_t len, int noack); +int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd, + const void *msg, size_t len, int noack, + const u16 *csa_offs, size_t csa_offs_len); int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason); int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, @@ -95,6 +99,18 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len); +int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, + unsigned int freq, + unsigned int wait, const u8 *dst, + const u8 *data, size_t len); +static inline void +hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd) +{ + if (!hapd->driver || !hapd->driver->send_action_cancel_wait || + !hapd->drv_priv) + return; + hapd->driver->send_action_cancel_wait(hapd->drv_priv); +} int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, @@ -120,6 +136,8 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len); +void hostapd_get_ext_capa(struct hostapd_iface *iface); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { @@ -150,7 +168,7 @@ static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, const u8 *addr) { - if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv) return 0; return hapd->driver->sta_remove(hapd->drv_priv, addr); } @@ -264,8 +282,9 @@ static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { - if (hapd->driver == NULL || hapd->driver->switch_channel == NULL) - return -ENOTSUP; + if (hapd->driver == NULL || hapd->driver->switch_channel == NULL || + hapd->drv_priv == NULL) + return -1; return hapd->driver->switch_channel(hapd->drv_priv, settings); } @@ -273,7 +292,7 @@ static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, size_t buflen) { - if (hapd->driver == NULL || hapd->driver->status == NULL) + if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv) return -1; return hapd->driver->status(hapd->drv_priv, buf, buflen); } @@ -332,9 +351,27 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) { - if (hapd->driver == NULL || hapd->driver->stop_ap == NULL) + if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv) return 0; return hapd->driver->stop_ap(hapd->drv_priv); } +static inline int hostapd_drv_channel_info(struct hostapd_data *hapd, + struct wpa_channel_info *ci) +{ + if (!hapd->driver || !hapd->driver->channel_info) + return -1; + return hapd->driver->channel_info(hapd->drv_priv, ci); +} + +static inline int +hostapd_drv_send_external_auth_status(struct hostapd_data *hapd, + struct external_auth *params) +{ + if (!hapd->driver || !hapd->drv_priv || + !hapd->driver->send_external_auth_status) + return -1; + return hapd->driver->send_external_auth_status(hapd->drv_priv, params); +} + #endif /* AP_DRV_OPS */ --- contrib/wpa/src/ap/ap_mlme.c.orig +++ contrib/wpa/src/ap/ap_mlme.c @@ -57,8 +57,13 @@ HOSTAPD_LEVEL_DEBUG, "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); - if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK && + !(sta->flags & WLAN_STA_MFP)) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -104,8 +109,12 @@ HOSTAPD_LEVEL_DEBUG, "MLME-ASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - if (sta->auth_alg != WLAN_AUTH_FT) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -128,8 +137,12 @@ HOSTAPD_LEVEL_DEBUG, "MLME-REASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - if (sta->auth_alg != WLAN_AUTH_FT) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } --- contrib/wpa/src/ap/authsrv.c.orig +++ contrib/wpa/src/ap/authsrv.c @@ -71,13 +71,19 @@ } if (eap_user->password) { - user->password = os_malloc(eap_user->password_len); + user->password = os_memdup(eap_user->password, + eap_user->password_len); if (user->password == NULL) goto out; - os_memcpy(user->password, eap_user->password, - eap_user->password_len); user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; + if (eap_user->salt && eap_user->salt_len) { + user->salt = os_memdup(eap_user->salt, + eap_user->salt_len); + if (!user->salt) + goto out; + user->salt_len = eap_user->salt_len; + } } user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; @@ -84,6 +90,7 @@ user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; user->accept_attr = eap_user->accept_attr; + user->t_c_timestamp = eap_user->t_c_timestamp; rv = 0; out: @@ -129,10 +136,13 @@ #ifdef CONFIG_HS20 srv.subscr_remediation_url = conf->subscr_remediation_url; srv.subscr_remediation_method = conf->subscr_remediation_method; + srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url; + srv.t_c_server_url = conf->t_c_server_url; #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; srv.erp_domain = conf->erp_domain; srv.tls_session_lifetime = conf->tls_session_lifetime; + srv.tls_flags = conf->tls_flags; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -146,6 +156,40 @@ #endif /* RADIUS_SERVER */ +#ifdef EAP_TLS_FUNCS +static void authsrv_tls_event(void *ctx, enum tls_event ev, + union tls_event_data *data) +{ + switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success"); + break; + case TLS_CERT_CHAIN_FAILURE: + wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'", + data->cert_fail.reason, + data->cert_fail.depth, + data->cert_fail.subject, + data->cert_fail.reason_txt); + break; + case TLS_PEER_CERTIFICATE: + wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s", + data->peer_cert.depth, + data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A", + data->peer_cert.subject); + break; + case TLS_ALERT: + if (data->alert.is_local) + wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s", + data->alert.description); + else + wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s", + data->alert.description); + break; + } +} +#endif /* EAP_TLS_FUNCS */ + + int authsrv_init(struct hostapd_data *hapd) { #ifdef EAP_TLS_FUNCS @@ -157,6 +201,19 @@ os_memset(&conf, 0, sizeof(conf)); conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + if (hapd->conf->crl_reload_interval > 0 && + hapd->conf->check_crl <= 0) { + wpa_printf(MSG_INFO, + "Cannot enable CRL reload functionality - it depends on check_crl being set"); + } else if (hapd->conf->crl_reload_interval > 0) { + conf.crl_reload_interval = + hapd->conf->crl_reload_interval; + wpa_printf(MSG_INFO, + "Enabled CRL reload functionality"); + } + conf.tls_flags = hapd->conf->tls_flags; + conf.event_cb = authsrv_tls_event; + conf.cb_ctx = hapd; hapd->ssl_ctx = tls_init(&conf); if (hapd->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize TLS"); @@ -171,8 +228,12 @@ params.private_key_passwd = hapd->conf->private_key_passwd; params.dh_file = hapd->conf->dh_file; params.openssl_ciphers = hapd->conf->openssl_ciphers; + params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves; params.ocsp_stapling_response = hapd->conf->ocsp_stapling_response; + params.ocsp_stapling_response_multi = + hapd->conf->ocsp_stapling_response_multi; + params.check_cert_subject = hapd->conf->check_cert_subject; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); @@ -181,7 +242,8 @@ } if (tls_global_set_verify(hapd->ssl_ctx, - hapd->conf->check_crl)) { + hapd->conf->check_crl, + hapd->conf->check_crl_strict)) { wpa_printf(MSG_ERROR, "Failed to enable check_crl"); authsrv_deinit(hapd); return -1; @@ -193,6 +255,7 @@ if (hapd->conf->eap_sim_db) { hapd->eap_sim_db_priv = eap_sim_db_init(hapd->conf->eap_sim_db, + hapd->conf->eap_sim_db_timeout, hostapd_sim_db_cb, hapd); if (hapd->eap_sim_db_priv == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " --- contrib/wpa/src/ap/beacon.c.orig +++ contrib/wpa/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" @@ -29,6 +30,8 @@ #include "beacon.h" #include "hs20.h" #include "dfs.h" +#include "taxonomy.h" +#include "ieee802_11_auth.h" #ifdef NEED_AP_MLME @@ -36,18 +39,21 @@ static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, size_t len) { - if (!hapd->conf->radio_measurements || len < 2 + 4) + size_t i; + + for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { + if (hapd->conf->radio_measurements[i]) + break; + } + + if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN) return eid; *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; - *eid++ = 5; - *eid++ = (hapd->conf->radio_measurements & BIT(0)) ? - WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - return eid; + *eid++ = RRM_CAPABILITIES_IE_LEN; + os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN); + + return eid + RRM_CAPABILITIES_IE_LEN; } @@ -297,19 +303,18 @@ static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) { - u8 chan; - - if (!hapd->cs_freq_params.freq) +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only) return eid; +#endif /* CONFIG_TESTING_OPTIONS */ - if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) == - NUM_HOSTAPD_MODES) + if (!hapd->cs_freq_params.channel) return eid; *eid++ = WLAN_EID_CHANNEL_SWITCH; *eid++ = 3; *eid++ = hapd->cs_block_tx; - *eid++ = chan; + *eid++ = hapd->cs_freq_params.channel; *eid++ = hapd->cs_count; return eid; @@ -316,46 +321,47 @@ } -static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid) { - u8 sec_ch; - - if (!hapd->cs_freq_params.sec_channel_offset) + if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class) return eid; - if (hapd->cs_freq_params.sec_channel_offset == -1) - sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; - else if (hapd->cs_freq_params.sec_channel_offset == 1) - sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; - else - return eid; + *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN; + *eid++ = 4; + *eid++ = hapd->cs_block_tx; + *eid++ = hapd->iface->cs_oper_class; + *eid++ = hapd->cs_freq_params.channel; + *eid++ = hapd->cs_count; - *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; - *eid++ = 1; - *eid++ = sec_ch; - return eid; } -static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, - u8 *start, unsigned int *csa_counter_off) +static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid) { - u8 *old_pos = pos; + u8 op_class, channel; - if (!csa_counter_off) - return pos; + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || + !hapd->iface->freq) + return eid; - *csa_counter_off = 0; - pos = hostapd_eid_csa(hapd, pos); + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return eid; - if (pos != old_pos) { - /* save an offset to the counter - should be last byte */ - *csa_counter_off = pos - start - 1; - pos = hostapd_eid_secondary_channel(hapd, pos); - } + *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES; + *eid++ = 2; - return pos; + /* Current Operating Class */ + *eid++ = op_class; + + /* TODO: Advertise all the supported operating classes */ + *eid++ = 0; + + return eid; } @@ -364,7 +370,7 @@ int is_p2p, size_t *resp_len) { struct ieee80211_mgmt *resp; - u8 *pos, *epos; + u8 *pos, *epos, *csa_pos; size_t buflen; #define MAX_PROBERESP_LEN 768 @@ -387,6 +393,18 @@ buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + 2 + sizeof(struct ieee80211_vht_operation); } + +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + buflen += 3 + sizeof(struct ieee80211_he_capabilities) + + 3 + sizeof(struct ieee80211_he_operation) + + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); + } +#endif /* CONFIG_IEEE80211AX */ + + buflen += hostapd_mbo_ie_len(hapd); + buflen += hostapd_eid_owe_trans_len(hapd); + resp = os_zalloc(buflen); if (resp == NULL) return NULL; @@ -424,6 +442,12 @@ /* Power Constraint element */ pos = hostapd_eid_pwr_constraint(hapd, pos); + /* CSA IE */ + csa_pos = hostapd_eid_csa(hapd, pos); + if (csa_pos != pos) + hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1; + pos = csa_pos; + /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); @@ -430,14 +454,27 @@ /* Extended supported rates */ pos = hostapd_eid_ext_supp_rates(hapd, pos); - /* RSN, MDIE, WPA */ - pos = hostapd_eid_wpa(hapd, pos, epos - pos); + /* RSN, MDIE */ + if (hapd->conf->wpa != WPA_PROTO_WPA) + pos = hostapd_eid_wpa(hapd, pos, epos - pos); pos = hostapd_eid_bss_load(hapd, pos, epos - pos); pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); + /* eCSA IE */ + csa_pos = hostapd_eid_ecsa(hapd, pos); + if (csa_pos != pos) + hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1; + pos = csa_pos; + + pos = hostapd_eid_supported_op_classes(hapd, pos); + #ifdef CONFIG_IEEE80211N + /* Secondary Channel Offset element */ + /* TODO: The standard doesn't specify a position for this element. */ + pos = hostapd_eid_secondary_channel(hapd, pos); + pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); #endif /* CONFIG_IEEE80211N */ @@ -451,9 +488,6 @@ pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); - pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, - &hapd->cs_c_off_proberesp); - #ifdef CONFIG_FST if (hapd->iface->fst_ies) { os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), @@ -464,13 +498,32 @@ #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); + pos = hostapd_eid_txpower_envelope(hapd, pos); + pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); } +#endif /* CONFIG_IEEE80211AC */ + + pos = hostapd_eid_fils_indic(hapd, pos, 0); + +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + pos = hostapd_eid_he_capab(hapd, pos); + pos = hostapd_eid_he_operation(hapd, pos); + pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos); + } +#endif /* CONFIG_IEEE80211AX */ + +#ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht) pos = hostapd_eid_vendor_vht(hapd, pos); #endif /* CONFIG_IEEE80211AC */ + /* WPA */ + if (hapd->conf->wpa == WPA_PROTO_WPA) + pos = hostapd_eid_wpa(hapd, pos, epos - pos); + /* Wi-Fi Alliance WMM */ pos = hostapd_eid_wmm(hapd, pos); @@ -501,6 +554,9 @@ pos = hostapd_eid_osen(hapd, pos); #endif /* CONFIG_HS20 */ + pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos); + pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos); + if (hapd->conf->vendor_elements) { os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); @@ -537,8 +593,8 @@ pos = ssid_list; end = ssid_list + ssid_list_len; - while (pos + 1 <= end) { - if (pos + 2 + pos[1] > end) + while (end - pos >= 1) { + if (2 + pos[1] > end - pos) break; if (pos[1] == 0) wildcard = 1; @@ -574,7 +630,7 @@ MAC2STR(info->addr)); dl_list_del(&info->list); iface->num_sta_seen--; - os_free(info); + sta_track_del(info); } } @@ -592,7 +648,7 @@ } -void sta_track_add(struct hostapd_iface *iface, const u8 *addr) +void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal) { struct hostapd_sta_info *info; @@ -602,13 +658,17 @@ dl_list_del(&info->list); dl_list_add_tail(&iface->sta_seen, &info->list); os_get_reltime(&info->last_seen); + info->ssi_signal = ssi_signal; return; } /* Add a new entry */ info = os_zalloc(sizeof(*info)); + if (info == NULL) + return; os_memcpy(info->addr, addr, ETH_ALEN); os_get_reltime(&info->last_seen); + info->ssi_signal = ssi_signal; if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { /* Expire oldest entry to make room for a new one */ @@ -648,6 +708,23 @@ } +#ifdef CONFIG_TAXONOMY +void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, + struct wpabuf **probe_ie_taxonomy) +{ + struct hostapd_sta_info *info; + + info = sta_track_get(iface, addr); + if (!info) + return; + + wpabuf_free(*probe_ie_taxonomy); + *probe_ie_taxonomy = info->probe_ie_taxonomy; + info->probe_ie_taxonomy = NULL; +} +#endif /* CONFIG_TAXONOMY */ + + void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) @@ -659,14 +736,33 @@ size_t i, resp_len; int noack; enum ssid_match_result res; + 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; - ie = mgmt->u.probe_req.variable; - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + if (len < IEEE80211_HDRLEN) return; + ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; if (hapd->iconf->track_sta_max_num) - sta_track_add(hapd->iface, mgmt->sa); - ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + 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, @@ -673,7 +769,7 @@ ie, ie_len, ssi_signal) > 0) return; - if (!hapd->iconf->send_probe_response) + if (!hapd->conf->send_probe_response) return; if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { @@ -711,7 +807,7 @@ } #ifdef CONFIG_P2P - if (hapd->p2p && elems.wps_ie) { + if (hapd->p2p && hapd->p2p_group && elems.wps_ie) { struct wpabuf *wps; wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { @@ -724,7 +820,7 @@ wpabuf_free(wps); } - if (hapd->p2p && elems.p2p) { + if (hapd->p2p && hapd->p2p_group && elems.p2p) { struct wpabuf *p2p; p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { @@ -754,6 +850,21 @@ } #endif /* CONFIG_P2P */ +#ifdef CONFIG_TAXONOMY + { + struct sta_info *sta; + struct hostapd_sta_info *info; + + if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) { + taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len); + } else if ((info = sta_track_get(hapd->iface, + mgmt->sa)) != NULL) { + taxonomy_hostapd_sta_info_probe_req(hapd, info, + ie, ie_len); + } + } +#endif /* CONFIG_TAXONOMY */ + res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len); if (res == NO_SSID_MATCH) { @@ -825,6 +936,17 @@ return; } + if (hapd->conf->no_probe_resp_if_max_sta && + is_multicast_ether_addr(mgmt->da) && + is_multicast_ether_addr(mgmt->bssid) && + hapd->num_sta >= hapd->conf->max_num_sta && + !ap_get_sta(hapd, mgmt->sa)) { + wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR + " since no room for additional STA", + hapd->conf->iface, MAC2STR(mgmt->sa)); + return; + } + #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_probe_probability > 0.0 && drand48() < hapd->iconf->ignore_probe_probability) { @@ -835,6 +957,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) @@ -847,7 +972,22 @@ noack = !!(res == WILDCARD_SSID_MATCH && is_broadcast_ether_addr(mgmt->da)); - if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) + csa_offs_len = 0; + if (hapd->csa_in_progress) { + if (hapd->cs_c_off_proberesp) + csa_offs[csa_offs_len++] = + hapd->cs_c_off_proberesp; + + if (hapd->cs_c_off_ecsa_proberesp) + csa_offs[csa_offs_len++] = + hapd->cs_c_off_ecsa_proberesp; + } + + ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack, + csa_offs_len ? csa_offs : NULL, + csa_offs_len); + + if (ret < 0) wpa_printf(MSG_INFO, "handle_probe_req: send failed"); os_free(resp); @@ -896,6 +1036,16 @@ #endif /* NEED_AP_MLME */ +void sta_track_del(struct hostapd_sta_info *info) +{ +#ifdef CONFIG_TAXONOMY + wpabuf_free(info->probe_ie_taxonomy); + info->probe_ie_taxonomy = NULL; +#endif /* CONFIG_TAXONOMY */ + os_free(info); +} + + int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params) { @@ -906,7 +1056,7 @@ size_t resp_len = 0; #ifdef NEED_AP_MLME u16 capab_info; - u8 *pos, *tailpos; + u8 *pos, *tailpos, *csa_pos; #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 @@ -934,6 +1084,17 @@ } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + tail_len += 3 + sizeof(struct ieee80211_he_capabilities) + + 3 + sizeof(struct ieee80211_he_operation) + + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); + } +#endif /* CONFIG_IEEE80211AX */ + + tail_len += hostapd_mbo_ie_len(hapd); + tail_len += hostapd_eid_owe_trans_len(hapd); + tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); @@ -987,6 +1148,12 @@ /* Power Constraint element */ tailpos = hostapd_eid_pwr_constraint(hapd, tailpos); + /* CSA IE */ + csa_pos = hostapd_eid_csa(hapd, tailpos); + if (csa_pos != tailpos) + hapd->cs_c_off_beacon = csa_pos - tail - 1; + tailpos = csa_pos; + /* ERP Information element */ tailpos = hostapd_eid_erp_info(hapd, tailpos); @@ -993,9 +1160,11 @@ /* Extended supported rates */ tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); - /* RSN, MDIE, WPA */ - tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - - tailpos); + /* RSN, MDIE */ + if (hapd->conf->wpa != WPA_PROTO_WPA) + tailpos = hostapd_eid_wpa(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - @@ -1004,7 +1173,19 @@ tailpos = hostapd_eid_bss_load(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + /* eCSA IE */ + csa_pos = hostapd_eid_ecsa(hapd, tailpos); + if (csa_pos != tailpos) + hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1; + tailpos = csa_pos; + + tailpos = hostapd_eid_supported_op_classes(hapd, tailpos); + #ifdef CONFIG_IEEE80211N + /* Secondary Channel Offset element */ + /* TODO: The standard doesn't specify a position for this element. */ + tailpos = hostapd_eid_secondary_channel(hapd, tailpos); + tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); #endif /* CONFIG_IEEE80211N */ @@ -1020,8 +1201,6 @@ tailpos = hostapd_eid_interworking(hapd, tailpos); tailpos = hostapd_eid_adv_proto(hapd, tailpos); tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); - tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, - &hapd->cs_c_off_beacon); #ifdef CONFIG_FST if (hapd->iface->fst_ies) { @@ -1033,13 +1212,34 @@ #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); tailpos = hostapd_eid_vht_operation(hapd, tailpos); + tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); + tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); } +#endif /* CONFIG_IEEE80211AC */ + + tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0); + +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + tailpos = hostapd_eid_he_capab(hapd, tailpos); + tailpos = hostapd_eid_he_operation(hapd, tailpos); + tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos); + } +#endif /* CONFIG_IEEE80211AX */ + +#ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht) tailpos = hostapd_eid_vendor_vht(hapd, tailpos); #endif /* CONFIG_IEEE80211AC */ + /* WPA */ + if (hapd->conf->wpa == WPA_PROTO_WPA) + tailpos = hostapd_eid_wpa(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); + /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); @@ -1069,6 +1269,10 @@ tailpos = hostapd_eid_osen(hapd, tailpos); #endif /* CONFIG_HS20 */ + tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); + tailpos = hostapd_eid_owe_trans(hapd, tailpos, + tail + tail_len - tailpos); + if (hapd->conf->vendor_elements) { os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); @@ -1090,6 +1294,8 @@ params->dtim_period = hapd->conf->dtim_period; params->beacon_int = hapd->iconf->beacon_int; params->basic_rates = hapd->iface->basic_rates; + params->beacon_rate = hapd->iconf->beacon_rate; + params->rate_type = hapd->iconf->rate_type; params->ssid = hapd->conf->ssid.ssid; params->ssid_len = hapd->conf->ssid.ssid_len; if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == @@ -1153,6 +1359,20 @@ params->osen = 1; } #endif /* CONFIG_HS20 */ + params->multicast_to_unicast = hapd->conf->multicast_to_unicast; + params->pbss = hapd->conf->pbss; + + if (hapd->conf->ftm_responder) { + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) { + params->ftm_responder = 1; + params->lci = hapd->iface->conf->lci; + params->civic = hapd->iface->conf->civic; + } else { + wpa_printf(MSG_WARNING, + "Not configuring FTM responder as the driver doesn't advertise support for it"); + } + } + return 0; } --- contrib/wpa/src/ap/beacon.h.orig +++ contrib/wpa/src/ap/beacon.h @@ -21,10 +21,13 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params); void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); -void sta_track_add(struct hostapd_iface *iface, const u8 *addr); +void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal); +void sta_track_del(struct hostapd_sta_info *info); void sta_track_expire(struct hostapd_iface *iface, int force); struct hostapd_data * sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, const char *ifname); +void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, + struct wpabuf **probe_ie_taxonomy); #endif /* BEACON_H */ --- contrib/wpa/src/ap/bss_load.c.orig +++ contrib/wpa/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; --- contrib/wpa/src/ap/ctrl_iface_ap.c.orig +++ contrib/wpa/src/ap/ctrl_iface_ap.c @@ -1,6 +1,6 @@ /* * Control interface for shared AP commands - * Copyright (c) 2004-2014, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,8 +22,34 @@ #include "p2p_hostapd.h" #include "ctrl_iface_ap.h" #include "ap_drv_ops.h" +#include "mbo_ap.h" +#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) @@ -30,17 +56,111 @@ { struct hostap_sta_driver_data data; int ret; + int len = 0; if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) return 0; ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" - "rx_bytes=%lu\ntx_bytes=%lu\n", + "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n" + "signal=%d\n", data.rx_packets, data.tx_packets, - data.rx_bytes, data.tx_bytes); + data.rx_bytes, data.tx_bytes, data.inactive_msec, + data.signal); if (os_snprintf_error(buflen, ret)) return 0; - return ret; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu", + data.current_rx_rate); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + if (data.flags & STA_DRV_DATA_RX_MCS) { + ret = os_snprintf(buf + len, buflen - len, " mcs %u", + data.rx_mcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_VHT_MCS) { + ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", + data.rx_vhtmcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_VHT_NSS) { + ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", + data.rx_vht_nss); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_SHORT_GI) { + ret = os_snprintf(buf + len, buflen - len, " shortGI"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu", + data.current_tx_rate); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + if (data.flags & STA_DRV_DATA_TX_MCS) { + ret = os_snprintf(buf + len, buflen - len, " mcs %u", + data.tx_mcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_VHT_MCS) { + ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", + data.tx_vhtmcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_VHT_NSS) { + ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", + data.tx_vht_nss); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_SHORT_GI) { + ret = os_snprintf(buf + len, buflen - len, " shortGI"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { + ret = os_snprintf(buf + len, buflen - len, + "rx_vht_mcs_map=%04x\n" + "tx_vht_mcs_map=%04x\n", + le_to_host16(sta->vht_capabilities-> + vht_supported_mcs_set.rx_map), + le_to_host16(sta->vht_capabilities-> + vht_supported_mcs_set.tx_map)); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + + if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { + len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, + sta->ht_capabilities-> + supported_mcs_set); + } + + if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) { + ret = os_snprintf(buf + len, buflen - len, + "last_ack_signal=%d\n", data.last_ack_rssi); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + + return len; } @@ -87,6 +207,7 @@ char *buf, size_t buflen) { int len, res, ret, i; + const char *keyid; if (!sta) return 0; @@ -161,6 +282,73 @@ len += res; } + res = mbo_ap_get_info(sta, buf + len, buflen - len); + if (res >= 0) + len += res; + + if (sta->supp_op_classes && + buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) { + len += os_snprintf(buf + len, buflen - len, "supp_op_classes="); + len += wpa_snprintf_hex(buf + len, buflen - len, + sta->supp_op_classes + 1, + sta->supp_op_classes[0]); + 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; + } + + keyid = ap_sta_wpa_get_keyid(hapd, sta); + if (keyid) { + ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + return len; } @@ -244,7 +432,7 @@ int ret; u8 *pos; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; mgmt = os_zalloc(sizeof(*mgmt) + 100); @@ -255,7 +443,7 @@ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR " with minor reason code %u (stype=%u (%s))", MAC2STR(addr), minor_reason_code, stype, - fc2str(mgmt->frame_control)); + fc2str(le_to_host16(mgmt->frame_control))); os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); @@ -263,11 +451,11 @@ if (stype == WLAN_FC_STYPE_DEAUTH) { mgmt->u.deauth.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); + pos = mgmt->u.deauth.variable; } else { mgmt->u.disassoc.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); + pos = mgmt->u.disassoc.variable; } *pos++ = WLAN_EID_VENDOR_SPECIFIC; @@ -311,7 +499,7 @@ if (pos) { struct ieee80211_mgmt mgmt; int encrypt; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; pos += 6; encrypt = atoi(pos); @@ -338,7 +526,10 @@ } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_deauth(hapd, addr, reason); + if (os_strstr(txtaddr, " tx=0")) + hostapd_drv_sta_remove(hapd, addr); + else + hostapd_drv_sta_deauth(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) ap_sta_deauthenticate(hapd, sta, reason); @@ -371,7 +562,7 @@ if (pos) { struct ieee80211_mgmt mgmt; int encrypt; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; pos += 6; encrypt = atoi(pos); @@ -398,7 +589,10 @@ } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_disassoc(hapd, addr, reason); + if (os_strstr(txtaddr, " tx=0")) + hostapd_drv_sta_remove(hapd, addr); + else + hostapd_drv_sta_disassoc(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) ap_sta_disassociate(hapd, sta, reason); @@ -409,11 +603,55 @@ } +#ifdef CONFIG_TAXONOMY +int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, + const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + return retrieve_sta_taxonomy(hapd, sta, buf, buflen); +} +#endif /* CONFIG_TAXONOMY */ + + +int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + hostapd_drv_poll_client(hapd, hapd->own_addr, addr, + sta->flags & WLAN_STA_WMM); + return 0; +} + + int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen) { struct hostapd_iface *iface = hapd->iface; - int len = 0, ret; + struct hostapd_hw_modes *mode = iface->current_mode; + int len = 0, ret, j; size_t i; ret = os_snprintf(buf + len, buflen - len, @@ -474,20 +712,93 @@ "secondary_channel=%d\n" "ieee80211n=%d\n" "ieee80211ac=%d\n" - "vht_oper_chwidth=%d\n" - "vht_oper_centr_freq_seg0_idx=%d\n" - "vht_oper_centr_freq_seg1_idx=%d\n", + "beacon_int=%u\n" + "dtim_period=%d\n", iface->conf->channel, - iface->conf->secondary_channel, - iface->conf->ieee80211n, - iface->conf->ieee80211ac, - iface->conf->vht_oper_chwidth, - iface->conf->vht_oper_centr_freq_seg0_idx, - iface->conf->vht_oper_centr_freq_seg1_idx); + 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, + iface->conf->beacon_int, + hapd->conf->dtim_period); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; + if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) { + 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_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_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, @@ -506,6 +817,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; } @@ -554,3 +874,121 @@ { return hostapd_drv_stop_ap(hapd); } + + +int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, + size_t len) +{ + return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); +} + + +void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) +{ + wpa_auth_pmksa_flush(hapd->wpa_auth); +} + + +int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd) +{ + u8 spa[ETH_ALEN]; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + char *pos, *pos2; + int akmp = 0, expiration = 0; + + /* + * Entry format: + * + */ + + if (hwaddr_aton(cmd, spa)) + return -1; + + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + + if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) + return -1; + + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + + pos2 = os_strchr(pos, ' '); + if (!pos2) + return -1; + pmk_len = (pos2 - pos) / 2; + if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX || + hexstr2bin(pos, pmk, pmk_len) < 0) + return -1; + + pos = pos2 + 1; + + if (sscanf(pos, "%d %d", &expiration, &akmp) != 2) + return -1; + + return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, + pmkid, expiration, akmp); +} + + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, + const u8 *addr, char *buf, size_t len) +{ + return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len); +} + + +void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd) +{ + u8 spa[ETH_ALEN]; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + char *pos; + int expiration; + + /* + * Entry format: + * + */ + + if (hwaddr_aton(cmd, spa)) + return NULL; + + pos = os_strchr(cmd, ' '); + if (!pos) + return NULL; + pos++; + + if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) + return NULL; + + pos = os_strchr(pos, ' '); + if (!pos) + return NULL; + pos++; + + if (hexstr2bin(pos, pmk, PMK_LEN) < 0) + return NULL; + + pos = os_strchr(pos, ' '); + if (!pos) + return NULL; + pos++; + + if (sscanf(pos, "%d", &expiration) != 1) + return NULL; + + return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration); +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ --- contrib/wpa/src/ap/ctrl_iface_ap.h.orig +++ contrib/wpa/src/ap/ctrl_iface_ap.h @@ -19,10 +19,22 @@ const char *txtaddr); int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, const char *txtaddr); +int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, + const char *txtaddr, + char *buf, size_t buflen); +int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, + const char *txtaddr); int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen); int hostapd_parse_csa_settings(const char *pos, struct csa_settings *settings); int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); +int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, + size_t len); +void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd); +int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd); +int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, + const u8 *addr, char *buf, size_t len); +void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd); #endif /* CTRL_IFACE_AP_H */ --- contrib/wpa/src/ap/dfs.c.orig +++ contrib/wpa/src/ap/dfs.c @@ -1,7 +1,7 @@ /* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen - * Copyright (c) 2013-2015, Qualcomm Atheros, Inc. + * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -142,6 +142,7 @@ { struct hostapd_channel_data *first_chan, *chan; int i; + u32 bw = num_chan_to_bw(num_chans); if (first_chan_idx + num_chans > mode->num_channels) return 0; @@ -148,6 +149,12 @@ first_chan = &mode->channels[first_chan_idx]; + /* hostapd DFS implementation assumes the first channel as primary. + * If it's not allowed to use the first channel as primary, decline the + * whole channel range. */ + if (!chan_pri_allowed(first_chan)) + return 0; + for (i = 0; i < num_chans; i++) { chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, first_chan_idx); @@ -154,6 +161,11 @@ if (!chan) return 0; + /* HT 40 MHz secondary channel availability checked only for + * primary channel */ + if (!chan_bw_allowed(chan, bw, 1, !i)) + return 0; + if (!dfs_channel_available(chan, skip_radar)) return 0; } @@ -197,7 +209,8 @@ /* Skip HT40/VHT incompatible channels */ if (iface->conf->ieee80211n && iface->conf->secondary_channel && - !dfs_is_chan_allowed(chan, n_chans)) + (!dfs_is_chan_allowed(chan, n_chans) || + !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) continue; /* Skip incompatible chandefs */ @@ -450,7 +463,7 @@ return NULL; if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) - _rand = os_random(); + return NULL; chan_idx = _rand % num_available_chandefs; dfs_find_channel(iface, &chan, chan_idx, skip_radar); @@ -704,7 +717,8 @@ skip_radar); if (!channel) { wpa_printf(MSG_ERROR, "could not get valid channel"); - return -1; + hostapd_set_state(iface, HAPD_IFACE_DFS); + return 0; } iface->freq = channel->freq; @@ -746,6 +760,23 @@ } +static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface) +{ + int n_chans, n_chans1, start_chan_idx, start_chan_idx1; + + /* Get the start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); + if (start_chan_idx < 0) + return 0; + + /* Get the number of used channels, depending on width */ + n_chans = dfs_get_used_n_chans(iface, &n_chans1); + + /* Check if all channels are DFS available */ + return dfs_check_chans_available(iface, start_chan_idx, n_chans); +} + + int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) @@ -766,8 +797,21 @@ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_AVAILABLE); - iface->cac_started = 0; - hostapd_setup_interface_complete(iface, 0); + /* + * Just mark the channel available when CAC completion + * event is received in enabled state. CAC result could + * have been propagated from another radio having the + * same regulatory configuration. When CAC completion is + * received during non-HAPD_IFACE_ENABLED state, make + * sure the configured channel is available because this + * CAC completion event could have been propagated from + * another radio. + */ + if (iface->state != HAPD_IFACE_ENABLED && + hostapd_config_dfs_chan_available(iface)) { + hostapd_setup_interface_complete(iface, 0); + iface->cac_started = 0; + } } } @@ -775,6 +819,25 @@ } +int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + /* Proceed only if DFS is not offloaded to the driver */ + if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) + return 0; + + set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, + cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + + return 0; +} + + static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; @@ -793,7 +856,6 @@ if (!channel) { wpa_printf(MSG_ERROR, "No valid channel available"); - hostapd_setup_interface_complete(iface, err); return err; } @@ -817,16 +879,6 @@ } -static int hostapd_csa_in_progress(struct hostapd_iface *iface) -{ - unsigned int i; - for (i = 0; i < iface->num_bss; i++) - if (iface->bss[i]->csa_in_progress) - return 1; - return 0; -} - - static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; @@ -850,6 +902,13 @@ if (iface->cac_started) return hostapd_dfs_start_channel_switch_cac(iface); + /* + * Allow selection of DFS channel in ETSI to comply with + * uniform spreading. + */ + if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) + skip_radar = 0; + /* Perform channel switch/CSA */ channel = dfs_get_valid_channel(iface, &secondary_channel, &vht_oper_centr_freq_seg0_idx, @@ -868,8 +927,9 @@ &vht_oper_centr_freq_seg1_idx, skip_radar); if (!channel) { - /* FIXME: Wait for channel(s) to become available */ - hostapd_disable_iface(iface); + wpa_printf(MSG_INFO, + "%s: no DFS channels left, waiting for NOP to finish", + __func__); return err; } @@ -992,6 +1052,11 @@ /* TODO add correct implementation here */ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + + /* Handle cases where all channels were initially unavailable */ + if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) + hostapd_handle_dfs(iface); + return 0; } @@ -1059,7 +1124,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; --- contrib/wpa/src/ap/dfs.h.orig +++ contrib/wpa/src/ap/dfs.h @@ -1,7 +1,7 @@ /* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen - * Copyright (c) 2013, Qualcomm Atheros, Inc. + * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,9 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2); +int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2); int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, --- contrib/wpa/src/ap/dhcp_snoop.c.orig +++ contrib/wpa/src/ap/dhcp_snoop.c @@ -7,10 +7,9 @@ */ #include "utils/includes.h" -#include -#include #include "utils/common.h" +#include "common/dhcp.h" #include "l2_packet/l2_packet.h" #include "hostapd.h" #include "sta_info.h" @@ -18,30 +17,7 @@ #include "x_snoop.h" #include "dhcp_snoop.h" -struct bootp_pkt { - struct iphdr iph; - struct udphdr udph; - u8 op; - u8 htype; - u8 hlen; - u8 hops; - be32 xid; - be16 secs; - be16 flags; - be32 client_ip; - be32 your_ip; - be32 server_ip; - be32 relay_ip; - u8 hw_addr[16]; - u8 serv_name[64]; - u8 boot_file[128]; - u8 exten[312]; -} STRUCT_PACKED; -#define DHCPACK 5 -static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 }; - - static const char * ipaddr_str(u32 addr) { static char buf[17]; @@ -74,24 +50,26 @@ if (tot_len > (unsigned int) (len - ETH_HLEN)) return; - if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie))) + if (WPA_GET_BE32(b->exten) != DHCP_MAGIC) return; /* Parse DHCP options */ end = (const u8 *) b + tot_len; pos = &b->exten[4]; - while (pos < end && *pos != 0xff) { + while (pos < end && *pos != DHCP_OPT_END) { const u8 *opt = pos++; - if (*opt == 0) /* padding */ + if (*opt == DHCP_OPT_PAD) continue; + if (pos >= end || 1 + *pos > end - pos) + break; pos += *pos + 1; if (pos >= end) break; switch (*opt) { - case 1: /* subnet mask */ + case DHCP_OPT_SUBNET_MASK: if (opt[1] == 4) subnet_mask = WPA_GET_BE32(&opt[2]); if (subnet_mask == 0) @@ -101,7 +79,7 @@ prefixlen--; } break; - case 53: /* message type */ + case DHCP_OPT_MSG_TYPE: if (opt[1]) msgtype = opt[2]; break; @@ -110,6 +88,15 @@ } } + if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + continue; + x_snoop_mcast_to_ucast_convert_send(hapd, sta, + (u8 *) buf, len); + } + } + if (msgtype == DHCPACK) { if (b->your_ip == 0) return; @@ -121,7 +108,8 @@ wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR " @ IPv4 address %s/%d", - MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)), + MAC2STR(sta->addr), + ipaddr_str(be_to_host32(b->your_ip)), prefixlen); if (sta->ipaddr == b->your_ip) @@ -145,15 +133,6 @@ } sta->ipaddr = b->your_ip; } - - if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!(sta->flags & WLAN_STA_AUTHORIZED)) - continue; - x_snoop_mcast_to_ucast_convert_send(hapd, sta, - (u8 *) buf, len); - } - } } @@ -175,4 +154,5 @@ void dhcp_snoop_deinit(struct hostapd_data *hapd) { l2_packet_deinit(hapd->sock_dhcp); + hapd->sock_dhcp = NULL; } --- contrib/wpa/src/ap/dpp_hostapd.c.orig +++ contrib/wpa/src/ap/dpp_hostapd.c @@ -0,0 +1,1646 @@ +/* + * hostapd / DPP integration + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/dpp.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "gas_query_ap.h" +#include "wpa_auth.h" +#include "dpp_hostapd.h" + + +static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx); +static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator); +static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx); +static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd); + +static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +/** + * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code + * @hapd: Pointer to hostapd_data + * @cmd: DPP URI read from a QR Code + * Returns: Identifier of the stored info or -1 on failure + */ +int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) +{ + struct dpp_bootstrap_info *bi; + struct dpp_authentication *auth = hapd->dpp_auth; + + bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd); + if (!bi) + return -1; + + if (auth && auth->response_pending && + dpp_notify_new_qr_code(auth, bi) == 1) { + wpa_printf(MSG_DEBUG, + "DPP: Sending out pending authentication response"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, + auth->peer_mac_addr, + wpabuf_head(hapd->dpp_auth->resp_msg), + wpabuf_len(hapd->dpp_auth->resp_msg)); + } + + return bi->id; +} + + +static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->resp_msg) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Retry Authentication Response after timeout"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr, + wpabuf_head(auth->resp_msg), + wpabuf_len(auth->resp_msg)); +} + + +static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + unsigned int wait_time, max_tries; + + if (!auth || !auth->resp_msg) + return; + + if (hapd->dpp_resp_max_tries) + max_tries = hapd->dpp_resp_max_tries; + else + max_tries = 5; + auth->auth_resp_tries++; + if (auth->auth_resp_tries >= max_tries) { + wpa_printf(MSG_INFO, + "DPP: No confirm received from initiator - stopping exchange"); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + + if (hapd->dpp_resp_retry_time) + wait_time = hapd->dpp_resp_retry_time; + else + wait_time = 1000; + wpa_printf(MSG_DEBUG, + "DPP: Schedule retransmission of Authentication Response frame in %u ms", + wait_time); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); + eloop_register_timeout(wait_time / 1000, + (wait_time % 1000) * 1000, + hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +} + + +void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t data_len, int ok) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + + wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d", + MAC2STR(dst), ok); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR + " result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED"); + + if (!hapd->dpp_auth) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore TX status since there is no ongoing authentication exchange"); + return; + } + +#ifdef CONFIG_DPP2 + if (auth->connect_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Complete exchange on configuration result"); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } +#endif /* CONFIG_DPP2 */ + + if (hapd->dpp_auth->remove_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate authentication exchange due to an earlier error"); + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, + NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + + if (hapd->dpp_auth_ok_on_ack) + hostapd_dpp_auth_success(hapd, 1); + + if (!is_broadcast_ether_addr(dst) && !ok) { + wpa_printf(MSG_DEBUG, + "DPP: Unicast DPP Action frame was not ACKed"); + if (auth->waiting_auth_resp) { + /* In case of DPP Authentication Request frame, move to + * the next channel immediately. */ + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_auth_init_next(hapd); + return; + } + if (auth->waiting_auth_conf) { + hostapd_dpp_auth_resp_retry(hapd); + return; + } + } + + if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) { + /* Allow timeout handling to stop iteration if no response is + * received from a peer that has ACKed a request. */ + auth->auth_req_ack = 1; + } + + if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 && + hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response", + hapd->dpp_auth->curr_freq, + hapd->dpp_auth->neg_freq); + hostapd_drv_send_action_cancel_wait(hapd); + + if (hapd->dpp_auth->neg_freq != + (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + hapd->dpp_auth->neg_freq, hapd->iface->freq); + } + } + + if (hapd->dpp_auth_ok_on_ack) + hapd->dpp_auth_ok_on_ack = 0; +} + + +static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + unsigned int freq; + struct os_reltime now, diff; + unsigned int wait_time, diff_ms; + + if (!auth || !auth->waiting_auth_resp) + return; + + wait_time = hapd->dpp_resp_wait_time ? + hapd->dpp_resp_wait_time : 2000; + os_get_reltime(&now); + os_reltime_sub(&now, &hapd->dpp_last_init, &diff); + diff_ms = diff.sec * 1000 + diff.usec / 1000; + wpa_printf(MSG_DEBUG, + "DPP: Reply wait timeout - wait_time=%u diff_ms=%u", + wait_time, diff_ms); + + if (auth->auth_req_ack && diff_ms >= wait_time) { + /* Peer ACK'ed Authentication Request frame, but did not reply + * with Authentication Response frame within two seconds. */ + wpa_printf(MSG_INFO, + "DPP: No response received from responder - stopping initiation attempt"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED); + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; + return; + } + + if (diff_ms >= wait_time) { + /* Authentication Request frame was not ACK'ed and no reply + * was receiving within two seconds. */ + wpa_printf(MSG_DEBUG, + "DPP: Continue Initiator channel iteration"); + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + hostapd_dpp_auth_init_next(hapd); + return; + } + + /* Driver did not support 2000 ms long wait_time with TX command, so + * schedule listen operation to continue waiting for the response. + * + * DPP listen operations continue until stopped, so simply schedule a + * new call to this function at the point when the two second reply + * wait has expired. */ + wait_time -= diff_ms; + + freq = auth->curr_freq; + if (auth->neg_freq > 0) + freq = auth->neg_freq; + wpa_printf(MSG_DEBUG, + "DPP: Continue reply wait on channel %u MHz for %u ms", + freq, wait_time); + hapd->dpp_in_response_listen = 1; + + if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + freq, hapd->iface->freq); + } + + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + hostapd_dpp_reply_wait_timeout, hapd, NULL); +} + + +static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd, + struct dpp_authentication *auth) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->dpp_config_obj_override) + auth->config_obj_override = + os_strdup(hapd->dpp_config_obj_override); + if (hapd->dpp_discovery_override) + auth->discovery_override = + os_strdup(hapd->dpp_discovery_override); + if (hapd->dpp_groups_override) + auth->groups_override = os_strdup(hapd->dpp_groups_override); + auth->ignore_netaccesskey_mismatch = + hapd->dpp_ignore_netaccesskey_mismatch; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + +static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + + if (!hapd->dpp_auth) + return; + wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout"); + hostapd_dpp_auth_init_next(hapd); +} + + +static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + const u8 *dst; + unsigned int wait_time, max_wait_time, freq, max_tries, used; + struct os_reltime now, diff; + + if (!auth) + return -1; + + if (auth->freq_idx == 0) + os_get_reltime(&hapd->dpp_init_iter_start); + + if (auth->freq_idx >= auth->num_freq) { + auth->num_freq_iters++; + if (hapd->dpp_init_max_tries) + max_tries = hapd->dpp_init_max_tries; + else + max_tries = 5; + if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) { + wpa_printf(MSG_INFO, + "DPP: No response received from responder - stopping initiation attempt"); + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_AUTH_INIT_FAILED); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return -1; + } + auth->freq_idx = 0; + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + if (hapd->dpp_init_retry_time) + wait_time = hapd->dpp_init_retry_time; + else + wait_time = 10000; + os_get_reltime(&now); + os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff); + used = diff.sec * 1000 + diff.usec / 1000; + if (used > wait_time) + wait_time = 0; + else + wait_time -= used; + wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms", + wait_time); + eloop_register_timeout(wait_time / 1000, + (wait_time % 1000) * 1000, + hostapd_dpp_init_timeout, hapd, + NULL); + return 0; + } + freq = auth->freq[auth->freq_idx++]; + auth->curr_freq = freq; + + if (is_zero_ether_addr(auth->peer_bi->mac_addr)) + dst = broadcast; + else + dst = auth->peer_bi->mac_addr; + hapd->dpp_auth_ok_on_ack = 0; + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */ + max_wait_time = hapd->dpp_resp_wait_time ? + hapd->dpp_resp_wait_time : 2000; + if (wait_time > max_wait_time) + wait_time = max_wait_time; + wait_time += 10; /* give the driver some extra time to complete */ + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + hostapd_dpp_reply_wait_timeout, hapd, NULL); + wait_time -= 10; + if (auth->neg_freq > 0 && freq != auth->neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response", + freq, auth->neg_freq); + } + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ); + auth->auth_req_ack = 0; + os_get_reltime(&hapd->dpp_last_init); + return hostapd_drv_send_action(hapd, freq, wait_time, + dst, + wpabuf_head(hapd->dpp_auth->req_msg), + wpabuf_len(hapd->dpp_auth->req_msg)); +} + + +int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) +{ + const char *pos; + struct dpp_bootstrap_info *peer_bi, *own_bi = NULL; + u8 allowed_roles = DPP_CAPAB_CONFIGURATOR; + unsigned int neg_freq = 0; + + pos = os_strstr(cmd, " peer="); + if (!pos) + return -1; + pos += 6; + peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos)); + if (!peer_bi) { + wpa_printf(MSG_INFO, + "DPP: Could not find bootstrapping info for the identified peer"); + return -1; + } + + pos = os_strstr(cmd, " own="); + if (pos) { + pos += 5; + own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, + atoi(pos)); + if (!own_bi) { + wpa_printf(MSG_INFO, + "DPP: Could not find bootstrapping info for the identified local entry"); + return -1; + } + + if (peer_bi->curve != own_bi->curve) { + wpa_printf(MSG_INFO, + "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)", + peer_bi->curve->name, own_bi->curve->name); + return -1; + } + } + + pos = os_strstr(cmd, " role="); + if (pos) { + pos += 6; + if (os_strncmp(pos, "configurator", 12) == 0) + allowed_roles = DPP_CAPAB_CONFIGURATOR; + else if (os_strncmp(pos, "enrollee", 8) == 0) + allowed_roles = DPP_CAPAB_ENROLLEE; + else if (os_strncmp(pos, "either", 6) == 0) + allowed_roles = DPP_CAPAB_CONFIGURATOR | + DPP_CAPAB_ENROLLEE; + else + goto fail; + } + + pos = os_strstr(cmd, " neg_freq="); + if (pos) + neg_freq = atoi(pos + 10); + + if (hapd->dpp_auth) { + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, + NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + } + + hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi, + allowed_roles, neg_freq, + hapd->iface->hw_features, + hapd->iface->num_hw_features); + if (!hapd->dpp_auth) + goto fail; + hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + hapd->dpp_auth, cmd) < 0) { + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + goto fail; + } + + hapd->dpp_auth->neg_freq = neg_freq; + + if (!is_zero_ether_addr(peer_bi->mac_addr)) + os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr, + ETH_ALEN); + + return hostapd_dpp_auth_init_next(hapd); +fail: + return -1; +} + + +int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd) +{ + int freq; + + freq = atoi(cmd); + if (freq <= 0) + return -1; + + if (os_strstr(cmd, " role=configurator")) + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR; + else if (os_strstr(cmd, " role=enrollee")) + hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; + else + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | + DPP_CAPAB_ENROLLEE; + hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL; + + if (freq != hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + freq, hapd->iface->freq); + return -1; + } + + return 0; +} + + +void hostapd_dpp_listen_stop(struct hostapd_data *hapd) +{ + /* TODO: Stop listen operation on non-operating channel */ +} + + +static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + const u8 *r_bootstrap, *i_bootstrap; + u16 r_bootstrap_len, i_bootstrap_len; + struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; + + if (!hapd->iface->interfaces->dpp) + return; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, + MAC2STR(src)); + + r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + + i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Initiator Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + + /* Try to find own and peer bootstrapping key matches based on the + * received hash values */ + dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap, + r_bootstrap, &own_bi, &peer_bi); + if (!own_bi) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "No matching own bootstrapping key found - ignore message"); + return; + } + + if (hapd->dpp_auth) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Already in DPP authentication exchange - ignore new one"); + return; + } + + hapd->dpp_auth_ok_on_ack = 0; + hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles, + hapd->dpp_qr_mutual, + peer_bi, own_bi, freq, hdr, buf, len); + if (!hapd->dpp_auth) { + wpa_printf(MSG_DEBUG, "DPP: No response generated"); + return; + } + hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + hapd->dpp_auth, + hapd->dpp_configurator_params) < 0) { + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(src), hapd->dpp_auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0, + src, wpabuf_head(hapd->dpp_auth->resp_msg), + wpabuf_len(hapd->dpp_auth->resp_msg)); +} + + +static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd, + struct dpp_authentication *auth) +{ + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s", + dpp_akm_str(auth->akm)); + if (auth->ssid_len) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s", + wpa_ssid_txt(auth->ssid, auth->ssid_len)); + if (auth->connector) { + /* TODO: Save the Connector and consider using a command + * to fetch the value instead of sending an event with + * it. The Connector could end up being larger than what + * most clients are ready to receive as an event + * message. */ + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s", + auth->connector); + } else if (auth->passphrase[0]) { + char hex[64 * 2 + 1]; + + wpa_snprintf_hex(hex, sizeof(hex), + (const u8 *) auth->passphrase, + os_strlen(auth->passphrase)); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s", + hex); + } else if (auth->psk_set) { + char hex[PMK_LEN * 2 + 1]; + + wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s", + hex); + } + if (auth->c_sign_key) { + char *hex; + size_t hexlen; + + hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1; + hex = os_malloc(hexlen); + if (hex) { + wpa_snprintf_hex(hex, hexlen, + wpabuf_head(auth->c_sign_key), + wpabuf_len(auth->c_sign_key)); + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_C_SIGN_KEY "%s", hex); + os_free(hex); + } + } + if (auth->net_access_key) { + char *hex; + size_t hexlen; + + hexlen = 2 * wpabuf_len(auth->net_access_key) + 1; + hex = os_malloc(hexlen); + if (hex) { + wpa_snprintf_hex(hex, hexlen, + wpabuf_head(auth->net_access_key), + wpabuf_len(auth->net_access_key)); + if (auth->net_access_key_expiry) + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex, + (unsigned long) + auth->net_access_key_expiry); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_NET_ACCESS_KEY "%s", hex); + os_free(hex); + } + } +} + + +static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code) +{ + struct hostapd_data *hapd = ctx; + const u8 *pos; + struct dpp_authentication *auth = hapd->dpp_auth; + enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED; + + 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); + status = DPP_STATUS_OK; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_REJECT_CONFIG) { + wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object"); + status = DPP_STATUS_CONFIG_REJECTED; + } +#endif /* CONFIG_TESTING_OPTIONS */ +fail: + if (status != DPP_STATUS_OK) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); +#ifdef CONFIG_DPP2 + if (auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result"); + msg = dpp_build_conf_result(auth, status); + if (!msg) + goto fail2; + + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(addr), auth->curr_freq, + DPP_PA_CONFIGURATION_RESULT); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, + addr, wpabuf_head(msg), + wpabuf_len(msg)); + wpabuf_free(msg); + + /* This exchange will be terminated in the TX status handler */ + auth->connect_on_tx_status = 1; + return; + } +fail2: +#endif /* CONFIG_DPP2 */ + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; +} + + +static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *buf; + char json[100]; + int res; + int netrole_ap = 1; + + os_snprintf(json, sizeof(json), + "{\"name\":\"Test\"," + "\"wi-fi_tech\":\"infra\"," + "\"netRole\":\"%s\"}", + netrole_ap ? "ap" : "sta"); + wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); + + buf = dpp_build_conf_req(auth, json); + if (!buf) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration request data available"); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", + MAC2STR(auth->peer_mac_addr), auth->curr_freq); + + res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq, + buf, hostapd_dpp_gas_resp_cb, hapd); + if (res < 0) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Failed to send Query Request"); + wpabuf_free(buf); + } else { + wpa_printf(MSG_DEBUG, + "DPP: GAS query started with dialog token %u", res); + } +} + + +static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator) +{ + wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d", + initiator); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Confirm"); + if (hapd->dpp_auth->configurator) { + /* Prevent GAS response */ + hapd->dpp_auth->auth_success = 0; + } + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!hapd->dpp_auth->configurator) + hostapd_dpp_start_gas_client(hapd); +} + + +static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR, + MAC2STR(src)); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return; + } + + if (!is_zero_ether_addr(auth->peer_mac_addr) && + os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + + if (auth->curr_freq != freq && auth->neg_freq == freq) { + wpa_printf(MSG_DEBUG, + "DPP: Responder accepted request for different negotiation channel"); + auth->curr_freq = freq; + } + + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + msg = dpp_auth_resp_rx(auth, hdr, buf, len); + if (!msg) { + if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, "DPP: Wait for full response"); + return; + } + wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); + return; + } + os_memcpy(auth->peer_mac_addr, src, ETH_ALEN); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), auth->curr_freq, + DPP_PA_AUTHENTICATION_CONF); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); + hapd->dpp_auth_ok_on_ack = 1; +} + + +static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR, + MAC2STR(src)); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return; + } + + if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); + return; + } + + hostapd_dpp_auth_success(hapd, 0); +} + + +#ifdef CONFIG_DPP2 + +static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->waiting_conf_result) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Timeout while waiting for Configuration Result"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; +} + + +static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + enum dpp_status_error status; + + wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR, + MAC2STR(src)); + + if (!auth || !auth->waiting_conf_result) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Configuration waiting for result - drop"); + return; + } + + if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + status = dpp_conf_result_rx(auth, hdr, buf, len); + + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + if (status == DPP_STATUS_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(auth); + hapd->dpp_auth = NULL; + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, + NULL); +} + +#endif /* CONFIG_DPP2 */ + + +static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd, + const u8 *src, unsigned int freq, + u8 trans_id, + enum dpp_status_error status) +{ + struct wpabuf *msg; + + msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, + 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector)); + if (!msg) + return; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID"); + goto skip_trans_id; + } + if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID"); + trans_id ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Transaction ID */ + wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, trans_id); + +#ifdef CONFIG_TESTING_OPTIONS +skip_trans_id: + if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + goto skip_status; + } + if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: + if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Connector"); + goto skip_connector; + } + if (status == DPP_STATUS_OK && + dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) { + char *connector; + + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector"); + connector = dpp_corrupt_connector_signature( + hapd->conf->dpp_connector); + if (!connector) { + wpabuf_free(msg); + return; + } + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(connector)); + wpabuf_put_str(msg, connector); + os_free(connector); + goto skip_connector; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Connector */ + if (status == DPP_STATUS_OK) { + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector)); + wpabuf_put_str(msg, hapd->conf->dpp_connector); + } + +#ifdef CONFIG_TESTING_OPTIONS +skip_connector: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR + " status=%d", MAC2STR(src), status); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d status=%d", MAC2STR(src), freq, + DPP_PA_PEER_DISCOVERY_RESP, status); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); +} + + +static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, + const u8 *src, + const u8 *buf, size_t len, + unsigned int freq) +{ + const u8 *connector, *trans_id; + u16 connector_len, trans_id_len; + struct os_time now; + struct dpp_introduction intro; + os_time_t expire; + int expiration; + enum dpp_status_error res; + + wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR, + MAC2STR(src)); + if (!hapd->wpa_auth || + !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) || + !(hapd->conf->wpa & WPA_PROTO_RSN)) { + wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use"); + return; + } + + if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey || + !hapd->conf->dpp_csign) { + wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set"); + return; + } + + os_get_time(&now); + + if (hapd->conf->dpp_netaccesskey_expiry && + (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) { + wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired"); + return; + } + + trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID, + &trans_id_len); + if (!trans_id || trans_id_len != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include Transaction ID"); + return; + } + + connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len); + if (!connector) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include its Connector"); + return; + } + + res = dpp_peer_intro(&intro, hapd->conf->dpp_connector, + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey), + wpabuf_head(hapd->conf->dpp_csign), + wpabuf_len(hapd->conf->dpp_csign), + connector, connector_len, &expire); + if (res == 255) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in internal failure (peer " + MACSTR ")", MAC2STR(src)); + return; + } + if (res != DPP_STATUS_OK) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in failure (peer " + MACSTR " status %d)", MAC2STR(src), res); + hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], + res); + return; + } + + if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire) + expire = hapd->conf->dpp_netaccesskey_expiry; + if (expire) + expiration = expire - now.sec; + else + expiration = 0; + + if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, + intro.pmkid, expiration, + WPA_KEY_MGMT_DPP) < 0) { + wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry"); + return; + } + + hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], + DPP_STATUS_OK); +} + + +static void +hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, + unsigned int freq) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR, + MAC2STR(src)); + + /* TODO: Support multiple PKEX codes by iterating over all the enabled + * values here */ + + if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) { + wpa_printf(MSG_DEBUG, + "DPP: No PKEX code configured - ignore request"); + return; + } + + if (hapd->dpp_pkex) { + /* TODO: Support parallel operations */ + wpa_printf(MSG_DEBUG, + "DPP: Already in PKEX session - ignore new request"); + return; + } + + hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx, + hapd->dpp_pkex_bi, + hapd->own_addr, src, + hapd->dpp_pkex_identifier, + hapd->dpp_pkex_code, + buf, len); + if (!hapd->dpp_pkex) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to process the request - ignore it"); + return; + } + + msg = hapd->dpp_pkex->exchange_resp; + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_EXCHANGE_RESP); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + if (hapd->dpp_pkex->failed) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate PKEX exchange due to an earlier error"); + if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t) + hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + } +} + + +static void +hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR, + MAC2STR(src)); + + /* TODO: Support multiple PKEX codes by iterating over all the enabled + * values here */ + + if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator || + hapd->dpp_pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len); + if (!msg) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR, + MAC2STR(src)); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_COMMIT_REVEAL_REQ); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); +} + + +static void +hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct wpabuf *msg; + struct dpp_pkex *pkex = hapd->dpp_pkex; + struct dpp_bootstrap_info *bi; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR, + MAC2STR(src)); + + if (!pkex || pkex->initiator || !pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len); + if (!msg) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the request"); + if (hapd->dpp_pkex->failed) { + wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange"); + if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t) + hapd->dpp_pkex->own_bi->pkex_t = + hapd->dpp_pkex->t; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + } + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to " + MACSTR, MAC2STR(src)); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_COMMIT_REVEAL_RESP); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); + + bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); + if (!bi) + return; + hapd->dpp_pkex = NULL; +} + + +static void +hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + int res; + struct dpp_bootstrap_info *bi; + struct dpp_pkex *pkex = hapd->dpp_pkex; + char cmd[500]; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR, + MAC2STR(src)); + + if (!pkex || !pkex->initiator || !pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); + return; + } + + bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); + if (!bi) + return; + hapd->dpp_pkex = NULL; + + os_snprintf(cmd, sizeof(cmd), " peer=%u %s", + bi->id, + hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : ""); + wpa_printf(MSG_DEBUG, + "DPP: Start authentication after PKEX with parameters: %s", + cmd); + if (hostapd_dpp_auth_init(hapd, cmd) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Authentication initialization failed"); + return; + } +} + + +void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + u8 crypto_suite; + enum dpp_public_action_frame_type type; + const u8 *hdr; + unsigned int pkex_t; + + if (len < DPP_HDR_LEN) + return; + if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE) + return; + hdr = buf; + buf += 4; + len -= 4; + crypto_suite = *buf++; + type = *buf++; + len -= 2; + + wpa_printf(MSG_DEBUG, + "DPP: Received DPP Public Action frame crypto suite %u type %d from " + MACSTR " freq=%u", + crypto_suite, type, MAC2STR(src), freq); + if (crypto_suite != 1) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u", + crypto_suite); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d ignore=unsupported-crypto-suite", + MAC2STR(src), freq, type); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len); + if (dpp_check_attrs(buf, len) < 0) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d ignore=invalid-attributes", + MAC2STR(src), freq, type); + return; + } + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, type); + + switch (type) { + case DPP_PA_AUTHENTICATION_REQ: + hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_RESP: + hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_CONF: + hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len); + break; + case DPP_PA_PEER_DISCOVERY_REQ: + hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_EXCHANGE_REQ: + hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_EXCHANGE_RESP: + hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_COMMIT_REVEAL_REQ: + hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len, + freq); + break; + case DPP_PA_PKEX_COMMIT_REVEAL_RESP: + hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len, + freq); + break; +#ifdef CONFIG_DPP2 + case DPP_PA_CONFIGURATION_RESULT: + hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len); + break; +#endif /* CONFIG_DPP2 */ + default: + wpa_printf(MSG_DEBUG, + "DPP: Ignored unsupported frame subtype %d", type); + break; + } + + if (hapd->dpp_pkex) + pkex_t = hapd->dpp_pkex->t; + else if (hapd->dpp_pkex_bi) + pkex_t = hapd->dpp_pkex_bi->pkex_t; + else + pkex_t = 0; + if (pkex_t >= PKEX_COUNTER_T_LIMIT) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0"); + hostapd_dpp_pkex_remove(hapd, "*"); + } +} + + +struct wpabuf * +hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, + const u8 *query, size_t query_len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa)); + if (!auth || !auth->auth_success || + os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, + "DPP: Received Configuration Request (GAS Query Request)", + query, query_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR, + MAC2STR(sa)); + resp = dpp_conf_req_rx(auth, query, query_len); + if (!resp) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + return resp; +} + + +void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth) + return; + + wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)", + ok); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +#ifdef CONFIG_DPP2 + if (ok && auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result"); + auth->waiting_conf_result = 1; + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, + hapd, NULL); + eloop_register_timeout(2, 0, + hostapd_dpp_config_result_wait_timeout, + hapd, NULL); + return; + } +#endif /* CONFIG_DPP2 */ + 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; +} + + +int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) +{ + struct dpp_authentication *auth; + int ret = -1; + char *curve = NULL; + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + return -1; + + curve = get_param(cmd, " curve="); + hostapd_dpp_set_testing_options(hapd, auth); + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + auth, cmd) == 0 && + dpp_configurator_own_config(auth, curve, 1) == 0) { + hostapd_dpp_handle_config_obj(hapd, auth); + ret = 0; + } + + dpp_auth_deinit(auth); + os_free(curve); + + return ret; +} + + +int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) +{ + struct dpp_bootstrap_info *own_bi; + const char *pos, *end; + + pos = os_strstr(cmd, " own="); + if (!pos) + return -1; + pos += 5; + own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos)); + if (!own_bi) { + wpa_printf(MSG_DEBUG, + "DPP: Identified bootstrap info not found"); + return -1; + } + if (own_bi->type != DPP_BOOTSTRAP_PKEX) { + wpa_printf(MSG_DEBUG, + "DPP: Identified bootstrap info not for PKEX"); + return -1; + } + hapd->dpp_pkex_bi = own_bi; + own_bi->pkex_t = 0; /* clear pending errors on new code */ + + os_free(hapd->dpp_pkex_identifier); + hapd->dpp_pkex_identifier = NULL; + pos = os_strstr(cmd, " identifier="); + if (pos) { + pos += 12; + end = os_strchr(pos, ' '); + if (!end) + return -1; + hapd->dpp_pkex_identifier = os_malloc(end - pos + 1); + if (!hapd->dpp_pkex_identifier) + return -1; + os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos); + hapd->dpp_pkex_identifier[end - pos] = '\0'; + } + + pos = os_strstr(cmd, " code="); + if (!pos) + return -1; + os_free(hapd->dpp_pkex_code); + hapd->dpp_pkex_code = os_strdup(pos + 6); + if (!hapd->dpp_pkex_code) + return -1; + + if (os_strstr(cmd, " init=1")) { + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX"); + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi, + hapd->own_addr, + hapd->dpp_pkex_identifier, + hapd->dpp_pkex_code); + if (!hapd->dpp_pkex) + return -1; + + msg = hapd->dpp_pkex->exchange_req; + /* TODO: Which channel to use? */ + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(broadcast), 2437, + DPP_PA_PKEX_EXCHANGE_REQ); + hostapd_drv_send_action(hapd, 2437, 0, broadcast, + wpabuf_head(msg), wpabuf_len(msg)); + } + + /* TODO: Support multiple PKEX info entries */ + + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = os_strdup(cmd); + + return 1; +} + + +int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code) + return -1; + + /* TODO: Support multiple PKEX entries */ + os_free(hapd->dpp_pkex_code); + hapd->dpp_pkex_code = NULL; + os_free(hapd->dpp_pkex_identifier); + hapd->dpp_pkex_identifier = NULL; + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = NULL; + hapd->dpp_pkex_bi = NULL; + /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */ + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + return 0; +} + + +void hostapd_dpp_stop(struct hostapd_data *hapd) +{ + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; +} + + +int hostapd_dpp_init(struct hostapd_data *hapd) +{ + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE; + hapd->dpp_init_done = 1; + return 0; +} + + +void hostapd_dpp_deinit(struct hostapd_data *hapd) +{ +#ifdef CONFIG_TESTING_OPTIONS + os_free(hapd->dpp_config_obj_override); + hapd->dpp_config_obj_override = NULL; + os_free(hapd->dpp_discovery_override); + hapd->dpp_discovery_override = NULL; + os_free(hapd->dpp_groups_override); + hapd->dpp_groups_override = NULL; + hapd->dpp_ignore_netaccesskey_mismatch = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + if (!hapd->dpp_init_done) + return; + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +#ifdef CONFIG_DPP2 + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, + NULL); +#endif /* CONFIG_DPP2 */ + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + hostapd_dpp_pkex_remove(hapd, "*"); + hapd->dpp_pkex = NULL; + os_free(hapd->dpp_configurator_params); + hapd->dpp_configurator_params = NULL; +} --- contrib/wpa/src/ap/dpp_hostapd.h.orig +++ contrib/wpa/src/ap/dpp_hostapd.h @@ -0,0 +1,37 @@ +/* + * hostapd / DPP integration + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DPP_HOSTAPD_H +#define DPP_HOSTAPD_H + +int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd); +void hostapd_dpp_listen_stop(struct hostapd_data *hapd); +void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, unsigned int freq); +void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t data_len, int ok); +struct wpabuf * +hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, + const u8 *query, size_t query_len); +void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok); +int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id); +int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id, + char *buf, size_t buflen); +int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id); +void hostapd_dpp_stop(struct hostapd_data *hapd); +int hostapd_dpp_init(struct hostapd_data *hapd); +void hostapd_dpp_deinit(struct hostapd_data *hapd); +void hostapd_dpp_init_global(struct hapd_interfaces *ifaces); +void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces); + +#endif /* DPP_HOSTAPD_H */ --- contrib/wpa/src/ap/drv_callbacks.c.orig +++ contrib/wpa/src/ap/drv_callbacks.c @@ -15,6 +15,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "common/dpp.h" #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" @@ -22,6 +23,7 @@ #include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" +#include "ieee802_11_auth.h" #include "sta_info.h" #include "accounting.h" #include "tkip_countermeasures.h" @@ -30,11 +32,77 @@ #include "wps_hostapd.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "ap_mlme.h" #include "hw_features.h" #include "dfs.h" #include "beacon.h" +#include "mbo_ap.h" +#include "dpp_hostapd.h" +#include "fils_hlp.h" +#include "neighbor_db.h" +#ifdef CONFIG_FILS +void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta) +{ + u16 reply_res = WLAN_STATUS_SUCCESS; + struct ieee802_11_elems elems; + u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf; + int new_assoc; + + wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR, + __func__, MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + if (!sta->fils_pending_assoc_req) + return; + + ieee802_11_parse_elems(sta->fils_pending_assoc_req, + sta->fils_pending_assoc_req_len, &elems, 0); + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element", + __func__); + return; + } + + p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, + elems.fils_session, + sta->fils_hlp_resp); + + reply_res = hostapd_sta_assoc(hapd, sta->addr, + sta->fils_pending_assoc_is_reassoc, + WLAN_STATUS_SUCCESS, + buf, p - buf); + ap_sta_set_authorized(hapd, sta, 1); + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + hostapd_set_sta_flags(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + fils_hlp_deinit(hapd); + + /* + * Remove the station in case transmission of a success response fails + * (the STA was added associated to the driver) or if the station was + * previously added unassociated. + */ + if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } +} +#endif /* CONFIG_FILS */ + + int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, const u8 *req_ies, size_t req_ies_len, int reassoc) { @@ -43,10 +111,10 @@ struct ieee802_11_elems elems; const u8 *ie; size_t ielen; -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) || defined(CONFIG_OWE) u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; u8 *p = buf; -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS || CONFIG_OWE */ u16 reason = WLAN_REASON_UNSPECIFIED; u16 status = WLAN_STATUS_SUCCESS; const u8 *p2p_dev_addr = NULL; @@ -114,6 +182,21 @@ } sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); + /* + * ACL configurations to the drivers (implementing AP SME and ACL + * offload) without hostapd's knowledge, can result in a disconnection + * though the driver accepts the connection. Skip the hostapd check for + * ACL if the driver supports ACL offload to avoid potentially + * conflicting ACL rules. + */ + if (hapd->iface->drv_max_acl_mac_addrs == 0 && + hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) { + wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect", + MAC2STR(addr)); + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + #ifdef CONFIG_P2P if (elems.p2p) { wpabuf_free(sta->p2p_ie); @@ -154,6 +237,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 @@ -164,6 +255,11 @@ sta->mb_ies = NULL; #endif /* CONFIG_FST */ + mbo_ap_check_sta_assoc(hapd, sta, &elems); + + ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, + elems.supp_op_classes_len); + if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { #ifdef CONFIG_WPS @@ -176,7 +272,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 && @@ -208,8 +306,10 @@ return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, ie, ielen, - elems.mdie, elems.mdie_len); + elems.mdie, elems.mdie_len, + elems.owe_dh, elems.owe_dh_len); if (res != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "WPA/RSN information element rejected? (res %u)", @@ -230,8 +330,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 { @@ -241,10 +341,14 @@ goto fail; } #ifdef CONFIG_IEEE80211W - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && (sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA @@ -271,7 +375,7 @@ sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, req_ies_len); @@ -285,7 +389,7 @@ goto fail; } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; @@ -338,23 +442,175 @@ return WLAN_STATUS_INVALID_IE; #endif /* CONFIG_HS20 */ } + +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && + elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && + hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_INFO, + "MBO: Reject WPA2 association without PMF"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_MBO */ + #ifdef CONFIG_WPS skip_wpa_check: #endif /* CONFIG_WPS */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), sta->auth_alg, req_ies, req_ies_len); + if (!p) { + wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + int delay_assoc = 0; + + if (!req_ies) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies, + req_ies_len, + sta->fils_session)) { + wpa_printf(MSG_DEBUG, + "FILS: Session validation failed"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies, + req_ies_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "FILS: Key Confirm validation failed"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) { + wpa_printf(MSG_DEBUG, + "FILS: Delaying Assoc Response (HLP)"); + delay_assoc = 1; + } else { + wpa_printf(MSG_DEBUG, + "FILS: Going ahead with Assoc Response (no HLP)"); + } + + if (sta) { + wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup"); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + sta->fils_drv_assoc_finish = 0; + } + + if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) { + u8 *req_tmp; + + req_tmp = os_malloc(req_ies_len); + if (!req_tmp) { + wpa_printf(MSG_DEBUG, + "FILS: buffer allocation failed for assoc req"); + goto fail; + } + os_memcpy(req_tmp, req_ies, req_ies_len); + sta->fils_pending_assoc_req = req_tmp; + sta->fils_pending_assoc_req_len = req_ies_len; + sta->fils_pending_assoc_is_reassoc = reassoc; + sta->fils_drv_assoc_finish = 1; + wpa_printf(MSG_DEBUG, + "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + eloop_register_timeout( + 0, hapd->conf->fils_hlp_wait_time * 1024, + fils_hlp_timeout, hapd, sta); + return 0; + } + p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, + elems.fils_session, + sta->fils_hlp_resp); + wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)", + buf, p - buf); + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && + elems.owe_dh) { + u8 *npos; + + npos = owe_assoc_req_process(hapd, sta, + elems.owe_dh, elems.owe_dh_len, + p, sizeof(buf) - (p - buf), + &reason); + if (npos) + p = npos; + if (!npos && + reason == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { + status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, + p - buf); + return 0; + } + + if (!npos || reason != WLAN_STATUS_SUCCESS) + goto fail; + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + hapd->conf->dpp_netaccesskey && sta->wpa_sm && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && + elems.owe_dh) { + sta->dpp_pfs = dpp_pfs_init( + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey)); + if (!sta->dpp_pfs) { + wpa_printf(MSG_DEBUG, + "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + + if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, + elems.owe_dh_len) < 0) { + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + } + + wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? + sta->dpp_pfs->secret : NULL); + pfs_fail: +#endif /* CONFIG_DPP2 */ + +#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE) hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); - if (sta->auth_alg == WLAN_AUTH_FT) + if (sta->auth_alg == WLAN_AUTH_FT || + sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) ap_sta_set_authorized(hapd, sta, 1); -#else /* CONFIG_IEEE80211R */ +#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ /* Keep compiler silent about unused variables */ if (status) { } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; @@ -364,6 +620,12 @@ if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); +#ifdef CONFIG_FILS + else if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS); +#endif /* CONFIG_FILS */ else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); @@ -381,9 +643,9 @@ return 0; fail: -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ hostapd_drv_sta_disassoc(hapd, sta->addr, reason); ap_free_sta(hapd, sta); return -1; @@ -431,7 +693,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, @@ -438,22 +700,92 @@ HOSTAPD_LEVEL_INFO, "disconnected due to excessive missing ACKs"); hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); - if (sta) - ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); + ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); } +void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, + enum smps_mode smps_mode, + enum chan_width chan_width, u8 rx_nss) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); + const char *txt; + + if (!sta) + return; + + switch (smps_mode) { + case SMPS_AUTOMATIC: + txt = "automatic"; + break; + case SMPS_OFF: + txt = "off"; + break; + case SMPS_DYNAMIC: + txt = "dynamic"; + break; + case SMPS_STATIC: + txt = "static"; + break; + default: + txt = NULL; + break; + } + if (txt) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED + MACSTR " %s", MAC2STR(addr), txt); + } + + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + txt = "20(no-HT)"; + break; + case CHAN_WIDTH_20: + txt = "20"; + break; + case CHAN_WIDTH_40: + txt = "40"; + break; + case CHAN_WIDTH_80: + txt = "80"; + break; + case CHAN_WIDTH_80P80: + txt = "80+80"; + break; + case CHAN_WIDTH_160: + txt = "160"; + break; + default: + txt = NULL; + break; + } + if (txt) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED + MACSTR " %s", MAC2STR(addr), txt); + } + + if (rx_nss != 0xff) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED + MACSTR " %d", MAC2STR(addr), rx_nss); + } +} + + void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2) { + /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */ + #ifdef NEED_AP_MLME - int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs; + int channel, chwidth, is_dfs; + u8 seg0_idx = 0, seg1_idx = 0; + size_t i; 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; @@ -491,21 +823,33 @@ seg1_idx = (cf2 - 5000) / 5; break; default: - seg0_idx = hostapd_hw_get_channel(hapd, cf1); - seg1_idx = hostapd_hw_get_channel(hapd, cf2); + ieee80211_freq_to_chan(cf1, &seg0_idx); + ieee80211_freq_to_chan(cf2, &seg1_idx); break; } 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) { @@ -518,6 +862,9 @@ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d dfs=%d", freq, is_dfs); } + + for (i = 0; i < hapd->iface->num_bss; i++) + hostapd_neighbor_set_own_report(hapd->iface->bss[i]); #endif /* NEED_AP_MLME */ } @@ -539,10 +886,11 @@ #ifdef CONFIG_ACS -static void hostapd_acs_channel_selected(struct hostapd_data *hapd, - struct acs_selected_channels *acs_res) +void hostapd_acs_channel_selected(struct hostapd_data *hapd, + struct acs_selected_channels *acs_res) { int ret, i; + int err = 0; if (hapd->iconf->channel) { wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", @@ -564,7 +912,8 @@ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver selected to bad hw_mode"); - return; + err = 1; + goto out; } } @@ -574,7 +923,8 @@ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver switched to bad channel"); - return; + err = 1; + goto out; } hapd->iconf->channel = acs_res->pri_channel; @@ -588,7 +938,8 @@ hapd->iconf->secondary_channel = 1; else { wpa_printf(MSG_ERROR, "Invalid secondary channel!"); - return; + err = 1; + goto out; } if (hapd->iface->conf->ieee80211ac) { @@ -617,7 +968,8 @@ } } - ret = hostapd_acs_completed(hapd->iface, 0); +out: + ret = hostapd_acs_completed(hapd->iface, err); if (ret) { wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid"); @@ -651,7 +1003,7 @@ #ifdef HOSTAPD -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, @@ -670,9 +1022,35 @@ hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_FILS +static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub) +{ + if (resp == WLAN_STATUS_SUCCESS) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_FILS_SK; + mlme_authenticate_indication(hapd, sta); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication failed (FILS)"); + } + + hostapd_sta_auth(hapd, sta->addr, 2, resp, + data ? wpabuf_head(data) : NULL, + data ? wpabuf_len(data) : 0); + wpabuf_free(data); +} +#endif /* CONFIG_FILS */ + + static void hostapd_notif_auth(struct hostapd_data *hapd, struct auth_info *rx_auth) { @@ -691,7 +1069,7 @@ } sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) @@ -709,7 +1087,19 @@ hostapd_notify_auth_ft_finish, hapd); return; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) { + sta->auth_alg = WLAN_AUTH_FILS_SK; + handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len, + rx_auth->auth_type, rx_auth->auth_transaction, + rx_auth->status_code, + hostapd_notify_auth_fils_finish); + return; + } +#endif /* CONFIG_FILS */ + fail: hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, status, resp_ies, resp_ies_len); @@ -716,6 +1106,7 @@ } +#ifndef NEED_AP_MLME static void hostapd_action_rx(struct hostapd_data *hapd, struct rx_mgmt *drv_mgmt) { @@ -723,11 +1114,12 @@ struct sta_info *sta; size_t plen __maybe_unused; u16 fc; + u8 *action __maybe_unused; - if (drv_mgmt->frame_len < 24 + 1) + if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1) return; - plen = drv_mgmt->frame_len - 24 - 1; + plen = drv_mgmt->frame_len - IEEE80211_HDRLEN; mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; fc = le_to_host16(mgmt->frame_control); @@ -734,8 +1126,11 @@ if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) return; /* handled by the driver */ - wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", - mgmt->u.action.category, (int) plen); + action = (u8 *) &mgmt->u.action.u; + wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR + " da " MACSTR " plen %d", + mgmt->u.action.category, *action, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { @@ -742,26 +1137,24 @@ wpa_printf(MSG_DEBUG, "%s: station not found", __func__); return; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (mgmt->u.action.category == WLAN_ACTION_FT) { - const u8 *payload = drv_mgmt->frame + 24 + 1; - - wpa_ft_action_rx(sta->wpa_sm, payload, plen); + wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen); + return; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W - if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { - ieee802_11_sa_query_action( - hapd, mgmt->sa, - mgmt->u.action.u.sa_query_resp.action, - mgmt->u.action.u.sa_query_resp.trans_id); + if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) { + ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len); + return; } #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (mgmt->u.action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); + return; } -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) { fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len); @@ -768,8 +1161,25 @@ return; } #endif /* CONFIG_FST */ +#ifdef CONFIG_DPP + if (plen >= 2 + 4 && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + pos = mgmt->u.action.u.vs_public_action.oui; + end = drv_mgmt->frame + drv_mgmt->frame_len; + hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, + drv_mgmt->freq); + return; + } +#endif /* CONFIG_DPP */ } +#endif /* NEED_AP_MLME */ #ifdef NEED_AP_MLME @@ -852,6 +1262,7 @@ } os_memset(&fi, 0, sizeof(fi)); + fi.freq = rx_mgmt->freq; fi.datarate = rx_mgmt->datarate; fi.ssi_signal = rx_mgmt->ssi_signal; @@ -884,11 +1295,24 @@ size_t len, u16 stype, int ok) { struct ieee80211_hdr *hdr; + struct hostapd_data *orig_hapd = hapd; hdr = (struct ieee80211_hdr *) buf; hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); - if (hapd == NULL || hapd == HAPD_BROADCAST) + if (!hapd) return; + if (hapd == HAPD_BROADCAST) { + if (stype != WLAN_FC_STYPE_ACTION || len <= 25 || + buf[24] != WLAN_ACTION_PUBLIC) + return; + hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2); + if (!hapd || hapd == HAPD_BROADCAST) + return; + /* + * Allow processing of TX status for a Public Action frame that + * used wildcard BBSID. + */ + } ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); } @@ -935,7 +1359,9 @@ ieee802_1x_receive(hapd, src, data, data_len); } +#endif /* HOSTAPD */ + static struct hostapd_channel_data * hostapd_get_mode_channel( struct hostapd_iface *iface, unsigned int freq) { @@ -944,8 +1370,6 @@ for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; - if (!chan) - return NULL; if ((unsigned int) chan->freq == freq) return chan; } @@ -1009,10 +1433,9 @@ } -static void hostapd_event_get_survey(struct hostapd_data *hapd, - struct survey_results *survey_results) +void hostapd_event_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_results) { - struct hostapd_iface *iface = hapd->iface; struct freq_survey *survey, *tmp; struct hostapd_channel_data *chan; @@ -1044,6 +1467,7 @@ } +#ifdef HOSTAPD #ifdef NEED_AP_MLME static void hostapd_event_iface_unavailable(struct hostapd_data *hapd) @@ -1070,6 +1494,16 @@ } +static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq); + hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd, struct dfs_event *radar) { @@ -1112,6 +1546,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) { @@ -1185,10 +1641,10 @@ if (!data->rx_mgmt.frame) break; #ifdef NEED_AP_MLME - if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) - break; + hostapd_mgmt_rx(hapd, &data->rx_mgmt); +#else /* NEED_AP_MLME */ + hostapd_action_rx(hapd, &data->rx_mgmt); #endif /* NEED_AP_MLME */ - hostapd_action_rx(hapd, &data->rx_mgmt); break; case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || @@ -1251,7 +1707,7 @@ data->connect_failed_reason.code); break; case EVENT_SURVEY: - hostapd_event_get_survey(hapd, &data->survey_results); + hostapd_event_get_survey(hapd->iface, &data->survey_results); break; #ifdef NEED_AP_MLME case EVENT_INTERFACE_UNAVAILABLE: @@ -1262,6 +1718,11 @@ break; hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); break; + case EVENT_DFS_PRE_CAC_EXPIRED: + if (!data) + break; + hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event); + break; case EVENT_DFS_CAC_FINISHED: if (!data) break; @@ -1299,9 +1760,17 @@ * Try to re-enable interface if the driver stopped it * when the interface got disabled. */ - wpa_auth_reconfig_group_keys(hapd->wpa_auth); + if (hapd->wpa_auth) + wpa_auth_reconfig_group_keys(hapd->wpa_auth); + else + hostapd_reconfig_encryption(hapd); hapd->reenable_beacon = 1; ieee802_11_set_beacon(hapd); +#ifdef NEED_AP_MLME + } else if (hapd->disabled && hapd->iface->cac_started) { + wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC"); + hostapd_handle_dfs(hapd->iface); +#endif /* NEED_AP_MLME */ } break; case EVENT_INTERFACE_DISABLED: @@ -1315,6 +1784,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; @@ -1321,4 +1802,31 @@ } } + +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct hapd_interfaces *interfaces = ctx; + struct hostapd_data *hapd; + + if (event != EVENT_INTERFACE_STATUS) + return; + + hapd = hostapd_get_iface(interfaces, data->interface_status.ifname); + if (hapd && hapd->driver && hapd->driver->get_ifindex && + hapd->drv_priv) { + unsigned int ifindex; + + ifindex = hapd->driver->get_ifindex(hapd->drv_priv); + if (ifindex != data->interface_status.ifindex) { + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "interface status ifindex %d mismatch (%d)", + ifindex, data->interface_status.ifindex); + return; + } + } + if (hapd) + wpa_supplicant_event(hapd, event, data); +} + #endif /* HOSTAPD */ --- contrib/wpa/src/ap/eap_user_db.c.orig +++ contrib/wpa/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], NULL, 10); } } @@ -137,6 +139,7 @@ struct hostapd_eap_user *user = NULL; char id_str[256], cmd[300]; size_t i; + int res; if (identity_len >= sizeof(id_str)) { wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d", @@ -172,6 +175,7 @@ if (hapd->tmp_eap_user.identity == NULL) return NULL; os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); + hapd->tmp_eap_user.identity_len = identity_len; if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", @@ -180,9 +184,12 @@ return NULL; } - os_snprintf(cmd, sizeof(cmd), - "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", - id_str, phase2); + res = os_snprintf(cmd, sizeof(cmd), + "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", + id_str, phase2); + if (os_snprintf_error(sizeof(cmd), res)) + goto fail; + wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { @@ -212,6 +219,7 @@ } } +fail: sqlite3_close(db); return user; --- contrib/wpa/src/ap/eth_p_oui.c.orig +++ contrib/wpa/src/ap/eth_p_oui.c @@ -0,0 +1,191 @@ +/* + * hostapd / IEEE 802 OUI Extended EtherType 88-B7 + * Copyright (c) 2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "l2_packet/l2_packet.h" +#include "hostapd.h" +#include "eth_p_oui.h" + +/* + * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended + * EtherType 88-B7. This file implements this with OUI 00:13:74 and + * vendor-specific subtype 0x0001. + */ +static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 }; + +struct eth_p_oui_iface { + struct dl_list list; + char ifname[IFNAMSIZ + 1]; + struct l2_packet_data *l2; + struct dl_list receiver; +}; + +struct eth_p_oui_ctx { + struct dl_list list; + struct eth_p_oui_iface *iface; + /* all data needed to deliver and unregister */ + u8 oui_suffix; /* last byte of OUI */ + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len); + void *rx_callback_ctx; +}; + + +void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len) +{ + ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr, + ctx->oui_suffix, buf, len); +} + + +static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct eth_p_oui_iface *iface = ctx; + struct eth_p_oui_ctx *receiver; + const struct l2_ethhdr *ethhdr; + + if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) { + /* too short packet */ + return; + } + + ethhdr = (struct l2_ethhdr *) buf; + /* trim eth_hdr from buf and len */ + buf += sizeof(*ethhdr); + len -= sizeof(*ethhdr); + + /* verify OUI and vendor-specific subtype match */ + if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0) + return; + buf += sizeof(global_oui); + len -= sizeof(global_oui); + + dl_list_for_each(receiver, &iface->receiver, + struct eth_p_oui_ctx, list) { + if (buf[0] != receiver->oui_suffix) + continue; + + eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest, + buf + 1, len - 1); + } +} + + +struct eth_p_oui_ctx * +eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len), + void *rx_callback_ctx) +{ + struct eth_p_oui_iface *iface; + struct eth_p_oui_ctx *receiver; + int found = 0; + struct hapd_interfaces *interfaces; + + receiver = os_zalloc(sizeof(*receiver)); + if (!receiver) + goto err; + + receiver->oui_suffix = oui_suffix; + receiver->rx_callback = rx_callback; + receiver->rx_callback_ctx = rx_callback_ctx; + + interfaces = hapd->iface->interfaces; + + dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface, + list) { + if (os_strcmp(iface->ifname, ifname) != 0) + continue; + found = 1; + break; + } + + if (!found) { + iface = os_zalloc(sizeof(*iface)); + if (!iface) + goto err; + + os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname)); + iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx, + iface, 1); + if (!iface->l2) { + os_free(iface); + goto err; + } + dl_list_init(&iface->receiver); + + dl_list_add_tail(&interfaces->eth_p_oui, &iface->list); + } + + dl_list_add_tail(&iface->receiver, &receiver->list); + receiver->iface = iface; + + return receiver; +err: + os_free(receiver); + return NULL; +} + + +void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx) +{ + struct eth_p_oui_iface *iface; + + if (!ctx) + return; + + iface = ctx->iface; + + dl_list_del(&ctx->list); + os_free(ctx); + + if (dl_list_empty(&iface->receiver)) { + dl_list_del(&iface->list); + l2_packet_deinit(iface->l2); + os_free(iface); + } +} + + +int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len) +{ + struct eth_p_oui_iface *iface = ctx->iface; + u8 *packet, *p; + size_t packet_len; + int ret; + struct l2_ethhdr *ethhdr; + + packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len; + packet = os_zalloc(packet_len); + if (!packet) + return -1; + p = packet; + + ethhdr = (struct l2_ethhdr *) packet; + os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN); + os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); + ethhdr->h_proto = host_to_be16(ETH_P_OUI); + p += sizeof(*ethhdr); + + os_memcpy(p, global_oui, sizeof(global_oui)); + p[sizeof(global_oui)] = ctx->oui_suffix; + p += sizeof(global_oui) + 1; + + os_memcpy(p, buf, len); + + ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len); + os_free(packet); + return ret; +} --- contrib/wpa/src/ap/eth_p_oui.h.orig +++ contrib/wpa/src/ap/eth_p_oui.h @@ -0,0 +1,28 @@ +/* + * hostapd / IEEE 802 OUI Extended Ethertype + * Copyright (c) 2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ETH_P_OUI_H +#define ETH_P_OUI_H + +struct eth_p_oui_ctx; +struct hostapd_data; + +/* rx_callback only gets payload after OUI passed as buf */ +struct eth_p_oui_ctx * +eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len), + void *rx_callback_ctx); +void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui); +int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len); +void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len); + +#endif /* ETH_P_OUI_H */ --- contrib/wpa/src/ap/fils_hlp.c.orig +++ contrib/wpa/src/ap/fils_hlp.c @@ -0,0 +1,654 @@ +/* + * FILS HLP request processing + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/dhcp.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ieee802_11.h" +#include "fils_hlp.h" + + +static be16 ip_checksum(const void *buf, size_t len) +{ + u32 sum = 0; + const u16 *pos; + + for (pos = buf; len >= 2; len -= 2) + sum += ntohs(*pos++); + if (len) + sum += ntohs(*pos << 8); + + sum = (sum >> 16) + (sum & 0xffff); + sum += sum >> 16; + return htons(~sum); +} + + +static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta, + struct dhcp_data *dhcpoffer, u8 *dhcpofferend) +{ + u8 *pos, *end; + struct dhcp_data *dhcp; + struct sockaddr_in addr; + ssize_t res; + const u8 *server_id = NULL; + + if (!sta->hlp_dhcp_discover) { + wpa_printf(MSG_DEBUG, + "FILS: No pending HLP DHCPDISCOVER available"); + return -1; + } + + /* Convert to DHCPREQUEST, remove rapid commit option, replace requested + * IP address option with yiaddr. */ + pos = wpabuf_mhead(sta->hlp_dhcp_discover); + end = pos + wpabuf_len(sta->hlp_dhcp_discover); + dhcp = (struct dhcp_data *) pos; + pos = (u8 *) (dhcp + 1); + pos += 4; /* skip magic */ + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + *pos = DHCPREQUEST; + break; + case DHCP_OPT_RAPID_COMMIT: + case DHCP_OPT_REQUESTED_IP_ADDRESS: + case DHCP_OPT_SERVER_ID: + /* Remove option */ + pos -= 2; + os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen); + end -= 2 + olen; + olen = 0; + break; + } + pos += olen; + } + if (pos >= end || *pos != DHCP_OPT_END) { + wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER"); + return -1; + } + sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp; + + /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */ + pos = (u8 *) (dhcpoffer + 1); + end = dhcpofferend; + pos += 4; /* skip magic */ + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_SERVER_ID: + server_id = pos - 2; + break; + } + pos += olen; + } + + if (wpabuf_resize(&sta->hlp_dhcp_discover, + 6 + 1 + (server_id ? 2 + server_id[1] : 0))) + return -1; + if (server_id) + wpabuf_put_data(sta->hlp_dhcp_discover, server_id, + 2 + server_id[1]); + wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS); + wpabuf_put_u8(sta->hlp_dhcp_discover, 4); + wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4); + wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END); + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_server_port); + res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover), + wpabuf_len(sta->hlp_dhcp_discover), 0, + (const struct sockaddr *) &addr, sizeof(addr)); + if (res < 0) { + wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", + strerror(errno)); + return -1; + } + wpa_printf(MSG_DEBUG, + "FILS: Acting as DHCP rapid commit proxy for %s:%d", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + sta->fils_dhcp_rapid_commit_proxy = 1; + return 0; +} + + +static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = sock_ctx; + struct sta_info *sta; + u8 buf[1500], *pos, *end, *end_opt = NULL; + struct dhcp_data *dhcp; + struct sockaddr_in addr; + socklen_t addr_len; + ssize_t res; + u8 msgtype = 0; + int rapid_commit = 0; + struct iphdr *iph; + struct udphdr *udph; + struct wpabuf *resp; + const u8 *rpos; + size_t left, len; + + addr_len = sizeof(addr); + res = recvfrom(sd, buf, sizeof(buf), 0, + (struct sockaddr *) &addr, &addr_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s", + strerror(errno)); + return; + } + wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res); + wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res); + if ((size_t) res < sizeof(*dhcp)) + return; + dhcp = (struct dhcp_data *) buf; + if (dhcp->op != 2) + return; /* Not a BOOTREPLY */ + if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - DHCP response to unknown relay address 0x%x", + dhcp->relay_ip); + return; + } + dhcp->relay_ip = 0; + pos = (u8 *) (dhcp + 1); + end = &buf[res]; + + if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) { + wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response"); + return; + } + pos += 4; + + wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response", + pos, end - pos); + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + msgtype = pos[0]; + break; + case DHCP_OPT_RAPID_COMMIT: + rapid_commit = 1; + break; + } + pos += olen; + } + if (pos < end && *pos == DHCP_OPT_END) + end_opt = pos; + + wpa_printf(MSG_DEBUG, + "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr=" + MACSTR ")", + msgtype, rapid_commit, MAC2STR(dhcp->hw_addr)); + + sta = ap_get_sta(hapd, dhcp->hw_addr); + if (!sta || !sta->fils_pending_assoc_req) { + wpa_printf(MSG_DEBUG, + "FILS: No pending HLP DHCP exchange with hw_addr " + MACSTR, MAC2STR(dhcp->hw_addr)); + return; + } + + if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER && + !rapid_commit) { + /* Use hostapd to take care of 4-message exchange and convert + * the final DHCPACK to rapid commit version. */ + if (fils_dhcp_request(hapd, sta, dhcp, end) == 0) + return; + /* failed, so send the server response as-is */ + } else if (msgtype != DHCPACK) { + wpa_printf(MSG_DEBUG, + "FILS: No DHCPACK available from the server and cannot do rapid commit proxying"); + } + + pos = buf; + resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 + + sizeof(*iph) + sizeof(*udph) + (end - pos) + 2); + if (!resp) + return; + wpabuf_put_data(resp, sta->addr, ETH_ALEN); + wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN); + wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6); + wpabuf_put_be16(resp, ETH_P_IP); + iph = wpabuf_put(resp, sizeof(*iph)); + iph->version = 4; + iph->ihl = sizeof(*iph) / 4; + iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos)); + iph->ttl = 1; + iph->protocol = 17; /* UDP */ + iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr; + iph->daddr = dhcp->client_ip; + iph->check = ip_checksum(iph, sizeof(*iph)); + udph = wpabuf_put(resp, sizeof(*udph)); + udph->uh_sport = htons(DHCP_SERVER_PORT); + udph->uh_dport = htons(DHCP_CLIENT_PORT); + udph->uh_ulen = htons(sizeof(*udph) + (end - pos)); + udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */ + if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK && + !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) { + /* Add rapid commit option */ + wpabuf_put_data(resp, pos, end_opt - pos); + wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT); + wpabuf_put_u8(resp, 0); + wpabuf_put_data(resp, end_opt, end - end_opt); + } else { + wpabuf_put_data(resp, pos, end - pos); + } + if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) + + 2 * wpabuf_len(resp) / 255 + 100)) { + wpabuf_free(resp); + return; + } + + rpos = wpabuf_head(resp); + left = wpabuf_len(resp); + + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */ + if (left <= 254) + len = 1 + left; + else + len = 255; + wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER); + /* Destination MAC Address, Source MAC Address, HLP Packet. + * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header + * when LPD is used). */ + wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1); + rpos += len - 1; + left -= len - 1; + while (left) { + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT); + len = left > 255 ? 255 : left; + wpabuf_put_u8(sta->fils_hlp_resp, len); + wpabuf_put_data(sta->fils_hlp_resp, rpos, len); + rpos += len; + left -= len; + } + wpabuf_free(resp); + + if (sta->fils_drv_assoc_finish) + hostapd_notify_assoc_fils_finish(hapd, sta); + else + fils_hlp_finish_assoc(hapd, sta); +} + + +static int fils_process_hlp_dhcp(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *msg, size_t len) +{ + const struct dhcp_data *dhcp; + struct wpabuf *dhcp_buf; + struct dhcp_data *dhcp_msg; + u8 msgtype = 0; + int rapid_commit = 0; + const u8 *pos = msg, *end; + struct sockaddr_in addr; + ssize_t res; + + if (len < sizeof(*dhcp)) + return 0; + dhcp = (const struct dhcp_data *) pos; + end = pos + len; + wpa_printf(MSG_DEBUG, + "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x", + dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops, + ntohl(dhcp->xid)); + pos += sizeof(*dhcp); + if (dhcp->op != 1) + return 0; /* Not a BOOTREQUEST */ + + if (end - pos < 4) + return 0; + if (WPA_GET_BE32(pos) != DHCP_MAGIC) { + wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic"); + return 0; + } + pos += 4; + + wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos); + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + msgtype = pos[0]; + break; + case DHCP_OPT_RAPID_COMMIT: + rapid_commit = 1; + break; + } + pos += olen; + } + + wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype); + if (msgtype != DHCPDISCOVER) + return 0; + + if (hapd->conf->dhcp_server.af != AF_INET || + hapd->conf->dhcp_server.u.v4.s_addr == 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - no DHCPv4 server configured - drop request"); + return 0; + } + + if (hapd->conf->own_ip_addr.af != AF_INET || + hapd->conf->own_ip_addr.u.v4.s_addr == 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - no IPv4 own_ip_addr configured - drop request"); + return 0; + } + + if (hapd->dhcp_sock < 0) { + int s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, + "FILS: Failed to open DHCP socket: %s", + strerror(errno)); + return 0; + } + + if (hapd->conf->dhcp_relay_port) { + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = + hapd->conf->own_ip_addr.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_relay_port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) { + wpa_printf(MSG_ERROR, + "FILS: Failed to bind DHCP socket: %s", + strerror(errno)); + close(s); + return 0; + } + } + if (eloop_register_sock(s, EVENT_TYPE_READ, + fils_dhcp_handler, NULL, hapd)) { + close(s); + return 0; + } + + hapd->dhcp_sock = s; + } + + dhcp_buf = wpabuf_alloc(len); + if (!dhcp_buf) + return 0; + dhcp_msg = wpabuf_put(dhcp_buf, len); + os_memcpy(dhcp_msg, msg, len); + dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr; + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_server_port); + res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0, + (const struct sockaddr *) &addr, sizeof(addr)); + if (res < 0) { + wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", + strerror(errno)); + wpabuf_free(dhcp_buf); + /* Close the socket to try to recover from error */ + eloop_unregister_read_sock(hapd->dhcp_sock); + close(hapd->dhcp_sock); + hapd->dhcp_sock = -1; + return 0; + } + + wpa_printf(MSG_DEBUG, + "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), + rapid_commit); + if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) { + /* Store a copy of the DHCPDISCOVER for rapid commit proxying + * purposes if the server does not support the rapid commit + * option. */ + wpa_printf(MSG_DEBUG, + "FILS: Store DHCPDISCOVER for rapid commit proxy"); + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = dhcp_buf; + } else { + wpabuf_free(dhcp_buf); + } + + return 1; +} + + +static int fils_process_hlp_udp(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *dst, + const u8 *pos, size_t len) +{ + const struct iphdr *iph; + const struct udphdr *udph; + u16 sport, dport, ulen; + + if (len < sizeof(*iph) + sizeof(*udph)) + return 0; + iph = (const struct iphdr *) pos; + udph = (const struct udphdr *) (iph + 1); + sport = ntohs(udph->uh_sport); + dport = ntohs(udph->uh_dport); + ulen = ntohs(udph->uh_ulen); + wpa_printf(MSG_DEBUG, + "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x", + sport, dport, ulen, ntohs(udph->uh_sum)); + /* TODO: Check UDP checksum */ + if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph)) + return 0; + + if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) { + return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1), + ulen - sizeof(*udph)); + } + + return 0; +} + + +static int fils_process_hlp_ip(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *dst, + const u8 *pos, size_t len) +{ + const struct iphdr *iph; + u16 tot_len; + + if (len < sizeof(*iph)) + return 0; + iph = (const struct iphdr *) pos; + if (ip_checksum(iph, sizeof(*iph)) != 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP request IPv4 packet had invalid header checksum - dropped"); + return 0; + } + tot_len = ntohs(iph->tot_len); + if (tot_len > len) + return 0; + wpa_printf(MSG_DEBUG, + "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u", + iph->saddr, iph->daddr, iph->protocol); + switch (iph->protocol) { + case 17: + return fils_process_hlp_udp(hapd, sta, dst, pos, len); + } + + return 0; +} + + +static int fils_process_hlp_req(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *pos, size_t len) +{ + const u8 *pkt, *end; + + wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR + " src=" MACSTR " len=%u)", + MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN), + (unsigned int) len); + if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "FILS: Ignore HLP request with unexpected source address" + MACSTR, MAC2STR(pos + ETH_ALEN)); + return 0; + } + + end = pos + len; + pkt = pos + 2 * ETH_ALEN; + if (end - pkt >= 6 && + os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) + pkt += 6; /* Remove SNAP/LLC header */ + wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt); + + if (end - pkt < 2) + return 0; + + switch (WPA_GET_BE16(pkt)) { + case ETH_P_IP: + return fils_process_hlp_ip(hapd, sta, pos, pkt + 2, + end - pkt - 2); + } + + return 0; +} + + +int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, int left) +{ + const u8 *end = pos + left; + u8 *tmp, *tmp_pos; + int ret = 0; + + if (sta->fils_pending_assoc_req && + eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) { + /* Do not process FILS HLP request again if the station + * retransmits (Re)Association Request frame before the previous + * HLP response has either been received or timed out. */ + wpa_printf(MSG_DEBUG, + "FILS: Do not relay another HLP request from " + MACSTR + " before processing of the already pending one has been completed", + MAC2STR(sta->addr)); + return 1; + } + + /* Old DHCPDISCOVER is not needed anymore, if it was still pending */ + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + sta->fils_dhcp_rapid_commit_proxy = 0; + + /* Check if there are any FILS HLP Container elements */ + while (end - pos >= 2) { + if (2 + pos[1] > end - pos) + return 0; + if (pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 + 2 * ETH_ALEN && + pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + pos += 2 + pos[1]; + } + if (end - pos < 2) + return 0; /* No FILS HLP Container elements */ + + tmp = os_malloc(end - pos); + if (!tmp) + return 0; + + while (end - pos >= 2) { + if (2 + pos[1] > end - pos || + pos[0] != WLAN_EID_EXTENSION || + pos[1] < 1 + 2 * ETH_ALEN || + pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + tmp_pos = tmp; + os_memcpy(tmp_pos, pos + 3, pos[1] - 1); + tmp_pos += pos[1] - 1; + pos += 2 + pos[1]; + + /* Add possible fragments */ + while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && + 2 + pos[1] <= end - pos) { + os_memcpy(tmp_pos, pos + 2, pos[1]); + tmp_pos += pos[1]; + pos += 2 + pos[1]; + } + + if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0) + ret = 1; + } + + os_free(tmp); + + return ret; +} + + +void fils_hlp_deinit(struct hostapd_data *hapd) +{ + if (hapd->dhcp_sock >= 0) { + eloop_unregister_read_sock(hapd->dhcp_sock); + close(hapd->dhcp_sock); + hapd->dhcp_sock = -1; + } +} --- contrib/wpa/src/ap/fils_hlp.h.orig +++ contrib/wpa/src/ap/fils_hlp.h @@ -0,0 +1,27 @@ +/* + * FILS HLP request processing + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FILS_HLP_H +#define FILS_HLP_H + +int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, int left); + +#ifdef CONFIG_FILS + +void fils_hlp_deinit(struct hostapd_data *hapd); + +#else /* CONFIG_FILS */ + +static inline void fils_hlp_deinit(struct hostapd_data *hapd) +{ +} + +#endif /* CONFIG_FILS */ + +#endif /* FILS_HLP_H */ --- contrib/wpa/src/ap/gas_query_ap.c.orig +++ contrib/wpa/src/ap/gas_query_ap.c @@ -0,0 +1,714 @@ +/* + * Generic advertisement service (GAS) query (hostapd) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2017, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/eloop.h" +#include "utils/list.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "gas_query_ap.h" + + +/** GAS query timeout in seconds */ +#define GAS_QUERY_TIMEOUT_PERIOD 2 + +/* GAS query wait-time / duration in ms */ +#define GAS_QUERY_WAIT_TIME_INITIAL 1000 +#define GAS_QUERY_WAIT_TIME_COMEBACK 150 + +/** + * struct gas_query_pending - Pending GAS query + */ +struct gas_query_pending { + struct dl_list list; + struct gas_query_ap *gas; + u8 addr[ETH_ALEN]; + u8 dialog_token; + u8 next_frag_id; + unsigned int wait_comeback:1; + unsigned int offchannel_tx_started:1; + unsigned int retry:1; + int freq; + u16 status_code; + struct wpabuf *req; + struct wpabuf *adv_proto; + struct wpabuf *resp; + struct os_reltime last_oper; + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code); + void *ctx; + u8 sa[ETH_ALEN]; +}; + +/** + * struct gas_query_ap - Internal GAS query data + */ +struct gas_query_ap { + struct hostapd_data *hapd; + void *msg_ctx; + struct dl_list pending; /* struct gas_query_pending */ + struct gas_query_pending *current; +}; + + +static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_timeout(void *eloop_data, void *user_ctx); +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_tx_initial_req(struct gas_query_ap *gas, + struct gas_query_pending *query); +static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst); + + +static int ms_from_time(struct os_reltime *last) +{ + struct os_reltime now, res; + + os_get_reltime(&now); + os_reltime_sub(&now, last, &res); + return res.sec * 1000 + res.usec / 1000; +} + + +/** + * gas_query_ap_init - Initialize GAS query component + * @hapd: Pointer to hostapd data + * Returns: Pointer to GAS query data or %NULL on failure + */ +struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd, + void *msg_ctx) +{ + struct gas_query_ap *gas; + + gas = os_zalloc(sizeof(*gas)); + if (!gas) + return NULL; + + gas->hapd = hapd; + gas->msg_ctx = msg_ctx; + dl_list_init(&gas->pending); + + return gas; +} + + +static const char * gas_result_txt(enum gas_query_ap_result result) +{ + switch (result) { + case GAS_QUERY_AP_SUCCESS: + return "SUCCESS"; + case GAS_QUERY_AP_FAILURE: + return "FAILURE"; + case GAS_QUERY_AP_TIMEOUT: + return "TIMEOUT"; + case GAS_QUERY_AP_PEER_ERROR: + return "PEER_ERROR"; + case GAS_QUERY_AP_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case GAS_QUERY_AP_DELETED_AT_DEINIT: + return "DELETED_AT_DEINIT"; + } + + return "N/A"; +} + + +static void gas_query_free(struct gas_query_pending *query, int del_list) +{ + if (del_list) + dl_list_del(&query->list); + + wpabuf_free(query->req); + wpabuf_free(query->adv_proto); + wpabuf_free(query->resp); + os_free(query); +} + + +static void gas_query_done(struct gas_query_ap *gas, + struct gas_query_pending *query, + enum gas_query_ap_result result) +{ + wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR + " dialog_token=%u freq=%d status_code=%u result=%s", + MAC2STR(query->addr), query->dialog_token, query->freq, + query->status_code, gas_result_txt(result)); + if (gas->current == query) + gas->current = NULL; + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); + dl_list_del(&query->list); + query->cb(query->ctx, query->addr, query->dialog_token, result, + query->adv_proto, query->resp, query->status_code); + gas_query_free(query, 0); +} + + +/** + * gas_query_ap_deinit - Deinitialize GAS query component + * @gas: GAS query data from gas_query_init() + */ +void gas_query_ap_deinit(struct gas_query_ap *gas) +{ + struct gas_query_pending *query, *next; + + if (gas == NULL) + return; + + dl_list_for_each_safe(query, next, &gas->pending, + struct gas_query_pending, list) + gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT); + + os_free(gas); +} + + +static struct gas_query_pending * +gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token) +{ + struct gas_query_pending *q; + dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { + if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 && + q->dialog_token == dialog_token) + return q; + } + return NULL; +} + + +static int gas_query_append(struct gas_query_pending *query, const u8 *data, + size_t len) +{ + if (wpabuf_resize(&query->resp, len) < 0) { + wpa_printf(MSG_DEBUG, "GAS: No memory to store the response"); + return -1; + } + wpabuf_put_data(query->resp, data, len); + return 0; +} + + +void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst, + const u8 *data, size_t data_len, int ok) +{ + struct gas_query_pending *query; + int dur; + + if (!gas || !gas->current) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR + " ok=%d - no query in progress", MAC2STR(dst), ok); + return; + } + + query = gas->current; + + dur = ms_from_time(&query->last_oper); + wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR + " ok=%d query=%p dialog_token=%u dur=%d ms", + MAC2STR(dst), ok, query, query->dialog_token, dur); + if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination"); + return; + } + os_get_reltime(&query->last_oper); + + eloop_cancel_timeout(gas_query_timeout, gas, query); + if (!ok) { + wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request"); + eloop_register_timeout(0, 250000, gas_query_timeout, + gas, query); + } else { + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); + } + if (query->wait_comeback && !query->retry) { + eloop_cancel_timeout(gas_query_rx_comeback_timeout, + gas, query); + eloop_register_timeout( + 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000, + gas_query_rx_comeback_timeout, gas, query); + } +} + + +static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + return sta && (sta->flags & WLAN_STA_MFP); +} + + +static int gas_query_tx(struct gas_query_ap *gas, + struct gas_query_pending *query, + struct wpabuf *req, unsigned int wait_time) +{ + int res, prot = pmf_in_use(gas->hapd, query->addr); + + wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " + "freq=%d prot=%d using src addr " MACSTR, + MAC2STR(query->addr), (unsigned int) wpabuf_len(req), + query->freq, prot, MAC2STR(query->sa)); + if (prot) { + u8 *categ = wpabuf_mhead_u8(req); + *categ = WLAN_ACTION_PROTECTED_DUAL; + } + os_get_reltime(&query->last_oper); + res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time, + query->addr, wpabuf_head(req), + wpabuf_len(req)); + return res; +} + + +static void gas_query_tx_comeback_req(struct gas_query_ap *gas, + struct gas_query_pending *query) +{ + struct wpabuf *req; + unsigned int wait_time; + + req = gas_build_comeback_req(query->dialog_token); + if (req == NULL) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + wait_time = (query->retry || !query->offchannel_tx_started) ? + GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK; + + if (gas_query_tx(gas, query, req, wait_time) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + } + + wpabuf_free(req); +} + + +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + int dialog_token; + + wpa_printf(MSG_DEBUG, + "GAS: No response to comeback request received (retry=%u)", + query->retry); + if (gas->current != query || query->retry) + return; + dialog_token = gas_query_new_dialog_token(gas, query->addr); + if (dialog_token < 0) + return; + wpa_printf(MSG_DEBUG, + "GAS: Retry GAS query due to comeback response timeout"); + query->retry = 1; + query->dialog_token = dialog_token; + *(wpabuf_mhead_u8(query->req) + 2) = dialog_token; + query->wait_comeback = 0; + query->next_frag_id = 0; + wpabuf_free(query->adv_proto); + query->adv_proto = NULL; + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + gas_query_tx_initial_req(gas, query); +} + + +static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + + wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR, + MAC2STR(query->addr)); + gas_query_tx_comeback_req(gas, query); +} + + +static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas, + struct gas_query_pending *query, + u16 comeback_delay) +{ + unsigned int secs, usecs; + + secs = (comeback_delay * 1024) / 1000000; + usecs = comeback_delay * 1024 - secs * 1000000; + wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR + " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs); + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout, + gas, query); +} + + +static void gas_query_rx_initial(struct gas_query_ap *gas, + struct gas_query_pending *query, + const u8 *adv_proto, const u8 *resp, + size_t len, u16 comeback_delay) +{ + wpa_printf(MSG_DEBUG, "GAS: Received initial response from " + MACSTR " (dialog_token=%u comeback_delay=%u)", + MAC2STR(query->addr), query->dialog_token, comeback_delay); + + query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]); + if (query->adv_proto == NULL) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + if (comeback_delay) { + eloop_cancel_timeout(gas_query_timeout, gas, query); + query->wait_comeback = 1; + gas_query_tx_comeback_req_delay(gas, query, comeback_delay); + return; + } + + /* Query was completed without comeback mechanism */ + if (gas_query_append(query, resp, len) < 0) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS); +} + + +static void gas_query_rx_comeback(struct gas_query_ap *gas, + struct gas_query_pending *query, + const u8 *adv_proto, const u8 *resp, + size_t len, u8 frag_id, u8 more_frags, + u16 comeback_delay) +{ + wpa_printf(MSG_DEBUG, "GAS: Received comeback response from " + MACSTR " (dialog_token=%u frag_id=%u more_frags=%u " + "comeback_delay=%u)", + MAC2STR(query->addr), query->dialog_token, frag_id, + more_frags, comeback_delay); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); + + if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) || + os_memcmp(adv_proto, wpabuf_head(query->adv_proto), + wpabuf_len(query->adv_proto)) != 0) { + wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed " + "between initial and comeback response from " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + + if (comeback_delay) { + if (frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response " + "with non-zero frag_id and comeback_delay " + "from " MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + gas_query_tx_comeback_req_delay(gas, query, comeback_delay); + return; + } + + if (frag_id != query->next_frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response " + "from " MACSTR, MAC2STR(query->addr)); + if (frag_id + 1 == query->next_frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible " + "retry of previous fragment"); + return; + } + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + query->next_frag_id++; + + if (gas_query_append(query, resp, len) < 0) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + if (more_frags) { + gas_query_tx_comeback_req(gas, query); + return; + } + + gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS); +} + + +/** + * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual + * frame + * @gas: GAS query data from gas_query_init() + * @sa: Source MAC address of the Action frame + * @categ: Category of the Action frame + * @data: Payload of the Action frame + * @len: Length of @data + * @freq: Frequency (in MHz) on which the frame was received + * Returns: 0 if the Public Action frame was a GAS frame or -1 if not + */ +int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, + const u8 *data, size_t len, int freq) +{ + struct gas_query_pending *query; + u8 action, dialog_token, frag_id = 0, more_frags = 0; + u16 comeback_delay, resp_len; + const u8 *pos, *adv_proto; + int prot, pmf; + unsigned int left; + + if (!gas || len < 4) + return -1; + + pos = data; + action = *pos++; + dialog_token = *pos++; + + if (action != WLAN_PA_GAS_INITIAL_RESP && + action != WLAN_PA_GAS_COMEBACK_RESP) + return -1; /* Not a GAS response */ + + prot = categ == WLAN_ACTION_PROTECTED_DUAL; + pmf = pmf_in_use(gas->hapd, sa); + if (prot && !pmf) { + wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled"); + return 0; + } + if (!prot && pmf) { + wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled"); + return 0; + } + + query = gas_query_get_pending(gas, sa, dialog_token); + if (query == NULL) { + wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR + " dialog token %u", MAC2STR(sa), dialog_token); + return -1; + } + + wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR, + ms_from_time(&query->last_oper), MAC2STR(sa)); + + if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from " + MACSTR " dialog token %u when waiting for comeback " + "response", MAC2STR(sa), dialog_token); + return 0; + } + + if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from " + MACSTR " dialog token %u when waiting for initial " + "response", MAC2STR(sa), dialog_token); + return 0; + } + + query->status_code = WPA_GET_LE16(pos); + pos += 2; + + if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING && + action == WLAN_PA_GAS_COMEBACK_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response"); + } else if (query->status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token " + "%u failed - status code %u", + MAC2STR(sa), dialog_token, query->status_code); + gas_query_done(gas, query, GAS_QUERY_AP_FAILURE); + return 0; + } + + if (action == WLAN_PA_GAS_COMEBACK_RESP) { + if (pos + 1 > data + len) + return 0; + frag_id = *pos & 0x7f; + more_frags = (*pos & 0x80) >> 7; + pos++; + } + + /* Comeback Delay */ + if (pos + 2 > data + len) + return 0; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + + /* Advertisement Protocol element */ + if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) { + wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement " + "Protocol element in the response from " MACSTR, + MAC2STR(sa)); + return 0; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement " + "Protocol element ID %u in response from " MACSTR, + *pos, MAC2STR(sa)); + return 0; + } + + adv_proto = pos; + pos += 2 + pos[1]; + + /* Query Response Length */ + if (pos + 2 > data + len) { + wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length"); + return 0; + } + resp_len = WPA_GET_LE16(pos); + pos += 2; + + left = data + len - pos; + if (resp_len > left) { + wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in " + "response from " MACSTR, MAC2STR(sa)); + return 0; + } + + if (resp_len < left) { + wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data " + "after Query Response from " MACSTR, + left - resp_len, MAC2STR(sa)); + } + + if (action == WLAN_PA_GAS_COMEBACK_RESP) + gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len, + frag_id, more_frags, comeback_delay); + else + gas_query_rx_initial(gas, query, adv_proto, pos, resp_len, + comeback_delay); + + return 0; +} + + +static void gas_query_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + + wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR + " dialog token %u", + MAC2STR(query->addr), query->dialog_token); + gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT); +} + + +static int gas_query_dialog_token_available(struct gas_query_ap *gas, + const u8 *dst, u8 dialog_token) +{ + struct gas_query_pending *q; + dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { + if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 && + dialog_token == q->dialog_token) + return 0; + } + + return 1; +} + + +static void gas_query_tx_initial_req(struct gas_query_ap *gas, + struct gas_query_pending *query) +{ + if (gas_query_tx(gas, query, query->req, + GAS_QUERY_WAIT_TIME_INITIAL) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + gas->current = query; + + wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u", + query->dialog_token); + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); +} + + +static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst) +{ + static int next_start = 0; + int dialog_token; + + for (dialog_token = 0; dialog_token < 256; dialog_token++) { + if (gas_query_dialog_token_available( + gas, dst, (next_start + dialog_token) % 256)) + break; + } + if (dialog_token == 256) + return -1; /* Too many pending queries */ + dialog_token = (next_start + dialog_token) % 256; + next_start = (dialog_token + 1) % 256; + return dialog_token; +} + + +/** + * gas_query_ap_req - Request a GAS query + * @gas: GAS query data from gas_query_init() + * @dst: Destination MAC address for the query + * @freq: Frequency (in MHz) for the channel on which to send the query + * @req: GAS query payload (to be freed by gas_query module in case of success + * return) + * @cb: Callback function for reporting GAS query result and response + * @ctx: Context pointer to use with the @cb call + * Returns: dialog token (>= 0) on success or -1 on failure + */ +int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq, + struct wpabuf *req, + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code), + void *ctx) +{ + struct gas_query_pending *query; + int dialog_token; + + if (!gas || wpabuf_len(req) < 3) + return -1; + + dialog_token = gas_query_new_dialog_token(gas, dst); + if (dialog_token < 0) + return -1; + + query = os_zalloc(sizeof(*query)); + if (query == NULL) + return -1; + + query->gas = gas; + os_memcpy(query->addr, dst, ETH_ALEN); + query->dialog_token = dialog_token; + query->freq = freq; + query->cb = cb; + query->ctx = ctx; + query->req = req; + dl_list_add(&gas->pending, &query->list); + + *(wpabuf_mhead_u8(req) + 2) = dialog_token; + + wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR + " dialog_token=%u freq=%d", + MAC2STR(query->addr), query->dialog_token, query->freq); + + gas_query_tx_initial_req(gas, query); + + return dialog_token; +} --- contrib/wpa/src/ap/gas_query_ap.h.orig +++ contrib/wpa/src/ap/gas_query_ap.h @@ -0,0 +1,43 @@ +/* + * Generic advertisement service (GAS) query + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2017, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_QUERY_AP_H +#define GAS_QUERY_AP_H + +struct gas_query_ap; + +struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd, + void *msg_ctx); +void gas_query_ap_deinit(struct gas_query_ap *gas); +int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, + const u8 *data, size_t len, int freq); + +/** + * enum gas_query_ap_result - GAS query result + */ +enum gas_query_ap_result { + GAS_QUERY_AP_SUCCESS, + GAS_QUERY_AP_FAILURE, + GAS_QUERY_AP_TIMEOUT, + GAS_QUERY_AP_PEER_ERROR, + GAS_QUERY_AP_INTERNAL_ERROR, + GAS_QUERY_AP_DELETED_AT_DEINIT +}; + +int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq, + struct wpabuf *req, + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code), + void *ctx); +void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst, + const u8 *data, size_t data_len, int ok); + +#endif /* GAS_QUERY_AP_H */ --- contrib/wpa/src/ap/gas_serv.c.orig +++ contrib/wpa/src/ap/gas_serv.c @@ -11,14 +11,31 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" +#include "common/wpa_ctrl.h" #include "utils/eloop.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "dpp_hostapd.h" #include "sta_info.h" #include "gas_serv.h" +#ifdef CONFIG_DPP +static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf) +{ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 8); /* Length */ + wpabuf_put_u8(buf, 0x7f); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, DPP_OUI_TYPE); + wpabuf_put_u8(buf, 0x01); +} +#endif /* CONFIG_DPP */ + + static void convert_to_protected_dual(struct wpabuf *msg) { u8 *categ = wpabuf_mhead_u8(msg); @@ -50,9 +67,12 @@ sta->flags |= WLAN_STA_GAS; /* * The default inactivity is 300 seconds. We don't need - * it to be that long. + * it to be that long. Use five second timeout and increase this + * with the comeback_delay for testing cases. */ - ap_sta_session_timeout(hapd, sta, 5); + ap_sta_session_timeout(hapd, sta, + hapd->conf->gas_comeback_delay / 1024 + + 5); } else { ap_sta_replenish_timeout(hapd, sta, 5); } @@ -101,6 +121,7 @@ if (sta->gas_dialog[i].dialog_token != dialog_token || !sta->gas_dialog[i].valid) continue; + ap_sta_replenish_timeout(hapd, sta, 5); return &sta->gas_dialog[i]; } wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " @@ -160,34 +181,127 @@ wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); if (hapd->conf->hs20_osu_providers_count) wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); + if (hapd->conf->hs20_osu_providers_nai_count) + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); if (hapd->conf->hs20_icons_count) wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); + if (hapd->conf->hs20_operator_icon_count) + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); gas_anqp_set_element_len(buf, len); } #endif /* CONFIG_HS20 */ +static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd, + u16 infoid) +{ + struct anqp_element *elem; + + dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element, + list) { + if (elem->infoid == infoid) + return elem; + } + + return NULL; +} + + +static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf, + u16 infoid) +{ + struct anqp_element *elem; + + elem = get_anqp_elem(hapd, infoid); + if (!elem) + return; + if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) { + wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload", + infoid); + return; + } + + wpabuf_put_le16(buf, infoid); + wpabuf_put_le16(buf, wpabuf_len(elem->payload)); + wpabuf_put_buf(buf, elem->payload); +} + + +static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf, + u16 infoid) +{ + if (get_anqp_elem(hapd, infoid)) { + anqp_add_elem(hapd, buf, infoid); + return 1; + } + + return 0; +} + + static void anqp_add_capab_list(struct hostapd_data *hapd, struct wpabuf *buf) { u8 *len; + u16 id; + if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST)) + return; + len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); - if (hapd->conf->venue_name) + if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME)) wpabuf_put_le16(buf, ANQP_VENUE_NAME); - if (hapd->conf->network_auth_type) + if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER); + if (hapd->conf->network_auth_type || + get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE)) wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); - if (hapd->conf->roaming_consortium) + if (hapd->conf->roaming_consortium || + get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM)) wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); - if (hapd->conf->ipaddr_type_configured) + if (hapd->conf->ipaddr_type_configured || + get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY)) wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); - if (hapd->conf->nai_realm_data) + if (hapd->conf->nai_realm_data || + get_anqp_elem(hapd, ANQP_NAI_REALM)) wpabuf_put_le16(buf, ANQP_NAI_REALM); - if (hapd->conf->anqp_3gpp_cell_net) + if (hapd->conf->anqp_3gpp_cell_net || + get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK)) wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); - if (hapd->conf->domain_name) + if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION)) + wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION); + if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION)) + wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION); + if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI)) + wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI); + if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME)) wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); + if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY)) + wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY); + if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); + if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) + wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); +#ifdef CONFIG_FILS + if (!dl_list_empty(&hapd->conf->fils_realms) || + get_anqp_elem(hapd, ANQP_FILS_REALM_INFO)) + wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); +#endif /* CONFIG_FILS */ + if (get_anqp_elem(hapd, ANQP_CAG)) + wpabuf_put_le16(buf, ANQP_CAG); + if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL)) + wpabuf_put_le16(buf, ANQP_VENUE_URL); + if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) + wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); + if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) + wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); + for (id = 280; id < 300; id++) { + if (get_anqp_elem(hapd, id)) + wpabuf_put_le16(buf, id); + } #ifdef CONFIG_HS20 anqp_add_hs_capab_list(hapd, buf); #endif /* CONFIG_HS20 */ @@ -197,6 +311,9 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME)) + return; + if (hapd->conf->venue_name) { u8 *len; unsigned int i; @@ -215,9 +332,35 @@ } +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) { + if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE)) + return; + if (hapd->conf->network_auth_type) { wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); @@ -233,6 +376,9 @@ unsigned int i; u8 *len; + if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM)) + return; + len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { struct hostapd_roaming_consortium *rc; @@ -247,6 +393,9 @@ static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY)) + return; + if (hapd->conf->ipaddr_type_configured) { wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); wpabuf_put_le16(buf, 1); @@ -309,7 +458,7 @@ pos = home_realm; end = pos + home_realm_len; - if (pos + 1 > end) { + if (end - pos < 1) { wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", home_realm, home_realm_len); return -1; @@ -317,7 +466,7 @@ num_realms = *pos++; for (i = 0; i < num_realms && num_matching < 10; i++) { - if (pos + 2 > end) { + if (end - pos < 2) { wpa_hexdump(MSG_DEBUG, "Truncated NAI Home Realm Query", home_realm, home_realm_len); @@ -325,7 +474,7 @@ } encoding = *pos++; realm_len = *pos++; - if (pos + realm_len > end) { + if (realm_len > end - pos) { wpa_hexdump(MSG_DEBUG, "Truncated NAI Home Realm Query", home_realm, home_realm_len); @@ -391,6 +540,10 @@ const u8 *home_realm, size_t home_realm_len, int nai_realm, int nai_home_realm) { + if (nai_realm && !nai_home_realm && + anqp_add_override(hapd, buf, ANQP_NAI_REALM)) + return; + if (nai_realm && hapd->conf->nai_realm_data) { u8 *len; unsigned int i, j; @@ -424,6 +577,9 @@ static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK)) + return; + if (hapd->conf->anqp_3gpp_cell_net) { wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); wpabuf_put_le16(buf, @@ -436,6 +592,9 @@ static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME)) + return; + if (hapd->conf->domain_name) { wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); wpabuf_put_le16(buf, hapd->conf->domain_name_len); @@ -445,6 +604,36 @@ } +#ifdef CONFIG_FILS +static void anqp_add_fils_realm_info(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + size_t count; + + if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO)) + return; + + count = dl_list_len(&hapd->conf->fils_realms); + if (count > 10000) + count = 10000; + if (count) { + struct fils_realm *realm; + + wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); + wpabuf_put_le16(buf, 2 * count); + + dl_list_for_each(realm, &hapd->conf->fils_realms, + struct fils_realm, list) { + if (count == 0) + break; + wpabuf_put_data(buf, realm->hash, 2); + count--; + } + } +} +#endif /* CONFIG_FILS */ + + #ifdef CONFIG_HS20 static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, @@ -518,6 +707,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) @@ -546,32 +758,14 @@ /* OSU Method List */ count = wpabuf_put(buf, 1); - for (i = 0; p->method_list[i] >= 0; i++) + for (i = 0; p->method_list && p->method_list[i] >= 0; i++) wpabuf_put_u8(buf, p->method_list[i]); *count = i; /* Icons Available */ len2 = wpabuf_put(buf, 2); - for (i = 0; i < p->icons_count; i++) { - size_t j; - struct hs20_icon *icon = NULL; - - for (j = 0; j < bss->hs20_icons_count && !icon; j++) { - if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) == - 0) - icon = &bss->hs20_icons[j]; - } - if (!icon) - continue; /* icon info not found */ - - wpabuf_put_le16(buf, icon->width); - wpabuf_put_le16(buf, icon->height); - wpabuf_put_data(buf, icon->language, 3); - wpabuf_put_u8(buf, os_strlen(icon->type)); - wpabuf_put_str(buf, icon->type); - wpabuf_put_u8(buf, os_strlen(icon->name)); - wpabuf_put_str(buf, icon->name); - } + for (i = 0; i < p->icons_count; i++) + anqp_add_icon(buf, bss, p->icons[i]); WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); /* OSU_NAI */ @@ -625,6 +819,40 @@ } +static void anqp_add_osu_provider_nai(struct wpabuf *buf, + struct hs20_osu_provider *p) +{ + /* OSU_NAI for shared BSS (Single SSID) */ + if (p->osu_nai2) { + wpabuf_put_u8(buf, os_strlen(p->osu_nai2)); + wpabuf_put_str(buf, p->osu_nai2); + } else { + wpabuf_put_u8(buf, 0); + } +} + + +static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_osu_providers_nai_count) { + size_t i; + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + + for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { + anqp_add_osu_provider_nai( + buf, &hapd->conf->hs20_osu_providers[i]); + } + + gas_anqp_set_element_len(buf, len); + } +} + + static void anqp_add_icon_binary_file(struct hostapd_data *hapd, struct wpabuf *buf, const u8 *name, size_t name_len) @@ -680,17 +908,78 @@ gas_anqp_set_element_len(buf, len); } + +static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + struct hostapd_bss_config *bss = hapd->conf; + size_t i; + u8 *len; + + if (!bss->hs20_operator_icon_count) + return; + + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); + wpabuf_put_u8(buf, 0); /* Reserved */ + + for (i = 0; i < bss->hs20_operator_icon_count; i++) + anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]); + + gas_anqp_set_element_len(buf, len); +} + #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO +static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->mbo_cell_data_conn_pref >= 0) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); + wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref); + gas_anqp_set_element_len(buf, len); + } +} +#endif /* CONFIG_MBO */ + + +static size_t anqp_get_required_len(struct hostapd_data *hapd, + const u16 *infoid, + unsigned int num_infoid) +{ + size_t len = 0; + unsigned int i; + + for (i = 0; i < num_infoid; i++) { + struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]); + + if (elem) + len += 2 + 2 + wpabuf_len(elem->payload); + } + + return len; +} + + static struct wpabuf * gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, unsigned int request, const u8 *home_realm, size_t home_realm_len, - const u8 *icon_name, size_t icon_name_len) + const u8 *icon_name, size_t icon_name_len, + const u16 *extra_req, + unsigned int num_extra_req) { struct wpabuf *buf; size_t len; + unsigned int i; len = 1400; if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) @@ -697,6 +986,11 @@ len += 1000; if (request & ANQP_REQ_ICON_REQUEST) len += 65536; +#ifdef CONFIG_FILS + if (request & ANQP_FILS_REALM_INFO) + len += 2 * dl_list_len(&hapd->conf->fils_realms); +#endif /* CONFIG_FILS */ + len += anqp_get_required_len(hapd, extra_req, num_extra_req); buf = wpabuf_alloc(len); if (buf == NULL) @@ -706,6 +1000,8 @@ anqp_add_capab_list(hapd, buf); if (request & ANQP_REQ_VENUE_NAME) anqp_add_venue_name(hapd, buf); + if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER); if (request & ANQP_REQ_NETWORK_AUTH_TYPE) anqp_add_network_auth_type(hapd, buf); if (request & ANQP_REQ_ROAMING_CONSORTIUM) @@ -718,9 +1014,35 @@ request & ANQP_REQ_NAI_HOME_REALM); if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) anqp_add_3gpp_cellular_network(hapd, buf); + if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) + anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION); + if (request & ANQP_REQ_AP_CIVIC_LOCATION) + anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION); + if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) + anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI); if (request & ANQP_REQ_DOMAIN_NAME) anqp_add_domain_name(hapd, buf); + if (request & ANQP_REQ_EMERGENCY_ALERT_URI) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI); + if (request & ANQP_REQ_TDLS_CAPABILITY) + anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY); + if (request & ANQP_REQ_EMERGENCY_NAI) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); + for (i = 0; i < num_extra_req; i++) { +#ifdef CONFIG_FILS + if (extra_req[i] == ANQP_FILS_REALM_INFO) { + anqp_add_fils_realm_info(hapd, buf); + continue; + } +#endif /* CONFIG_FILS */ + if (extra_req[i] == ANQP_VENUE_URL) { + anqp_add_venue_url(hapd, buf); + continue; + } + anqp_add_elem(hapd, buf, extra_req[i]); + } + #ifdef CONFIG_HS20 if (request & ANQP_REQ_HS_CAPABILITY_LIST) anqp_add_hs_capab_list(hapd, buf); @@ -736,12 +1058,23 @@ anqp_add_osu_providers_list(hapd, buf); if (request & ANQP_REQ_ICON_REQUEST) anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); + if (request & ANQP_REQ_OPERATOR_ICON_METADATA) + anqp_add_operator_icon_metadata(hapd, buf); + if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST) + anqp_add_osu_providers_nai_list(hapd, buf); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) + anqp_add_mbo_cell_data_conn_pref(hapd, buf); +#endif /* CONFIG_MBO */ + return buf; } +#define ANQP_MAX_EXTRA_REQ 20 + struct anqp_query_info { unsigned int request; const u8 *home_realm_query; @@ -749,6 +1082,8 @@ const u8 *icon_name; size_t icon_name_len; int p2p_sd; + u16 extra_req[ANQP_MAX_EXTRA_REQ]; + unsigned int num_extra_req; }; @@ -776,6 +1111,11 @@ set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", hapd->conf->venue_name != NULL, qi); break; + case ANQP_EMERGENCY_CALL_NUMBER: + set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER, + "Emergency Call Number", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; case ANQP_NETWORK_AUTH_TYPE: set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", hapd->conf->network_auth_type != NULL, qi); @@ -798,13 +1138,65 @@ "3GPP Cellular Network", hapd->conf->anqp_3gpp_cell_net != NULL, qi); break; + case ANQP_AP_GEOSPATIAL_LOCATION: + set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION, + "AP Geospatial Location", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_AP_CIVIC_LOCATION: + set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION, + "AP Civic Location", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_AP_LOCATION_PUBLIC_URI: + set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI, + "AP Location Public URI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; case ANQP_DOMAIN_NAME: set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", hapd->conf->domain_name != NULL, qi); break; + case ANQP_EMERGENCY_ALERT_URI: + set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI, + "Emergency Alert URI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_TDLS_CAPABILITY: + set_anqp_req(ANQP_REQ_TDLS_CAPABILITY, + "TDLS Capability", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_EMERGENCY_NAI: + set_anqp_req(ANQP_REQ_EMERGENCY_NAI, + "Emergency NAI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; default: - wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", - info_id); +#ifdef CONFIG_FILS + if (info_id == ANQP_FILS_REALM_INFO && + !dl_list_empty(&hapd->conf->fils_realms)) { + wpa_printf(MSG_DEBUG, + "ANQP: FILS Realm Information (local)"); + } else +#endif /* CONFIG_FILS */ + if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) { + wpa_printf(MSG_DEBUG, + "ANQP: Venue URL (local)"); + } else if (!get_anqp_elem(hapd, info_id)) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", + info_id); + break; + } + if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) { + wpa_printf(MSG_DEBUG, + "ANQP: No more room for extra requests - ignore Info Id %u", + info_id); + break; + } + wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id); + qi->extra_req[qi->num_extra_req] = info_id; + qi->num_extra_req++; break; } } @@ -817,7 +1209,7 @@ wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", (unsigned int) (end - pos) / 2); - while (pos + 2 <= end) { + while (end - pos >= 2) { rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); pos += 2; } @@ -857,6 +1249,16 @@ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", hapd->conf->hs20_osu_providers_count, qi); break; + case HS20_STYPE_OPERATOR_ICON_METADATA: + set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA, + "Operator Icon Metadata", + hapd->conf->hs20_operator_icon_count, qi); + break; + case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: + set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST, + "OSU Providers NAI List", + hapd->conf->hs20_osu_providers_nai_count, qi); + break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", subtype); @@ -899,52 +1301,15 @@ } -static void rx_anqp_vendor_specific(struct hostapd_data *hapd, - const u8 *pos, const u8 *end, - struct anqp_query_info *qi) +static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) { - u32 oui; u8 subtype; - if (pos + 4 > end) { - wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " - "Query element"); + if (end - pos <= 1) return; - } - oui = WPA_GET_BE24(pos); - pos += 3; - if (oui != OUI_WFA) { - wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", - oui); - return; - } - -#ifdef CONFIG_P2P - if (*pos == P2P_OUI_TYPE) { - /* - * This is for P2P SD and will be taken care of by the P2P - * implementation. This query needs to be ignored in the generic - * GAS server to avoid duplicated response. - */ - wpa_printf(MSG_DEBUG, - "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", - *pos); - qi->p2p_sd = 1; - return; - } -#endif /* CONFIG_P2P */ - - if (*pos != HS20_ANQP_OUI_TYPE) { - wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", - *pos); - return; - } - pos++; - - if (pos + 1 >= end) - return; - subtype = *pos++; pos++; /* Reserved */ switch (subtype) { @@ -971,9 +1336,119 @@ #endif /* CONFIG_HS20 */ +#ifdef CONFIG_P2P +static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd, + struct anqp_query_info *qi) +{ + /* + * This is for P2P SD and will be taken care of by the P2P + * implementation. This query needs to be ignored in the generic + * GAS server to avoid duplicated response. + */ + wpa_printf(MSG_DEBUG, + "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", + P2P_OUI_TYPE); + qi->p2p_sd = 1; + return; +} +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_MBO + +static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype, + struct anqp_query_info *qi) +{ + switch (subtype) { + case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: + set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF, + "Cellular Data Connection Preference", + hapd->conf->mbo_cell_data_conn_pref >= 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u", + subtype); + break; + } +} + + +static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u8 subtype; + + if (end - pos < 1) + return; + + subtype = *pos++; + switch (subtype) { + case MBO_ANQP_SUBTYPE_QUERY_LIST: + wpa_printf(MSG_DEBUG, "ANQP: MBO Query List"); + while (pos < end) { + rx_anqp_mbo_query_list(hapd, *pos, qi); + pos++; + } + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u", + subtype); + break; + } +} + +#endif /* CONFIG_MBO */ + + +static void rx_anqp_vendor_specific(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u32 oui; + + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " + "Query element"); + return; + } + + oui = WPA_GET_BE24(pos); + pos += 3; + if (oui != OUI_WFA) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", + oui); + return; + } + + switch (*pos) { +#ifdef CONFIG_P2P + case P2P_OUI_TYPE: + rx_anqp_vendor_specific_p2p(hapd, qi); + break; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_HS20 + case HS20_ANQP_OUI_TYPE: + rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi); + break; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + case MBO_ANQP_OUI_TYPE: + rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi); + break; +#endif /* CONFIG_MBO */ + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", + *pos); + break; + } +} + + static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, - struct anqp_query_info *qi, int prot) + struct anqp_query_info *qi, int prot, + int std_addr3) { struct wpabuf *buf, *tx_buf; @@ -980,7 +1455,8 @@ buf = gas_serv_build_gas_resp_payload(hapd, qi->request, qi->home_realm_query, qi->home_realm_query_len, - qi->icon_name, qi->icon_name_len); + qi->icon_name, qi->icon_name_len, + qi->extra_req, qi->num_extra_req); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); if (!buf) @@ -994,7 +1470,7 @@ } #endif /* CONFIG_P2P */ - if (wpabuf_len(buf) > hapd->gas_frag_limit || + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || hapd->conf->gas_comeback_delay) { struct gas_dialog_info *di; u16 comeback_delay = 1; @@ -1033,15 +1509,88 @@ return; if (prot) convert_to_protected_dual(tx_buf); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +} + + +#ifdef CONFIG_DPP +static void gas_serv_req_dpp_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + int prot, struct wpabuf *buf) +{ + struct wpabuf *tx_buf; + + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || + hapd->conf->gas_comeback_delay) { + struct gas_dialog_info *di; + u16 comeback_delay = 1; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, + "DPP: Too long response to fit in initial response - use GAS comeback"); + di = gas_dialog_create(hapd, sa, dialog_token); + if (!di) { + wpa_printf(MSG_INFO, "DPP: Could not create dialog for " + MACSTR " (dialog token %u)", + MAC2STR(sa), dialog_token); + wpabuf_free(buf); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, + 0, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } else { + di->prot = prot; + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, + comeback_delay, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } + } else { + wpa_printf(MSG_DEBUG, + "DPP: GAS Initial response (no comeback)"); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, 0, + 10 + 2 + wpabuf_len(buf)); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_le16(tx_buf, wpabuf_len(buf)); + wpabuf_put_buf(tx_buf, buf); + hostapd_dpp_gas_status_handler(hapd, 1); + } + wpabuf_free(buf); + } + if (!tx_buf) + return; + if (prot) + convert_to_protected_dual(tx_buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); wpabuf_free(tx_buf); } +#endif /* CONFIG_DPP */ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, - const u8 *data, size_t len, int prot) + const u8 *data, size_t len, int prot, + int std_addr3) { const u8 *pos = data; const u8 *end = data + len; @@ -1050,6 +1599,9 @@ u16 slen; struct anqp_query_info qi; const u8 *adv_proto; +#ifdef CONFIG_DPP + int dpp = 0; +#endif /* CONFIG_DPP */ if (len < 1 + 2) return; @@ -1069,14 +1621,23 @@ adv_proto = pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Invalid IE in GAS Initial Request"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ +#ifdef CONFIG_DPP + if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC && + pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA && + pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) { + wpa_printf(MSG_DEBUG, "DPP: Configuration Request"); + dpp = 1; + } else +#endif /* CONFIG_DPP */ + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { struct wpabuf *buf; wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -1093,8 +1654,15 @@ wpabuf_put_le16(buf, 0); /* Query Response Length */ if (prot) convert_to_protected_dual(buf); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(buf), wpabuf_len(buf)); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(buf), + wpabuf_len(buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, + hapd->iface->freq, 0, + sa, wpabuf_head(buf), + wpabuf_len(buf)); wpabuf_free(buf); return; } @@ -1101,19 +1669,31 @@ pos = next; /* Query Request */ - if (pos + 2 > end) + if (end - pos < 2) return; slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end) + if (slen > end - pos) return; end = pos + slen; +#ifdef CONFIG_DPP + if (dpp) { + struct wpabuf *msg; + + msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen); + if (!msg) + return; + gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); + return; + } +#endif /* CONFIG_DPP */ + /* ANQP Query Request */ while (pos < end) { u16 info_id, elen; - if (pos + 4 > end) + if (end - pos < 4) return; info_id = WPA_GET_LE16(pos); @@ -1121,7 +1701,7 @@ elen = WPA_GET_LE16(pos); pos += 2; - if (pos + elen > end) { + if (elen > end - pos) { wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); return; } @@ -1130,11 +1710,9 @@ case ANQP_QUERY_LIST: rx_anqp_query_list(hapd, pos, pos + elen, &qi); break; -#ifdef CONFIG_HS20 case ANQP_VENDOR_SPECIFIC: rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); break; -#endif /* CONFIG_HS20 */ default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " "Request element %u", info_id); @@ -1144,13 +1722,15 @@ pos += elen; } - gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot); + gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot, + std_addr3); } static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, const u8 *sa, - const u8 *data, size_t len, int prot) + const u8 *data, size_t len, int prot, + int std_addr3) { struct gas_dialog_info *dialog; struct wpabuf *buf, *tx_buf; @@ -1182,8 +1762,8 @@ } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; - if (frag_len > hapd->gas_frag_limit) { - frag_len = hapd->gas_frag_limit; + if (frag_len > hapd->conf->gas_frag_limit) { + frag_len = hapd->conf->gas_frag_limit; more = 1; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", @@ -1196,6 +1776,18 @@ gas_serv_dialog_clear(dialog); return; } +#ifdef CONFIG_DPP + if (dialog->dpp) { + tx_buf = gas_build_comeback_resp(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, more, 0, + 10 + frag_len); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_buf(tx_buf, buf); + } + } else +#endif /* CONFIG_DPP */ tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, @@ -1219,6 +1811,10 @@ } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " "SD response sent"); +#ifdef CONFIG_DPP + if (dialog->dpp) + hostapd_dpp_gas_status_handler(hapd, 1); +#endif /* CONFIG_DPP */ gas_serv_dialog_clear(dialog); gas_serv_free_dialogs(hapd, sa); } @@ -1226,8 +1822,14 @@ send_resp: if (prot) convert_to_protected_dual(tx_buf); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); wpabuf_free(tx_buf); } @@ -1238,7 +1840,7 @@ struct hostapd_data *hapd = ctx; const struct ieee80211_mgmt *mgmt; const u8 *sa, *data; - int prot; + int prot, std_addr3; mgmt = (const struct ieee80211_mgmt *) buf; if (len < IEEE80211_HDRLEN + 2) @@ -1253,14 +1855,22 @@ */ prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; sa = mgmt->sa; + if (hapd->conf->gas_address3 == 1) + std_addr3 = 1; + else if (hapd->conf->gas_address3 == 2) + std_addr3 = 0; + else + std_addr3 = is_broadcast_ether_addr(mgmt->bssid); len -= IEEE80211_HDRLEN + 1; data = buf + IEEE80211_HDRLEN + 1; switch (data[0]) { case WLAN_PA_GAS_INITIAL_REQ: - gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot); + gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot, + std_addr3); break; case WLAN_PA_GAS_COMEBACK_REQ: - gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot); + gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot, + std_addr3); break; } } @@ -1270,9 +1880,6 @@ { hapd->public_action_cb2 = gas_serv_rx_public_action; hapd->public_action_cb2_ctx = hapd; - hapd->gas_frag_limit = 1400; - if (hapd->conf->gas_frag_limit > 0) - hapd->gas_frag_limit = hapd->conf->gas_frag_limit; return 0; } --- contrib/wpa/src/ap/gas_serv.h.orig +++ contrib/wpa/src/ap/gas_serv.h @@ -9,10 +9,13 @@ #ifndef GAS_SERV_H #define GAS_SERV_H +/* First 16 ANQP InfoIDs can be included in the optimized bitmap */ #define ANQP_REQ_CAPABILITY_LIST \ (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST)) #define ANQP_REQ_VENUE_NAME \ (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_CALL_NUMBER \ + (1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST)) #define ANQP_REQ_NETWORK_AUTH_TYPE \ (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) #define ANQP_REQ_ROAMING_CONSORTIUM \ @@ -23,8 +26,24 @@ (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) #define ANQP_REQ_3GPP_CELLULAR_NETWORK \ (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \ + (1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_CIVIC_LOCATION \ + (1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \ + (1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST)) #define ANQP_REQ_DOMAIN_NAME \ (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_ALERT_URI \ + (1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST)) +#define ANQP_REQ_TDLS_CAPABILITY \ + (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_NAI \ + (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST)) +/* + * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the + * optimized bitmap. + */ #define ANQP_REQ_HS_CAPABILITY_LIST \ (0x10000 << HS20_STYPE_CAPABILITY_LIST) #define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ @@ -41,6 +60,13 @@ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST) #define ANQP_REQ_ICON_REQUEST \ (0x10000 << HS20_STYPE_ICON_REQUEST) +#define ANQP_REQ_OPERATOR_ICON_METADATA \ + (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA) +#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \ + (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST) +/* The first MBO ANQP-element can be included in the optimized bitmap. */ +#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \ + (BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF) struct gas_dialog_info { u8 valid; @@ -49,6 +75,7 @@ size_t sd_resp_pos; /* Offset in sd_resp */ u8 sd_frag_id; int prot; /* whether Protected Dual of Public Action frame is used */ + int dpp; /* whether this is a DPP Config Response */ }; struct hostapd_data; --- contrib/wpa/src/ap/hostapd.c.orig +++ contrib/wpa/src/ap/hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/hw_features_common.h" #include "radius/radius_client.h" #include "radius/radius_das.h" #include "eap_server/tncs.h" @@ -30,6 +31,8 @@ #include "vlan_init.h" #include "wpa_auth.h" #include "wps_hostapd.h" +#include "dpp_hostapd.h" +#include "gas_query_ap.h" #include "hw_features.h" #include "wpa_auth_glue.h" #include "ap_drv_ops.h" @@ -42,6 +45,11 @@ #include "x_snoop.h" #include "dhcp_snoop.h" #include "ndisc_snoop.h" +#include "neighbor_db.h" +#include "rrm.h" +#include "fils_hlp.h" +#include "acs.h" +#include "hs20.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -49,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, @@ -68,10 +78,26 @@ } +void hostapd_reconfig_encryption(struct hostapd_data *hapd) +{ + if (hapd->wpa_auth) + return; + + hostapd_set_privacy(hapd, 0); + hostapd_setup_encryption(hapd->conf->iface, hapd); +} + + static void hostapd_reload_bss(struct hostapd_data *hapd) { struct hostapd_ssid *ssid; + if (!hapd->started) + return; + + if (hapd->conf->wmm_enabled < 0) + hapd->conf->wmm_enabled = hapd->iconf->ieee80211n; + #ifndef CONFIG_NO_RADIUS radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ @@ -150,8 +176,27 @@ } +static int hostapd_iface_conf_changed(struct hostapd_config *newconf, + struct hostapd_config *oldconf) +{ + size_t i; + + if (newconf->num_bss != oldconf->num_bss) + return 1; + + for (i = 0; i < newconf->num_bss; i++) { + if (os_strcmp(newconf->bss[i]->iface, + oldconf->bss[i]->iface) != 0) + return 1; + } + + return 0; +} + + int hostapd_reload_config(struct hostapd_iface *iface) { + struct hapd_interfaces *interfaces = iface->interfaces; struct hostapd_data *hapd = iface->bss[0]; struct hostapd_config *newconf, *oldconf; size_t j; @@ -174,6 +219,35 @@ hostapd_clear_old(iface); oldconf = hapd->iconf; + if (hostapd_iface_conf_changed(newconf, oldconf)) { + char *fname; + int res; + + wpa_printf(MSG_DEBUG, + "Configuration changes include interface/BSS modification - force full disable+enable sequence"); + fname = os_strdup(iface->config_fname); + if (!fname) { + hostapd_config_free(newconf); + return -1; + } + hostapd_remove_iface(interfaces, hapd->conf->iface); + iface = hostapd_init(interfaces, fname); + os_free(fname); + hostapd_config_free(newconf); + if (!iface) { + wpa_printf(MSG_ERROR, + "Failed to initialize interface on config reload"); + return -1; + } + iface->interfaces = interfaces; + interfaces->iface[interfaces->count] = iface; + interfaces->count++; + res = hostapd_enable_iface(iface); + if (res < 0) + wpa_printf(MSG_ERROR, + "Failed to enable interface on config reload"); + return res; + } iface->conf = newconf; for (j = 0; j < iface->num_bss; j++) { @@ -203,10 +277,12 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, - char *ifname) + const char *ifname) { int i; + if (!ifname || !hapd->drv_priv) + return; for (i = 0; i < NUM_WEP_KEYS; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0, NULL, 0, NULL, 0)) { @@ -272,10 +348,11 @@ if (!hapd->started) { wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", - __func__, hapd->conf->iface); + __func__, hapd->conf ? hapd->conf->iface : "N/A"); return; } hapd->started = 0; + hapd->beacon_set_done = 0; wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); iapp_deinit(hapd->iapp); @@ -292,6 +369,10 @@ #endif /* CONFIG_NO_RADIUS */ hostapd_deinit_wps(hapd); +#ifdef CONFIG_DPP + hostapd_dpp_deinit(hapd); + gas_query_ap_deinit(hapd->gas); +#endif /* CONFIG_DPP */ authsrv_deinit(hapd); @@ -334,6 +415,23 @@ wpabuf_free(hapd->mesh_pending_auth); hapd->mesh_pending_auth = NULL; #endif /* CONFIG_MESH */ + + hostapd_clean_rrm(hapd); + fils_hlp_deinit(hapd); + +#ifdef CONFIG_SAE + { + struct hostapd_sae_commit_queue *q; + + while ((q = dl_list_first(&hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, + list))) { + dl_list_del(&q->list); + os_free(q); + } + } + eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL); +#endif /* CONFIG_SAE */ } @@ -348,10 +446,12 @@ static void hostapd_cleanup(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, - hapd->conf->iface); + hapd->conf ? hapd->conf->iface : "N/A"); if (hapd->iface->interfaces && - hapd->iface->interfaces->ctrl_iface_deinit) + hapd->iface->interfaces->ctrl_iface_deinit) { + wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING); hapd->iface->interfaces->ctrl_iface_deinit(hapd); + } hostapd_free_hapd_data(hapd); } @@ -367,7 +467,7 @@ list))) { dl_list_del(&info->list); iface->num_sta_seen--; - os_free(info); + sta_track_del(info); } } @@ -380,8 +480,11 @@ hostapd_stop_setup_timers(iface); #endif /* NEED_AP_MLME */ #endif /* CONFIG_IEEE80211N */ + if (iface->current_mode) + acs_cleanup(iface); hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = NULL; + iface->current_mode = NULL; os_free(iface->current_rates); iface->current_rates = NULL; os_free(iface->basic_rates); @@ -402,6 +505,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); @@ -416,7 +521,7 @@ static void hostapd_clear_wep(struct hostapd_data *hapd) { - if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) { + if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) { hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); } @@ -477,9 +582,12 @@ ret = -1; } } - wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); - os_memset(addr, 0xff, ETH_ALEN); - hostapd_drv_sta_deauth(hapd, addr, reason); + if (hapd->conf && hapd->conf->broadcast_deauth) { + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Deauthenticate all stations"); + os_memset(addr, 0xff, ETH_ALEN); + hostapd_drv_sta_deauth(hapd, addr, reason); + } hostapd_free_stas(hapd); return ret; @@ -511,6 +619,9 @@ if (hostapd_drv_none(hapd)) return 0; + if (iface->conf->use_driver_iface_addr) + return 0; + /* Generate BSSID mask that is large enough to cover the BSSIDs. */ /* Determine the bits necessary to cover the number of BSSIDs. */ @@ -520,7 +631,7 @@ /* Determine the bits necessary to any configured BSSIDs, if they are higher than the number of BSSIDs. */ for (j = 0; j < iface->conf->num_bss; j++) { - if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) { + if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) { if (j) auto_addr++; continue; @@ -562,8 +673,10 @@ for (i = 5; i > 5 - j; i--) mask[i] = 0; j = bits % 8; - while (j--) + while (j) { + j--; mask[i] <<= 1; + } skip_mask_ext: wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", @@ -672,7 +785,7 @@ if (attr->acct_session_id) { num_attr++; - if (attr->acct_session_id_len != 17) { + if (attr->acct_session_id_len != 16) { wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id cannot match"); return NULL; @@ -682,10 +795,9 @@ for (sta = hapd->sta_list; sta; sta = sta->next) { if (!sta->radius_das_match) continue; - os_snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, - sta->acct_session_id_lo); - if (os_memcmp(attr->acct_session_id, buf, 17) != 0) + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) sta->acct_session_id); + if (os_memcmp(attr->acct_session_id, buf, 16) != 0) sta->radius_das_match = 0; else count++; @@ -701,7 +813,7 @@ if (attr->acct_multi_session_id) { num_attr++; - if (attr->acct_multi_session_id_len != 17) { + if (attr->acct_multi_session_id_len != 16) { wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Multi-Session-Id cannot match"); return NULL; @@ -712,14 +824,14 @@ if (!sta->radius_das_match) continue; if (!sta->eapol_sm || - !sta->eapol_sm->acct_multi_session_id_hi) { + !sta->eapol_sm->acct_multi_session_id) { sta->radius_das_match = 0; continue; } - os_snprintf(buf, sizeof(buf), "%08X+%08X", - sta->eapol_sm->acct_multi_session_id_hi, - sta->eapol_sm->acct_multi_session_id_lo); - if (os_memcmp(attr->acct_multi_session_id, buf, 17) != + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) + sta->eapol_sm->acct_multi_session_id); + if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) sta->radius_das_match = 0; else @@ -864,6 +976,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 */ @@ -905,12 +1059,9 @@ hapd->started = 1; if (!first || first == -1) { - if (hostapd_mac_comp_empty(conf->bssid) == 0) { - /* Allocate the next available BSSID. */ - do { - inc_byte_array(hapd->own_addr, ETH_ALEN); - } while (mac_in_conf(hapd->iconf, hapd->own_addr)); - } else { + u8 *addr = hapd->own_addr; + + if (!is_zero_ether_addr(conf->bssid)) { /* Allocate the configured BSSID. */ os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN); @@ -922,11 +1073,18 @@ "the radio", conf->iface); return -1; } + } else if (hapd->iconf->use_driver_iface_addr) { + addr = NULL; + } else { + /* Allocate the next available BSSID. */ + do { + inc_byte_array(hapd->own_addr, ETH_ALEN); + } while (mac_in_conf(hapd->iconf, hapd->own_addr)); } hapd->interface_added = 1; if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, - conf->iface, hapd->own_addr, hapd, + conf->iface, addr, hapd, &hapd->drv_priv, force_ifname, if_addr, conf->bridge[0] ? conf->bridge : NULL, first == -1)) { @@ -935,13 +1093,21 @@ hapd->interface_added = 0; return -1; } + + if (!addr) + os_memcpy(hapd->own_addr, if_addr, ETH_ALEN); } if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; +#ifdef CONFIG_IEEE80211R_AP + if (is_zero_ether_addr(conf->r1_key_holder)) + os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN); +#endif /* CONFIG_IEEE80211R_AP */ + #ifdef CONFIG_MESH - if (hapd->iface->mconf == NULL) + if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL) flush_old_stations = 0; #endif /* CONFIG_MESH */ @@ -1022,8 +1188,11 @@ das_conf.time_window = conf->radius_das_time_window; das_conf.require_event_timestamp = conf->radius_das_require_event_timestamp; + das_conf.require_message_authenticator = + 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 " @@ -1040,6 +1209,14 @@ if (hostapd_init_wps(hapd, conf)) return -1; +#ifdef CONFIG_DPP + hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx); + if (!hapd->gas) + return -1; + if (hostapd_dpp_init(hapd)) + return -1; +#endif /* CONFIG_DPP */ + if (authsrv_init(hapd) < 0) return -1; @@ -1127,7 +1304,7 @@ struct hostapd_tx_queue_params *p; #ifdef CONFIG_MESH - if (iface->mconf == NULL) + if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL) return; #endif /* CONFIG_MESH */ @@ -1508,17 +1685,119 @@ #endif /* CONFIG_FST */ +#ifdef CONFIG_OWE -/** - * hostapd_setup_interface_complete - Complete interface setup - * - * This function is called when previous steps in the interface setup has been - * completed. This can also start operations, e.g., DFS, that will require - * additional processing before interface is ready to be enabled. Such - * operations will call this function from eloop callbacks when finished. - */ -int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) +static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx) { + struct hostapd_data *hapd = ctx; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + + if (os_strcmp(hapd->conf->owe_transition_ifname, + bss->conf->iface) != 0) + continue; + + wpa_printf(MSG_DEBUG, + "OWE: ifname=%s found transition mode ifname=%s BSSID " + MACSTR " SSID %s", + hapd->conf->iface, bss->conf->iface, + MAC2STR(bss->own_addr), + wpa_ssid_txt(bss->conf->ssid.ssid, + bss->conf->ssid.ssid_len)); + if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len || + is_zero_ether_addr(bss->own_addr)) + continue; + + os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr, + ETH_ALEN); + os_memcpy(hapd->conf->owe_transition_ssid, + bss->conf->ssid.ssid, bss->conf->ssid.ssid_len); + hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len; + wpa_printf(MSG_DEBUG, + "OWE: Copied transition mode information"); + return 1; + } + + return 0; +} + + +int hostapd_owe_trans_get_info(struct hostapd_data *hapd) +{ + if (hapd->conf->owe_transition_ssid_len > 0 && + !is_zero_ether_addr(hapd->conf->owe_transition_bssid)) + return 0; + + /* Find transition mode SSID/BSSID information from a BSS operated by + * this hostapd instance. */ + if (!hapd->iface->interfaces || + !hapd->iface->interfaces->for_each_interface) + return hostapd_owe_iface_iter(hapd->iface, hapd); + else + return hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_owe_iface_iter, hapd); +} + + +static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx) +{ + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + int res; + + if (!bss->conf->owe_transition_ifname[0]) + continue; + res = hostapd_owe_trans_get_info(bss); + if (res == 0) + continue; + wpa_printf(MSG_DEBUG, + "OWE: Matching transition mode interface enabled - update beacon data for %s", + bss->conf->iface); + ieee802_11_set_beacon(bss); + } + + return 0; +} + +#endif /* CONFIG_OWE */ + + +static void hostapd_owe_update_trans(struct hostapd_iface *iface) +{ +#ifdef CONFIG_OWE + /* Check whether the enabled BSS can complete OWE transition mode + * configuration for any pending interface. */ + if (!iface->interfaces || + !iface->interfaces->for_each_interface) + hostapd_owe_iface_iter2(iface, NULL); + else + iface->interfaces->for_each_interface( + iface->interfaces, hostapd_owe_iface_iter2, NULL); +#endif /* CONFIG_OWE */ +} + + +static void hostapd_interface_setup_failure_handler(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + struct hostapd_data *hapd; + + if (iface->num_bss < 1 || !iface->bss || !iface->bss[0]) + return; + hapd = iface->bss[0]; + if (hapd->setup_complete_cb) + hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); +} + + +static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, + int err) +{ struct hostapd_data *hapd = iface->bss[0]; size_t j; u8 *prev_addr; @@ -1605,15 +1884,17 @@ } } - if (hapd->iconf->rts_threshold > -1 && - hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { + if (hapd->iconf->rts_threshold >= -1 && + hostapd_set_rts(hapd, hapd->iconf->rts_threshold) && + hapd->iconf->rts_threshold >= -1) { wpa_printf(MSG_ERROR, "Could not set RTS threshold for " "kernel driver"); goto fail; } - if (hapd->iconf->fragm_threshold > -1 && - hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { + if (hapd->iconf->fragm_threshold >= -1 && + hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) && + hapd->iconf->fragm_threshold != -1) { wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " "for kernel driver"); goto fail; @@ -1626,14 +1907,17 @@ if (j) os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); if (hostapd_setup_bss(hapd, j == 0)) { - do { + for (;;) { hapd = iface->bss[j]; hostapd_bss_deinit_no_free(hapd); hostapd_free_hapd_data(hapd); - } while (j-- > 0); + if (j == 0) + break; + j--; + } goto fail; } - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) + if (is_zero_ether_addr(hapd->conf->bssid)) prev_addr = hapd->own_addr; } hapd = iface->bss[0]; @@ -1641,7 +1925,6 @@ hostapd_tx_queue_params(iface); ap_list_init(iface); - dl_list_init(&iface->sta_seen); hostapd_set_acl(hapd); @@ -1692,6 +1975,7 @@ #endif /* CONFIG_FST */ hostapd_set_state(iface, HAPD_IFACE_ENABLED); + hostapd_owe_update_trans(iface); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); @@ -1701,6 +1985,9 @@ if (iface->interfaces && iface->interfaces->terminate_on_error > 0) iface->interfaces->terminate_on_error--; + for (j = 0; j < iface->num_bss; j++) + hostapd_neighbor_set_own_report(iface->bss[j]); + return 0; fail: @@ -1713,13 +2000,107 @@ 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; } /** + * hostapd_setup_interface_complete - Complete interface setup + * + * This function is called when previous steps in the interface setup has been + * completed. This can also start operations, e.g., DFS, that will require + * additional processing before interface is ready to be enabled. Such + * operations will call this function from eloop callbacks when finished. + */ +int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) +{ + struct hapd_interfaces *interfaces = iface->interfaces; + struct hostapd_data *hapd = iface->bss[0]; + unsigned int i; + int not_ready_in_sync_ifaces = 0; + + if (!iface->need_to_start_in_sync) + return hostapd_setup_interface_complete_sync(iface, err); + + if (err) { + wpa_printf(MSG_ERROR, "Interface initialization failed"); + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + iface->need_to_start_in_sync = 0; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + if (interfaces && interfaces->terminate_on_error) + eloop_terminate(); + return -1; + } + + if (iface->ready_to_start_in_sync) { + /* Already in ready and waiting. should never happpen */ + return 0; + } + + for (i = 0; i < interfaces->count; i++) { + if (interfaces->iface[i]->need_to_start_in_sync && + !interfaces->iface[i]->ready_to_start_in_sync) + not_ready_in_sync_ifaces++; + } + + /* + * Check if this is the last interface, if yes then start all the other + * waiting interfaces. If not, add this interface to the waiting list. + */ + if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) { + /* + * If this interface went through CAC, do not synchronize, just + * start immediately. + */ + iface->need_to_start_in_sync = 0; + wpa_printf(MSG_INFO, + "%s: Finished CAC - bypass sync and start interface", + iface->bss[0]->conf->iface); + return hostapd_setup_interface_complete_sync(iface, err); + } + + if (not_ready_in_sync_ifaces > 1) { + /* need to wait as there are other interfaces still coming up */ + iface->ready_to_start_in_sync = 1; + wpa_printf(MSG_INFO, + "%s: Interface waiting to sync with other interfaces", + iface->bss[0]->conf->iface); + return 0; + } + + wpa_printf(MSG_INFO, + "%s: Last interface to sync - starting all interfaces", + iface->bss[0]->conf->iface); + iface->need_to_start_in_sync = 0; + hostapd_setup_interface_complete_sync(iface, err); + for (i = 0; i < interfaces->count; i++) { + if (interfaces->iface[i]->need_to_start_in_sync && + interfaces->iface[i]->ready_to_start_in_sync) { + hostapd_setup_interface_complete_sync( + interfaces->iface[i], 0); + /* Only once the interfaces are sync started */ + interfaces->iface[i]->need_to_start_in_sync = 0; + } + } + + return 0; +} + + +/** * hostapd_setup_interface - Setup of an interface * @iface: Pointer to interface data. * Returns: 0 on success, -1 on failure @@ -1776,8 +2157,19 @@ hapd->iconf = conf; hapd->conf = bss; hapd->iface = hapd_iface; - hapd->driver = hapd->iconf->driver; + if (conf) + hapd->driver = conf->driver; hapd->ctrl_sock = -1; + dl_list_init(&hapd->ctrl_dst); + dl_list_init(&hapd->nr_db); + hapd->dhcp_sock = -1; +#ifdef CONFIG_IEEE80211R_AP + dl_list_init(&hapd->l2_queue); + dl_list_init(&hapd->l2_oui_queue); +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_SAE + dl_list_init(&hapd->sae_commit_queue); +#endif /* CONFIG_SAE */ return hapd; } @@ -1785,8 +2177,10 @@ static void hostapd_bss_deinit(struct hostapd_data *hapd) { + if (!hapd) + return; wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, - hapd->conf->iface); + hapd->conf ? hapd->conf->iface : "N/A"); hostapd_bss_deinit_no_free(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); hostapd_cleanup(hapd); @@ -1803,12 +2197,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; @@ -1819,8 +2207,18 @@ } #endif /* CONFIG_FST */ - for (j = iface->num_bss - 1; j >= 0; j--) + for (j = (int) iface->num_bss - 1; j >= 0; j--) { + if (!iface->bss) + 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 */ } @@ -1829,6 +2227,8 @@ size_t j; wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); for (j = 0; j < iface->num_bss; j++) { + if (!iface->bss) + break; wpa_printf(MSG_DEBUG, "%s: free hapd %p", __func__, iface->bss[j]); os_free(iface->bss[j]); @@ -1837,6 +2237,20 @@ } +struct hostapd_iface * hostapd_alloc_iface(void) +{ + struct hostapd_iface *hapd_iface; + + hapd_iface = os_zalloc(sizeof(*hapd_iface)); + if (!hapd_iface) + return NULL; + + dl_list_init(&hapd_iface->sta_seen); + + return hapd_iface; +} + + /** * hostapd_init - Allocate and initialize per-interface data * @config_file: Path to the configuration file @@ -1854,7 +2268,7 @@ struct hostapd_data *hapd; size_t i; - hapd_iface = os_zalloc(sizeof(*hapd_iface)); + hapd_iface = hostapd_alloc_iface(); if (hapd_iface == NULL) goto fail; @@ -2158,6 +2572,11 @@ !!(hapd_iface->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); +#ifdef NEED_AP_MLME + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_cleanup_cs_params(hapd_iface->bss[j]); +#endif /* NEED_AP_MLME */ + /* same as hostapd_interface_deinit without deinitializing ctrl-iface */ for (j = 0; j < hapd_iface->num_bss; j++) { struct hostapd_data *hapd = hapd_iface->bss[j]; @@ -2190,7 +2609,7 @@ return NULL; interfaces->iface = iface; hapd_iface = interfaces->iface[interfaces->count] = - os_zalloc(sizeof(*hapd_iface)); + hostapd_alloc_iface(); if (hapd_iface == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "the interface", __func__); @@ -2215,7 +2634,7 @@ if (conf == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "configuration", __func__); - return NULL; + return NULL; } if (driver) { @@ -2368,6 +2787,7 @@ return -1; } } + hostapd_owe_update_trans(hapd_iface); return 0; } @@ -2557,6 +2977,7 @@ } hostapd_prune_associations(hapd, sta->addr); + ap_sta_clear_disconnect_timeouts(hapd, sta); /* IEEE 802.11F (IAPP) */ if (hapd->conf->ieee802_11f) @@ -2584,15 +3005,28 @@ ieee802_1x_new_station(hapd, sta); if (reassoc) { if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK && !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); } else wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); - if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { - wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " - "for " MACSTR " (%d seconds - ap_max_inactivity)", - __func__, MAC2STR(sta->addr), + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) { + if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) { + wpa_printf(MSG_DEBUG, + "%s: %s: canceled wired ap_handle_timer timeout for " + MACSTR, + hapd->conf->iface, __func__, + MAC2STR(sta->addr)); + } + } else if (!(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + wpa_printf(MSG_DEBUG, + "%s: %s: reschedule ap_handle_timer timeout for " + MACSTR " (%d seconds - ap_max_inactivity)", + hapd->conf->iface, __func__, MAC2STR(sta->addr), hapd->conf->ap_max_inactivity); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, @@ -2627,12 +3061,23 @@ void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s) { wpa_printf(MSG_INFO, "%s: interface state %s->%s", - iface->conf->bss[0]->iface, hostapd_state_text(iface->state), - hostapd_state_text(s)); + iface->conf ? iface->conf->bss[0]->iface : "N/A", + hostapd_state_text(iface->state), hostapd_state_text(s)); iface->state = s; } +int hostapd_csa_in_progress(struct hostapd_iface *iface) +{ + unsigned int i; + + for (i = 0; i < iface->num_bss; i++) + if (iface->bss[i]->csa_in_progress) + return 1; + return 0; +} + + #ifdef NEED_AP_MLME static void free_beacon_data(struct beacon_data *beacon) @@ -2671,60 +3116,52 @@ goto free_ap_params; ret = -1; - beacon->head = os_malloc(params.head_len); + beacon->head = os_memdup(params.head, params.head_len); if (!beacon->head) goto free_ap_extra_ies; - os_memcpy(beacon->head, params.head, params.head_len); beacon->head_len = params.head_len; - beacon->tail = os_malloc(params.tail_len); + beacon->tail = os_memdup(params.tail, params.tail_len); if (!beacon->tail) goto free_beacon; - os_memcpy(beacon->tail, params.tail, params.tail_len); beacon->tail_len = params.tail_len; if (params.proberesp != NULL) { - beacon->probe_resp = os_malloc(params.proberesp_len); + beacon->probe_resp = os_memdup(params.proberesp, + params.proberesp_len); if (!beacon->probe_resp) goto free_beacon; - os_memcpy(beacon->probe_resp, params.proberesp, - params.proberesp_len); beacon->probe_resp_len = params.proberesp_len; } /* copy the extra ies */ if (beacon_extra) { - beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra)); + beacon->beacon_ies = os_memdup(beacon_extra->buf, + wpabuf_len(beacon_extra)); if (!beacon->beacon_ies) goto free_beacon; - os_memcpy(beacon->beacon_ies, - beacon_extra->buf, wpabuf_len(beacon_extra)); beacon->beacon_ies_len = wpabuf_len(beacon_extra); } if (proberesp_extra) { - beacon->proberesp_ies = - os_malloc(wpabuf_len(proberesp_extra)); + beacon->proberesp_ies = os_memdup(proberesp_extra->buf, + wpabuf_len(proberesp_extra)); if (!beacon->proberesp_ies) goto free_beacon; - os_memcpy(beacon->proberesp_ies, proberesp_extra->buf, - wpabuf_len(proberesp_extra)); beacon->proberesp_ies_len = wpabuf_len(proberesp_extra); } if (assocresp_extra) { - beacon->assocresp_ies = - os_malloc(wpabuf_len(assocresp_extra)); + beacon->assocresp_ies = os_memdup(assocresp_extra->buf, + wpabuf_len(assocresp_extra)); if (!beacon->assocresp_ies) goto free_beacon; - os_memcpy(beacon->assocresp_ies, assocresp_extra->buf, - wpabuf_len(assocresp_extra)); beacon->assocresp_ies_len = wpabuf_len(assocresp_extra); } @@ -2744,9 +3181,9 @@ /* - * TODO: This flow currently supports only changing frequency within the - * same hw_mode. Any other changes to MAC parameters or provided settings (even - * width) are not supported. + * TODO: This flow currently supports only changing channel and width within + * the same hw_mode. Any other changes to MAC parameters or provided settings + * are not supported. */ static int hostapd_change_config_freq(struct hostapd_data *hapd, struct hostapd_config *conf, @@ -2765,15 +3202,44 @@ return -1; /* if a pointer to old_params is provided we save previous state */ - if (old_params) { - old_params->channel = conf->channel; - old_params->ht_enabled = conf->ieee80211n; - old_params->sec_channel_offset = conf->secondary_channel; + if (old_params && + hostapd_set_freq_params(old_params, conf->hw_mode, + hostapd_hw_get_freq(hapd, conf->channel), + conf->channel, conf->ieee80211n, + conf->ieee80211ac, + conf->secondary_channel, + conf->vht_oper_chwidth, + conf->vht_oper_centr_freq_seg0_idx, + conf->vht_oper_centr_freq_seg1_idx, + conf->vht_capab)) + return -1; + + switch (params->bandwidth) { + case 0: + case 20: + case 40: + conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + break; + case 80: + if (params->center_freq2) + conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; + else + conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + break; + case 160: + conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; + break; + default: + return -1; } conf->channel = channel; conf->ieee80211n = params->ht_enabled; conf->secondary_channel = params->sec_channel_offset; + ieee80211_freq_to_chan(params->center_freq1, + &conf->vht_oper_centr_freq_seg0_idx); + ieee80211_freq_to_chan(params->center_freq2, + &conf->vht_oper_centr_freq_seg1_idx); /* TODO: maybe call here hostapd_config_check here? */ @@ -2787,11 +3253,43 @@ struct hostapd_iface *iface = hapd->iface; struct hostapd_freq_params old_freq; int ret; + u8 chan, vht_bandwidth; os_memset(&old_freq, 0, sizeof(old_freq)); if (!iface || !iface->freq || hapd->csa_in_progress) return -1; + switch (settings->freq_params.bandwidth) { + case 80: + if (settings->freq_params.center_freq2) + vht_bandwidth = VHT_CHANWIDTH_80P80MHZ; + else + vht_bandwidth = VHT_CHANWIDTH_80MHZ; + break; + case 160: + vht_bandwidth = VHT_CHANWIDTH_160MHZ; + break; + default: + vht_bandwidth = VHT_CHANWIDTH_USE_HT; + break; + } + + if (ieee80211_freq_to_channel_ext( + settings->freq_params.freq, + settings->freq_params.sec_channel_offset, + vht_bandwidth, + &hapd->iface->cs_oper_class, + &chan) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)", + settings->freq_params.freq, + settings->freq_params.sec_channel_offset, + settings->freq_params.vht_enabled); + return -1; + } + + settings->freq_params.channel = chan; + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, &settings->freq_params, &old_freq); @@ -2818,8 +3316,10 @@ return ret; } - settings->counter_offset_beacon = hapd->cs_c_off_beacon; - settings->counter_offset_presp = hapd->cs_c_off_proberesp; + settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon; + settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp; + settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon; + settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp; return 0; } @@ -2833,9 +3333,24 @@ hapd->cs_c_off_beacon = 0; hapd->cs_c_off_proberesp = 0; hapd->csa_in_progress = 0; + hapd->cs_c_off_ecsa_beacon = 0; + hapd->cs_c_off_ecsa_proberesp = 0; } +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) { @@ -2870,7 +3385,6 @@ const struct hostapd_freq_params *freq_params) { int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT; - unsigned int i; wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes"); @@ -2912,15 +3426,15 @@ /* * cs_params must not be cleared earlier because the freq_params * argument may actually point to one of these. + * These params will be cleared during interface disable below. */ - for (i = 0; i < iface->num_bss; i++) - hostapd_cleanup_cs_params(iface->bss[i]); - hostapd_disable_iface(iface); hostapd_enable_iface(iface); } +#endif /* NEED_AP_MLME */ + struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, const char *ifname) { @@ -2940,9 +3454,7 @@ return NULL; } -#endif /* NEED_AP_MLME */ - void hostapd_periodic_iface(struct hostapd_iface *iface) { size_t i; --- contrib/wpa/src/ap/hostapd.h.orig +++ contrib/wpa/src/ap/hostapd.h @@ -14,6 +14,13 @@ #include "ap_config.h" #include "drivers/driver.h" +#define OCE_STA_CFON_ENABLED(hapd) \ + ((hapd->conf->oce & OCE_STA_CFON) && \ + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) +#define OCE_AP_ENABLED(hapd) \ + ((hapd->conf->oce & OCE_AP) && \ + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP)) + struct wpa_ctrl_dst; struct radius_server_data; struct upnp_wps_device_sm; @@ -41,7 +48,7 @@ size_t count; int global_ctrl_sock; - struct wpa_ctrl_dst *global_ctrl_dst; + struct dl_list global_ctrl_dst; char *global_iface_path; char *global_iface_name; #ifndef CONFIG_NATIVE_WINDOWS @@ -53,6 +60,14 @@ #ifndef CONFIG_NO_VLAN struct dynamic_iface *vlan_priv; #endif /* CONFIG_NO_VLAN */ +#ifdef CONFIG_ETH_P_OUI + struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */ +#endif /* CONFIG_ETH_P_OUI */ + int eloop_initialized; + +#ifdef CONFIG_DPP + struct dpp_global *dpp; +#endif /* CONFIG_DPP */ }; enum hostapd_chan_status { @@ -75,6 +90,7 @@ }; struct hostapd_frame_info { + unsigned int freq; u32 channel; u32 datarate; int ssi_signal; /* dBm */ @@ -99,7 +115,25 @@ u8 peer_addr[ETH_ALEN]; }; +struct hostapd_neighbor_entry { + struct dl_list list; + u8 bssid[ETH_ALEN]; + struct wpa_ssid_value ssid; + struct wpabuf *nr; + struct wpabuf *lci; + struct wpabuf *civic; + /* LCI update time */ + struct os_time lci_date; + int stationary; +}; +struct hostapd_sae_commit_queue { + struct dl_list list; + int rssi; + size_t len; + u8 msg[]; +}; + /** * struct hostapd_data - hostapd per-BSS data structure */ @@ -138,7 +172,7 @@ void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ struct radius_client_data *radius; - u32 acct_session_id_hi, acct_session_id_lo; + u64 acct_session_id; struct radius_das_data *radius_das; struct iapp_data *iapp; @@ -155,7 +189,7 @@ int tkip_countermeasures; int ctrl_sock; - struct wpa_ctrl_dst *ctrl_dst; + struct dl_list ctrl_dst; void *ssl_ctx; void *eap_sim_db_priv; @@ -173,6 +207,17 @@ #endif /* CONFIG_FULL_DYNAMIC_VLAN */ struct l2_packet_data *l2; + +#ifdef CONFIG_IEEE80211R_AP + struct dl_list l2_queue; + struct dl_list l2_oui_queue; + struct eth_p_oui_ctx *oui_pull; + struct eth_p_oui_ctx *oui_resp; + struct eth_p_oui_ctx *oui_push; + struct eth_p_oui_ctx *oui_sreq; + struct eth_p_oui_ctx *oui_sresp; +#endif /* CONFIG_IEEE80211R_AP */ + struct wps_context *wps; int beacon_set_done; @@ -228,10 +273,9 @@ unsigned int cs_c_off_beacon; unsigned int cs_c_off_proberesp; int csa_in_progress; + 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; @@ -246,9 +290,6 @@ int noa_start; int noa_duration; #endif /* CONFIG_P2P */ -#ifdef CONFIG_INTERWORKING - size_t gas_frag_limit; -#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_PROXYARP struct l2_packet_data *sock_dhcp; struct l2_packet_data *sock_ndisc; @@ -256,9 +297,11 @@ #ifdef CONFIG_MESH int num_plinks; int max_plinks; - void (*mesh_sta_free_cb)(struct sta_info *sta); + void (*mesh_sta_free_cb)(struct hostapd_data *hapd, + struct sta_info *sta); struct wpabuf *mesh_pending_auth; struct os_reltime mesh_pending_auth_time; + u8 mesh_required_peer[ETH_ALEN]; #endif /* CONFIG_MESH */ #ifdef CONFIG_SQLITE @@ -269,7 +312,10 @@ /** Key used for generating SAE anti-clogging tokens */ u8 sae_token_key[8]; struct os_reltime last_sae_token_key_update; + u16 sae_token_idx; + u16 sae_pending_token_idx[256]; int dot11RSNASAERetransPeriod; /* msec */ + struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */ #endif /* CONFIG_SAE */ #ifdef CONFIG_TESTING_OPTIONS @@ -277,7 +323,62 @@ unsigned int ext_eapol_frame_io:1; struct l2_packet_data *l2_test; + + enum wpa_alg last_gtk_alg; + int last_gtk_key_idx; + u8 last_gtk[WPA_GTK_MAX_LEN]; + size_t last_gtk_len; + +#ifdef CONFIG_IEEE80211W + enum wpa_alg last_igtk_alg; + int last_igtk_key_idx; + u8 last_igtk[WPA_IGTK_MAX_LEN]; + size_t last_igtk_len; +#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_MBO + unsigned int mbo_assoc_disallow; +#endif /* CONFIG_MBO */ + + struct dl_list nr_db; + + u8 beacon_req_token; + u8 lci_req_token; + u8 range_req_token; + unsigned int lci_req_active:1; + unsigned int range_req_active:1; + + int dhcp_sock; /* UDP socket used with the DHCP server */ + +#ifdef CONFIG_DPP + int dpp_init_done; + struct dpp_authentication *dpp_auth; + u8 dpp_allowed_roles; + int dpp_qr_mutual; + int dpp_auth_ok_on_ack; + int dpp_in_response_listen; + struct gas_query_ap *gas; + struct dpp_pkex *dpp_pkex; + struct dpp_bootstrap_info *dpp_pkex_bi; + char *dpp_pkex_code; + char *dpp_pkex_identifier; + char *dpp_pkex_auth_cmd; + char *dpp_configurator_params; + struct os_reltime dpp_last_init; + struct os_reltime dpp_init_iter_start; + unsigned int dpp_init_max_tries; + unsigned int dpp_init_retry_time; + unsigned int dpp_resp_wait_time; + unsigned int dpp_resp_max_tries; + unsigned int dpp_resp_retry_time; +#ifdef CONFIG_TESTING_OPTIONS + char *dpp_config_obj_override; + char *dpp_discovery_override; + char *dpp_groups_override; + unsigned int dpp_ignore_netaccesskey_mismatch:1; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_DPP */ }; @@ -285,6 +386,10 @@ struct dl_list list; u8 addr[ETH_ALEN]; struct os_reltime last_seen; + int ssi_signal; +#ifdef CONFIG_TAXONOMY + struct wpabuf *probe_ie_taxonomy; +#endif /* CONFIG_TAXONOMY */ }; /** @@ -327,6 +432,15 @@ */ unsigned int driver_ap_teardown:1; + /* + * When set, indicates that this interface is part of list of + * interfaces that need to be started together (synchronously). + */ + unsigned int need_to_start_in_sync:1; + + /* Ready to start but waiting for other interfaces to become ready. */ + unsigned int ready_to_start_in_sync:1; + int num_ap; /* number of entries in ap_list */ struct ap_info *ap_list; /* AP info list head */ struct ap_info *ap_hash[STA_HASH_SIZE]; @@ -402,6 +516,13 @@ 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; + unsigned int dfs_cac_ms; struct os_reltime dfs_cac_start; @@ -418,6 +539,8 @@ struct dl_list sta_seen; /* struct hostapd_sta_info */ unsigned int num_sta_seen; + + u8 dfs_domain; }; /* hostapd.c */ @@ -425,6 +548,7 @@ int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); int hostapd_reload_config(struct hostapd_iface *iface); +void hostapd_reconfig_encryption(struct hostapd_data *hapd); struct hostapd_data * hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, struct hostapd_config *conf, @@ -433,6 +557,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); void hostapd_interface_deinit(struct hostapd_iface *iface); void hostapd_interface_free(struct hostapd_iface *iface); +struct hostapd_iface * hostapd_alloc_iface(void); struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, const char *config_file); struct hostapd_iface * @@ -449,6 +574,8 @@ void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s); +int hostapd_csa_in_progress(struct hostapd_iface *iface); +void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); void @@ -456,6 +583,7 @@ const struct hostapd_freq_params *freq_params); void hostapd_cleanup_cs_params(struct hostapd_data *hapd); void hostapd_periodic_iface(struct hostapd_iface *iface); +int hostapd_owe_trans_get_info(struct hostapd_data *hapd); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, @@ -467,6 +595,8 @@ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); /* drv_callbacks.c (TODO: move to somewhere else?) */ +void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta); int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, const u8 *ie, size_t ielen, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); @@ -478,6 +608,11 @@ int ssi_signal); void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2); +struct survey_results; +void hostapd_event_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_results); +void hostapd_acs_channel_selected(struct hostapd_data *hapd, + struct acs_selected_channels *acs_res); const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, @@ -485,6 +620,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, --- contrib/wpa/src/ap/hs20.c.orig +++ contrib/wpa/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" @@ -23,17 +25,20 @@ if (!hapd->conf->hs20) return eid; *eid++ = WLAN_EID_VENDOR_SPECIFIC; - *eid++ = 7; + *eid++ = hapd->conf->hs20_release < 2 ? 5 : 7; WPA_PUT_BE24(eid, OUI_WFA); eid += 3; *eid++ = HS20_INDICATION_OUI_TYPE; - conf = HS20_VERSION; /* Release Number */ - conf |= HS20_ANQP_DOMAIN_ID_PRESENT; + conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */ + if (hapd->conf->hs20_release >= 2) + conf |= HS20_ANQP_DOMAIN_ID_PRESENT; if (hapd->conf->disable_dgaf) conf |= HS20_DGAF_DISABLED; *eid++ = conf; - WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id); - eid += 2; + if (hapd->conf->hs20_release >= 2) { + WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id); + eid += 2; + } return eid; } @@ -82,6 +87,10 @@ capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (hapd->conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ WPA_PUT_LE16(eid, capab); eid += 2; @@ -175,3 +184,72 @@ return ret; } + + +int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, + const u8 *addr, const char *url) +{ + struct wpabuf *buf; + int ret; + size_t url_len; + + if (!url) { + wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available"); + return -1; + } + + url_len = os_strlen(url); + if (5 + url_len > 255) { + wpa_printf(MSG_INFO, + "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'", + url); + return -1; + } + + buf = wpabuf_alloc(4 + 7 + url_len); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpabuf_put_u8(buf, 1); /* Dialog token */ + wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ + + /* Terms and Conditions Acceptance subelement */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + 1 + url_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE); + wpabuf_put_u8(buf, url_len); + wpabuf_put_str(buf, url); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + + wpabuf_free(buf); + + return ret; +} + + +void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta, + int enabled) +{ + if (enabled) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions filtering required for " + MACSTR, MAC2STR(sta->addr)); + sta->hs20_t_c_filtering = 1; + /* TODO: Enable firewall filtering for the STA */ + wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR, + MAC2STR(sta->addr)); + } else { + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions filtering not required for " + MACSTR, MAC2STR(sta->addr)); + sta->hs20_t_c_filtering = 0; + /* TODO: Disable firewall filtering for the STA */ + wpa_msg(hapd->msg_ctx, MSG_INFO, + HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr)); + } +} --- contrib/wpa/src/ap/hs20.h.orig +++ contrib/wpa/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 */ --- contrib/wpa/src/ap/hw_features.c.orig +++ contrib/wpa/src/ap/hw_features.c @@ -78,10 +78,12 @@ int i, j; u16 num_modes, flags; struct hostapd_hw_modes *modes; + u8 dfs_domain; if (hostapd_drv_none(hapd)) return -1; - modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); + modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags, + &dfs_domain); if (modes == NULL) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -91,6 +93,7 @@ } iface->hw_flags = flags; + iface->dfs_domain = dfs_domain; hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = modes; @@ -226,9 +229,6 @@ { int pri_chan, sec_chan; - if (!iface->conf->secondary_channel) - return 1; /* HT40 not used */ - pri_chan = iface->conf->channel; sec_chan = pri_chan + iface->conf->secondary_channel * 4; @@ -329,6 +329,10 @@ res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; + iface->conf->vht_oper_centr_freq_seg0_idx = 0; + iface->conf->vht_oper_centr_freq_seg1_idx = 0; + iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + res = 1; wpa_printf(MSG_INFO, "Fallback to 20 MHz"); } @@ -472,8 +476,9 @@ struct wpa_driver_scan_params params; int ret; - if (!iface->conf->secondary_channel) - return 0; /* HT40 not used */ + /* Check that HT40 is used and PRI / SEC switch is allowed */ + if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch) + return 0; hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " @@ -619,41 +624,6 @@ #ifdef CONFIG_IEEE80211AC - -static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name) -{ - u32 req_cap = conf & cap; - - /* - * Make sure we support all requested capabilities. - * NOTE: We assume that 'cap' represents a capability mask, - * not a discrete value. - */ - if ((hw & req_cap) != req_cap) { - wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]", - name); - return 0; - } - return 1; -} - - -static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, - unsigned int shift, - const char *name) -{ - u32 hw_max = hw & mask; - u32 conf_val = conf & mask; - - if (conf_val > hw_max) { - wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", - name, conf_val >> shift, hw_max >> shift); - return 0; - } - return 1; -} - - static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) { struct hostapd_hw_modes *mode = iface->current_mode; @@ -681,45 +651,7 @@ } } -#define VHT_CAP_CHECK(cap) \ - do { \ - if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ - return 0; \ - } while (0) - -#define VHT_CAP_CHECK_MAX(cap) \ - do { \ - if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ - #cap)) \ - return 0; \ - } while (0) - - VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); - VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ); - VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ); - VHT_CAP_CHECK(VHT_CAP_RXLDPC); - VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); - VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); - VHT_CAP_CHECK(VHT_CAP_TXSTBC); - VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); - VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); - VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); - VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); - VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); - VHT_CAP_CHECK(VHT_CAP_HTC_VHT); - VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); - VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); - VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); - VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); - VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); - -#undef VHT_CAP_CHECK -#undef VHT_CAP_CHECK_MAX - - return 1; + return ieee80211ac_cap_check(hw, conf); } #endif /* CONFIG_IEEE80211AC */ @@ -744,7 +676,8 @@ if (!ieee80211n_supported_ht_capab(iface)) return -1; #ifdef CONFIG_IEEE80211AC - if (!ieee80211ac_supported_vht_capab(iface)) + if (iface->conf->ieee80211ac && + !ieee80211ac_supported_vht_capab(iface)) return -1; #endif /* CONFIG_IEEE80211AC */ ret = ieee80211n_check_40mhz(iface); @@ -761,28 +694,25 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, int channel, int primary) { - int i; struct hostapd_channel_data *chan; if (!iface->current_mode) return 0; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; - if (chan->chan != channel) - continue; + chan = hw_get_channel_chan(iface->current_mode, channel, NULL); + if (!chan) + return 0; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) - return 1; + if ((primary && chan_pri_allowed(chan)) || + (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED))) + return 1; - wpa_printf(MSG_DEBUG, - "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s", - primary ? "" : "Configured HT40 secondary ", - i, chan->chan, chan->flag, - chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", - chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); - } - + wpa_printf(MSG_INFO, + "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s", + channel, primary ? "primary" : "secondary", + chan->flag, + chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", + chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); return 0; } @@ -789,6 +719,14 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) { + int secondary_chan; + struct hostapd_channel_data *pri_chan; + + pri_chan = hw_get_channel_chan(iface->current_mode, + iface->conf->channel, NULL); + if (!pri_chan) + return 0; + if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) return 0; @@ -795,8 +733,27 @@ if (!iface->conf->secondary_channel) return 1; - return hostapd_is_usable_chan(iface, iface->conf->channel + - iface->conf->secondary_channel * 4, 0); + if (!iface->conf->ht40_plus_minus_allowed) + return hostapd_is_usable_chan( + iface, iface->conf->channel + + iface->conf->secondary_channel * 4, 0); + + /* Both HT40+ and HT40- are set, pick a valid secondary channel */ + secondary_chan = iface->conf->channel + 4; + if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { + iface->conf->secondary_channel = 1; + return 1; + } + + secondary_chan = iface->conf->channel - 4; + if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { + iface->conf->secondary_channel = -1; + return 1; + } + + return 0; } @@ -976,5 +933,19 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) { - return hw_get_chan(hapd->iface->current_mode, freq); + int i, channel; + struct hostapd_hw_modes *mode; + + channel = hw_get_chan(hapd->iface->current_mode, freq); + if (channel) + return channel; + /* Check other available modes since the channel list for the current + * mode did not include the specified frequency. */ + for (i = 0; i < hapd->iface->num_hw_features; i++) { + mode = &hapd->iface->hw_features[i]; + channel = hw_get_chan(mode, freq); + if (channel) + return channel; + } + return 0; } --- contrib/wpa/src/ap/iapp.c.orig +++ contrib/wpa/src/ap/iapp.c @@ -34,11 +34,7 @@ #include "utils/includes.h" #include #include -#ifdef USE_KERNEL_HEADERS -#include -#else /* USE_KERNEL_HEADERS */ #include -#endif /* USE_KERNEL_HEADERS */ #include "utils/common.h" #include "utils/eloop.h" @@ -385,6 +381,7 @@ struct sockaddr_in *paddr, uaddr; struct iapp_data *iapp; struct ip_mreqn mreq; + int reuseaddr = 1; iapp = os_zalloc(sizeof(*iapp)); if (iapp == NULL) @@ -447,6 +444,18 @@ os_memset(&uaddr, 0, sizeof(uaddr)); uaddr.sin_family = AF_INET; uaddr.sin_port = htons(IAPP_UDP_PORT); + + if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, + sizeof(reuseaddr)) < 0) { + wpa_printf(MSG_INFO, + "iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s", + strerror(errno)); + /* + * Ignore this and try to continue. This is fine for single + * BSS cases, but may fail if multiple BSSes enable IAPP. + */ + } + if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, sizeof(uaddr)) < 0) { wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", --- contrib/wpa/src/ap/ieee802_11.c.orig +++ contrib/wpa/src/ap/ieee802_11.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2002-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,11 +14,15 @@ #include "utils/eloop.h" #include "crypto/crypto.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "common/sae.h" +#include "common/dpp.h" +#include "common/ocv.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -42,8 +46,43 @@ #include "hw_features.h" #include "ieee802_11.h" #include "dfs.h" +#include "mbo_ap.h" +#include "rrm.h" +#include "taxonomy.h" +#include "fils_hlp.h" +#include "dpp_hostapd.h" +#include "gas_query_ap.h" +#ifdef CONFIG_FILS +static struct wpabuf * +prepare_auth_resp_fils(struct hostapd_data *hapd, + struct sta_info *sta, u16 *resp, + struct rsn_pmksa_cache_entry *pmksa, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len, + int *is_pub); +#endif /* CONFIG_FILS */ +static void handle_auth(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi, int from_queue); + + +u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid) +{ + u8 multi_ap_val = 0; + + if (!hapd->conf->multi_ap) + return eid; + if (hapd->conf->multi_ap & BACKHAUL_BSS) + multi_ap_val |= MULTI_AP_BACKHAUL_BSS; + if (hapd->conf->multi_ap & FRONTHAUL_BSS) + multi_ap_val |= MULTI_AP_FRONTHAUL_BSS; + + return eid + add_multi_ap_ie(eid, 9, multi_ap_val); +} + + u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; @@ -139,6 +178,7 @@ int capab = WLAN_CAPABILITY_ESS; int privacy; int dfs; + int i; /* Check if any of configured channels require DFS */ dfs = hostapd_is_dfs_required(hapd->iface); @@ -186,8 +226,12 @@ (hapd->iconf->spectrum_mgmt_required || dfs)) capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - if (hapd->conf->radio_measurements) - capab |= IEEE80211_CAP_RRM; + for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { + if (hapd->conf->radio_measurements[i]) { + capab |= IEEE80211_CAP_RRM; + break; + } + } return capab; } @@ -207,16 +251,17 @@ if (!sta->challenge) { /* Generate a pseudo-random challenge */ u8 key[8]; - struct os_time now; - int r; + sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - os_get_time(&now); - r = os_random(); - os_memcpy(key, &now.sec, 4); - os_memcpy(key + 4, &r, 4); + if (os_get_random(key, sizeof(key)) < 0) { + os_free(sta->challenge); + sta->challenge = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + rc4_skip(key, sizeof(key), 0, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); } @@ -250,19 +295,20 @@ #endif /* CONFIG_NO_RC4 */ -static void 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) +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 char *dbg) { struct ieee80211_mgmt *reply; u8 *buf; size_t rlen; + int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; buf = os_zalloc(rlen); if (buf == NULL) - return; + return -1; reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, @@ -279,17 +325,21 @@ 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"); + wpa_printf(MSG_INFO, "send_auth_reply: send failed"); + else + reply_res = WLAN_STATUS_SUCCESS; os_free(buf); + + return reply_res; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len) @@ -296,28 +346,44 @@ { struct hostapd_data *hapd = ctx; struct sta_info *sta; + int reply_res; - send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, - status, ies, ies_len); + reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, + auth_transaction, status, ies, ies_len, + "auth-ft-finish"); - if (status != WLAN_STATUS_SUCCESS) - return; - sta = ap_get_sta(hapd, dst); if (sta == NULL) return; + if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS || + status != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + return; + } + + if (status != WLAN_STATUS_SUCCESS) + return; + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); sta->flags |= WLAN_STA_AUTH; mlme_authenticate_indication(hapd, sta); } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE -#define dot11RSNASAESync 5 /* attempts */ +static void sae_set_state(struct sta_info *sta, enum sae_state state, + const char *reason) +{ + wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)", + sae_state_txt(sta->sae->state), sae_state_txt(state), + MAC2STR(sta->addr), reason); + sta->sae->state = state; +} static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, @@ -324,8 +390,28 @@ struct sta_info *sta, int update) { struct wpabuf *buf; + const char *password = NULL; + struct sae_password_entry *pw; + const char *rx_id = NULL; - if (hapd->conf->ssid.wpa_passphrase == NULL) { + if (sta->sae->tmp) + rx_id = sta->sae->tmp->pw_id; + + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (!is_broadcast_ether_addr(pw->peer_addr) && + os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0) + continue; + if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier)) + continue; + if (rx_id && pw->identifier && + os_strcmp(rx_id, pw->identifier) != 0) + continue; + password = pw->password; + break; + } + if (!password) + password = hapd->conf->ssid.wpa_passphrase; + if (!password) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } @@ -332,18 +418,27 @@ if (update && sae_prepare_commit(hapd->own_addr, sta->addr, - (u8 *) hapd->conf->ssid.wpa_passphrase, - os_strlen(hapd->conf->ssid.wpa_passphrase), + (u8 *) password, os_strlen(password), rx_id, sta->sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } - buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + if (pw && pw->vlan_id) { + if (!sta->sae->tmp) { + wpa_printf(MSG_INFO, + "SAE: No temporary data allocated - cannot store VLAN ID"); + return NULL; + } + sta->sae->tmp->vlan_id = pw->vlan_id; + } + + 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; } @@ -369,18 +464,21 @@ const u8 *bssid, int update) { struct wpabuf *data; + 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; - send_auth_reply(hapd, sta->addr, bssid, - WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, - wpabuf_head(data), wpabuf_len(data)); + reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, + WLAN_STATUS_SUCCESS, wpabuf_head(data), + wpabuf_len(data), "sae-send-commit"); wpabuf_free(data); - return WLAN_STATUS_SUCCESS; + return reply_res; } @@ -389,18 +487,19 @@ const u8 *bssid) { struct wpabuf *data; + int reply_res; data = auth_build_sae_confirm(hapd, sta); if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - send_auth_reply(hapd, sta->addr, bssid, - WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, - wpabuf_head(data), wpabuf_len(data)); + reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, + WLAN_STATUS_SUCCESS, wpabuf_head(data), + wpabuf_len(data), "sae-send-confirm"); wpabuf_free(data); - return WLAN_STATUS_SUCCESS; + return reply_res; } @@ -423,22 +522,58 @@ return 1; } + /* In addition to already existing open SAE sessions, check whether + * there are enough pending commit messages in the processing queue to + * potentially result in too many open sessions. */ + if (open + dl_list_len(&hapd->sae_commit_queue) >= + hapd->conf->sae_anti_clogging_threshold) + return 1; + return 0; } +static u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr) +{ + u8 hash[SHA256_MAC_LEN]; + + hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, hash); + return hash[0]; +} + + static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, const u8 *token, size_t token_len) { u8 mac[SHA256_MAC_LEN]; + const u8 *addrs[2]; + size_t len[2]; + u16 token_idx; + u8 idx; if (token_len != SHA256_MAC_LEN) return -1; - if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), - addr, ETH_ALEN, mac) < 0 || - os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0) + idx = sae_token_hash(hapd, addr); + token_idx = hapd->sae_pending_token_idx[idx]; + if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) { + wpa_printf(MSG_DEBUG, "SAE: Invalid anti-clogging token from " + MACSTR " - token_idx 0x%04x, expected 0x%04x", + MAC2STR(addr), WPA_GET_BE16(token), token_idx); return -1; + } + addrs[0] = addr; + len[0] = ETH_ALEN; + addrs[1] = token; + len[1] = 2; + if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), + 2, addrs, len, mac) < 0 || + os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0) + return -1; + + hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */ + return 0; } @@ -449,10 +584,16 @@ struct wpabuf *buf; u8 *token; struct os_reltime now; + u8 idx[2]; + const u8 *addrs[2]; + size_t len[2]; + u8 p_idx; + u16 token_idx; os_get_reltime(&now); if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || - os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) { + os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) || + hapd->sae_token_idx == 0xffff) { if (random_get_bytes(hapd->sae_token_key, sizeof(hapd->sae_token_key)) < 0) return NULL; @@ -459,6 +600,9 @@ wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", hapd->sae_token_key, sizeof(hapd->sae_token_key)); hapd->last_sae_token_key_update = now; + hapd->sae_token_idx = 0; + os_memset(hapd->sae_pending_token_idx, 0, + sizeof(hapd->sae_pending_token_idx)); } buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); @@ -467,18 +611,34 @@ wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + p_idx = sae_token_hash(hapd, addr); + token_idx = hapd->sae_pending_token_idx[p_idx]; + if (!token_idx) { + hapd->sae_token_idx++; + token_idx = hapd->sae_token_idx; + hapd->sae_pending_token_idx[p_idx] = token_idx; + } + WPA_PUT_BE16(idx, token_idx); token = wpabuf_put(buf, SHA256_MAC_LEN); - hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), - addr, ETH_ALEN, token); + addrs[0] = addr; + len[0] = ETH_ALEN; + addrs[1] = idx; + len[1] = sizeof(idx); + if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), + 2, addrs, len, token) < 0) { + wpabuf_free(buf); + return NULL; + } + WPA_PUT_BE16(token, token_idx); return buf; } -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; } @@ -492,9 +652,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=%s)", + MAC2STR(sta->addr), sta->sae->sync, + sae_state_txt(sta->sae->state)); switch (sta->sae->state) { case SAE_COMMITTED: @@ -537,21 +701,85 @@ } +static void sae_sme_send_external_auth_status(struct hostapd_data *hapd, + struct sta_info *sta, u16 status) +{ + struct external_auth params; + + os_memset(¶ms, 0, sizeof(params)); + params.status = status; + params.bssid = sta->addr; + if (status == WLAN_STATUS_SUCCESS && sta->sae) + params.pmkid = sta->sae->pmkid; + + hostapd_drv_send_external_auth_status(hapd, ¶ms); +} + + +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ +#ifndef CONFIG_NO_VLAN + struct vlan_description vlan_desc; + + if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) { + wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR + " to VLAN ID %d", + MAC2STR(sta->addr), sta->sae->tmp->vlan_id); + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = 1; + vlan_desc.untagged = sta->sae->tmp->vlan_id; + if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + wpa_printf(MSG_INFO, + "Invalid VLAN ID %d in sae_password", + sta->sae->tmp->vlan_id); + return; + } + + if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 || + ap_sta_bind_vlan(hapd, sta) < 0) { + wpa_printf(MSG_INFO, + "Failed to assign VLAN ID %d from sae_password to " + MACSTR, sta->sae->tmp->vlan_id, + MAC2STR(sta->addr)); + return; + } + } +#endif /* CONFIG_NO_VLAN */ + + sta->flags |= WLAN_STA_AUTH; + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + sta->sae->pmk, sta->sae->pmkid); + sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS); +} + + static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *bssid, u8 auth_transaction) + const u8 *bssid, u8 auth_transaction, int allow_reuse, + int *sta_removed) { int ret; + *sta_removed = 0; + 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); + ret = auth_sae_send_commit(hapd, sta, bssid, + !allow_reuse); 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; @@ -573,7 +801,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 @@ -580,7 +809,7 @@ * message now to get alternating sequence of * Authentication frames between the AP and STA. * Confirm will be sent in - * Commited -> Confirmed/Accepted transition + * Committed -> Confirmed/Accepted transition * when receiving Confirm from STA. */ } @@ -602,7 +831,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) { @@ -610,7 +839,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++; @@ -629,7 +858,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 @@ -636,13 +865,14 @@ * step to get to Accepted without waiting for * additional events. */ - return sae_sm_step(hapd, sta, bssid, auth_transaction); + return sae_sm_step(hapd, sta, bssid, auth_transaction, + 0, sta_removed); } break; 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++; @@ -659,23 +889,32 @@ sae_set_retransmit_timer(hapd, sta); } else { - sta->flags |= WLAN_STA_AUTH; - 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; - wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, - sta->sae->pmk); + 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)); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); ap_free_sta(hapd, sta); + *sta_removed = 1; + } 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++; @@ -694,26 +933,109 @@ } +static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sae_data *sae = sta->sae; + int i, *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; + + if (sae->state != SAE_COMMITTED) + return; + + wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); + + if (!groups) + groups = default_groups; + for (i = 0; groups[i] > 0; i++) { + if (sae->group == groups[i]) + break; + } + + if (groups[i] <= 0) { + wpa_printf(MSG_DEBUG, + "SAE: Previously selected group not found from the current configuration"); + return; + } + + for (;;) { + i++; + if (groups[i] <= 0) { + wpa_printf(MSG_DEBUG, + "SAE: No alternative group enabled"); + return; + } + + if (sae_set_group(sae, groups[i]) < 0) + continue; + + break; + } + wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]); +} + + static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 auth_transaction, u16 status_code) { - u16 resp = WLAN_STATUS_SUCCESS; + int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; + int *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; + const u8 *pos, *end; + int sta_removed = 0; + if (!groups) + groups = default_groups; + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->sae_reflection_attack && auth_transaction == 1) { + wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, pos, end - pos, + "auth-sae-reflection-attack"); + goto remove_sta; + } + + if (hapd->conf->sae_commit_override && auth_transaction == 1) { + wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + wpabuf_head(hapd->conf->sae_commit_override), + wpabuf_len(hapd->conf->sae_commit_override), + "sae-commit-override"); + goto remove_sta; + } +#endif /* CONFIG_TESTING_OPTIONS */ if (!sta->sae) { - if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) - return; + if (auth_transaction != 1 || + status_code != WLAN_STATUS_SUCCESS) { + resp = -1; + goto remove_sta; + } sta->sae = os_zalloc(sizeof(*sta->sae)); - if (sta->sae == NULL) - return; - sta->sae->state = SAE_NOTHING; + if (!sta->sae) { + resp = -1; + goto remove_sta; + } + sae_set_state(sta, SAE_NOTHING, "Init"); sta->sae->sync = 0; } + if (sta->mesh_sae_pmksa_caching) { + wpa_printf(MSG_DEBUG, + "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication"); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + sta->mesh_sae_pmksa_caching = 0; + } + if (auth_transaction == 1) { - const u8 *token = NULL, *pos, *end; + const u8 *token = NULL; size_t token_len = 0; + int allow_reuse = 0; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "start SAE authentication (RX commit, status=%u)", @@ -730,8 +1052,7 @@ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } - resp = sae_group_allowed(sta->sae, - hapd->conf->sae_groups, + resp = sae_group_allowed(sta->sae, groups, WPA_GET_LE16(pos)); if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, @@ -746,7 +1067,8 @@ if (sta->sae->tmp->anti_clogging_token == NULL) { wpa_printf(MSG_ERROR, "SAE: Failed to alloc for anti-clogging token"); - return; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto remove_sta; } /* @@ -756,42 +1078,93 @@ * Authentication frame, and the commit-scalar and * COMMIT-ELEMENT previously sent. */ - if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) { + resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0); + if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Failed to send commit message"); - return; + 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; } + if ((hapd->conf->mesh & MESH_ENABLED) && + status_code == + WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + sta->sae->tmp) { + wpa_printf(MSG_DEBUG, + "SAE: Peer did not accept our SAE group"); + sae_pick_next_group(hapd, sta); + goto remove_sta; + } + if (status_code != WLAN_STATUS_SUCCESS) - return; + 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. */ + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + if (end - pos >= (int) sizeof(le16) && + sae_group_allowed(sta->sae, groups, + WPA_GET_LE16(pos)) == + WLAN_STATUS_SUCCESS) { + /* Do not waste resources deriving the same PWE + * again since the same group is reused. */ + sae_set_state(sta, SAE_NOTHING, + "Allow previous PWE to be reused"); + allow_reuse = 1; + } else { + 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, - &token_len, hapd->conf->sae_groups); + &token_len, groups); if (resp == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", MAC2STR(sta->addr)); - return; + 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 " "incorrect token from " MACSTR, MAC2STR(sta->addr)); - return; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto remove_sta; } if (resp != WLAN_STATUS_SUCCESS) goto reply; - if (!token && use_sae_anti_clogging(hapd)) { + if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) { wpa_printf(MSG_DEBUG, "SAE: Request anti-clogging token from " MACSTR, MAC2STR(sta->addr)); @@ -799,11 +1172,13 @@ 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; } - resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, + allow_reuse, &sta_removed); } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -810,17 +1185,42 @@ "SAE authentication (RX confirm, status=%u)", status_code); if (status_code != WLAN_STATUS_SUCCESS) - return; + 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); + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0, + &sta_removed); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -827,17 +1227,35 @@ "unexpected SAE authentication transaction %u (status=%u)", auth_transaction, status_code); if (status_code != WLAN_STATUS_SUCCESS) - return; + goto remove_sta; resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } reply: - if (resp != WLAN_STATUS_SUCCESS) { + if (!sta_removed && resp != WLAN_STATUS_SUCCESS) { + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + + /* Copy the Finite Cyclic Group field from the request if we + * rejected it as unsupported group. */ + if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + !data && end - pos >= 2) + data = wpabuf_alloc_copy(pos, 2); + + sae_sme_send_external_auth_status(hapd, sta, resp); 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: + if (!sta_removed && sta->added_unassoc && + (resp != WLAN_STATUS_SUCCESS || + status_code != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } wpabuf_free(data); } @@ -866,7 +1284,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); @@ -873,20 +1291,749 @@ return 0; } + +void auth_sae_process_commit(void *eloop_ctx, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct hostapd_sae_commit_queue *q; + unsigned int queue_len; + + q = dl_list_first(&hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list); + if (!q) + return; + wpa_printf(MSG_DEBUG, + "SAE: Process next available message from queue"); + dl_list_del(&q->list); + handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len, + q->rssi, 1); + os_free(q); + + if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) + return; + queue_len = dl_list_len(&hapd->sae_commit_queue); + eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, + hapd, NULL); +} + + +static void auth_sae_queue(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi) +{ + struct hostapd_sae_commit_queue *q, *q2; + unsigned int queue_len; + const struct ieee80211_mgmt *mgmt2; + + queue_len = dl_list_len(&hapd->sae_commit_queue); + if (queue_len >= 15) { + wpa_printf(MSG_DEBUG, + "SAE: No more room in message queue - drop the new frame from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + + wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from " + MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa), + queue_len); + q = os_zalloc(sizeof(*q) + len); + if (!q) + return; + q->rssi = rssi; + q->len = len; + os_memcpy(q->msg, mgmt, len); + + /* Check whether there is already a queued Authentication frame from the + * same station with the same transaction number and if so, replace that + * queue entry with the new one. This avoids issues with a peer that + * sends multiple times (e.g., due to frequent SAE retries). There is no + * point in us trying to process the old attempts after a new one has + * obsoleted them. */ + dl_list_for_each(q2, &hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list) { + mgmt2 = (const struct ieee80211_mgmt *) q2->msg; + if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 && + mgmt->u.auth.auth_transaction == + mgmt2->u.auth.auth_transaction) { + wpa_printf(MSG_DEBUG, + "SAE: Replace queued message from same STA with same transaction number"); + dl_list_add(&q2->list, &q->list); + dl_list_del(&q2->list); + os_free(q2); + goto queued; + } + } + + /* No pending identical entry, so add to the end of the queue */ + dl_list_add_tail(&hapd->sae_commit_queue, &q->list); + +queued: + if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) + return; + eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, + hapd, NULL); +} + + +static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr) +{ + struct hostapd_sae_commit_queue *q; + const struct ieee80211_mgmt *mgmt; + + dl_list_for_each(q, &hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list) { + mgmt = (const struct ieee80211_mgmt *) q->msg; + if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + #endif /* CONFIG_SAE */ +static u16 wpa_res_to_status_code(int res) +{ + if (res == WPA_INVALID_GROUP) + return WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + if (res == WPA_INVALID_PAIRWISE) + return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + if (res == WPA_INVALID_AKMP) + return WLAN_STATUS_AKMP_NOT_VALID; + if (res == WPA_ALLOC_FAIL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; +#ifdef CONFIG_IEEE80211W + if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; +#endif /* CONFIG_IEEE80211W */ + if (res == WPA_INVALID_MDIE) + return WLAN_STATUS_INVALID_MDIE; + if (res == WPA_INVALID_PMKID) + return WLAN_STATUS_INVALID_PMKID; + if (res != WPA_IE_OK) + return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_SUCCESS; +} + + +#ifdef CONFIG_FILS + +static void handle_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub); + +void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, size_t len, u16 auth_alg, + u16 auth_transaction, u16 status_code, + void (*cb)(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub)) +{ + u16 resp = WLAN_STATUS_SUCCESS; + const u8 *end; + struct ieee802_11_elems elems; + int res; + struct wpa_ie_data rsn; + struct rsn_pmksa_cache_entry *pmksa = NULL; + + if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) + return; + + end = pos + len; + + wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", + pos, end - pos); + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (auth_alg == WLAN_AUTH_FILS_SK_PFS) { + u16 group; + struct wpabuf *pub; + size_t elem_len; + + /* Using FILS PFS */ + + /* Finite Cyclic Group */ + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, + "FILS: No room for Finite Cyclic Group"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + group = WPA_GET_LE16(pos); + pos += 2; + if (group != hapd->conf->fils_dh_group) { + wpa_printf(MSG_DEBUG, + "FILS: Unsupported Finite Cyclic Group: %u (expected %u)", + group, hapd->conf->fils_dh_group); + resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto fail; + } + + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = crypto_ecdh_init(group); + if (!sta->fils_ecdh) { + wpa_printf(MSG_INFO, + "FILS: Could not initialize ECDH with group %d", + group); + resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto fail; + } + + pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); + if (!pub) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to derive ECDH public key"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + elem_len = wpabuf_len(pub); + wpabuf_free(pub); + + /* Element */ + if ((size_t) (end - pos) < elem_len) { + wpa_printf(MSG_DEBUG, "FILS: No room for Element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + wpabuf_free(sta->fils_g_sta); + sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len); + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1, + pos, elem_len); + if (!sta->fils_dh_ss) { + wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss); + pos += elem_len; + } else { + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = NULL; + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; + } +#endif /* CONFIG_FILS_SK_PFS */ + + wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); + if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* RSNE */ + wpa_hexdump(MSG_DEBUG, "FILS: RSN element", + elems.rsn_ie, elems.rsn_ie_len); + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0) { + wpa_printf(MSG_DEBUG, "FILS: No valid RSN element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, + NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to initialize RSN state machine"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, + elems.rsn_ie - 2, elems.rsn_ie_len + 2, + elems.mdie, elems.mdie_len, NULL, 0); + resp = wpa_res_to_status_code(res); + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + + if (!elems.fils_nonce) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce, + FILS_NONCE_LEN); + os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN); + + /* PMKID List */ + if (rsn.pmkid && rsn.num_pmkid > 0) { + u8 num; + const u8 *pmkid; + + wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", + rsn.pmkid, rsn.num_pmkid * PMKID_LEN); + + pmkid = rsn.pmkid; + num = rsn.num_pmkid; + while (num) { + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN); + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, + pmkid); + if (pmksa) + break; + pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth, + sta->addr, + pmkid); + if (pmksa) + break; + pmkid += PMKID_LEN; + num--; + } + } + if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) { + wpa_printf(MSG_DEBUG, + "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore", + wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp); + pmksa = NULL; + } + if (pmksa) + wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry"); + + /* FILS Session */ + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, + FILS_SESSION_LEN); + os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN); + + /* FILS Wrapped Data */ + if (elems.fils_wrapped_data) { + wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", + elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + if (!pmksa) { +#ifndef CONFIG_NO_RADIUS + if (!sta->eapol_sm) { + sta->eapol_sm = + ieee802_1x_alloc_eapol_sm(hapd, sta); + } + wpa_printf(MSG_DEBUG, + "FILS: Forward EAP-Initiate/Re-auth to authentication server"); + ieee802_1x_encapsulate_radius( + hapd, sta, elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + sta->fils_pending_cb = cb; + wpa_printf(MSG_DEBUG, + "FILS: Will send Authentication frame once the response from authentication server is available"); + sta->flags |= WLAN_STA_PENDING_FILS_ERP; + /* Calculate pending PMKID here so that we do not need + * to maintain a copy of the EAP-Initiate/Reauth + * message. */ + if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm), + elems.fils_wrapped_data, + elems.fils_wrapped_data_len, + sta->fils_erp_pmkid) == 0) + sta->fils_erp_pmkid_set = 1; + return; +#else /* CONFIG_NO_RADIUS */ + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; +#endif /* CONFIG_NO_RADIUS */ + } + } + +fail: + if (cb) { + struct wpabuf *data; + int pub = 0; + + data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL, + NULL, 0, &pub); + if (!data) { + wpa_printf(MSG_DEBUG, + "%s: prepare_auth_resp_fils() returned failure", + __func__); + } + + cb(hapd, sta, resp, data, pub); + } +} + + +static struct wpabuf * +prepare_auth_resp_fils(struct hostapd_data *hapd, + struct sta_info *sta, u16 *resp, + struct rsn_pmksa_cache_entry *pmksa, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len, + int *is_pub) +{ + u8 fils_nonce[FILS_NONCE_LEN]; + size_t ielen; + struct wpabuf *data = NULL; + const u8 *ie; + u8 *ie_buf = NULL; + const u8 *pmk = NULL; + size_t pmk_len = 0; + u8 pmk_buf[PMK_LEN_MAX]; + struct wpabuf *pub = NULL; + + if (*resp != WLAN_STATUS_SUCCESS) + goto fail; + + ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); + if (!ie) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (pmksa) { + /* Add PMKID of the selected PMKSA into RSNE */ + ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN); + if (!ie_buf) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + os_memcpy(ie_buf, ie, ielen); + if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + ie = ie_buf; + } + + if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", + fils_nonce, FILS_NONCE_LEN); + +#ifdef CONFIG_FILS_SK_PFS + if (sta->fils_dh_ss && sta->fils_ecdh) { + pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); + if (!pub) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } +#endif /* CONFIG_FILS_SK_PFS */ + + data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0)); + if (!data) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (pub) { + /* Finite Cyclic Group */ + wpabuf_put_le16(data, hapd->conf->fils_dh_group); + + /* Element */ + wpabuf_put_buf(data, pub); + } +#endif /* CONFIG_FILS_SK_PFS */ + + /* RSNE */ + wpabuf_put_data(data, ie, ielen); + + /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */ + +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) { + /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */ + int res; + int use_sha384 = wpa_key_mgmt_sha384( + wpa_auth_sta_key_mgmt(sta->wpa_sm)); + + res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384, + wpabuf_put(data, 0), + wpabuf_tailroom(data)); + if (res < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpabuf_put(data, res); + } +#endif /* CONFIG_IEEE80211R_AP */ + + /* FILS Nonce */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN); + + /* FILS Wrapped Data */ + if (!pmksa && erp_resp) { + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA); + wpabuf_put_buf(data, erp_resp); + + if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), + msk, msk_len, sta->fils_snonce, fils_nonce, + sta->fils_dh_ss ? + wpabuf_head(sta->fils_dh_ss) : NULL, + sta->fils_dh_ss ? + wpabuf_len(sta->fils_dh_ss) : 0, + pmk_buf, &pmk_len)) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + pmk = pmk_buf; + + /* Don't use DHss in PTK derivation if PMKSA caching is not + * used. */ + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; + + if (sta->fils_erp_pmkid_set) { + /* TODO: get PMKLifetime from WPA parameters */ + unsigned int dot11RSNAConfigPMKLifetime = 43200; + int session_timeout; + + session_timeout = dot11RSNAConfigPMKLifetime; + if (sta->session_timeout_set) { + struct os_reltime now, diff; + + os_get_reltime(&now); + os_reltime_sub(&sta->session_timeout, &now, + &diff); + session_timeout = diff.sec; + } + + sta->fils_erp_pmkid_set = 0; + wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len, + sta->fils_erp_pmkid); + if (!hapd->conf->disable_pmksa_caching && + wpa_auth_pmksa_add2( + hapd->wpa_auth, sta->addr, + pmk, pmk_len, + sta->fils_erp_pmkid, + session_timeout, + wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) { + wpa_printf(MSG_ERROR, + "FILS: Failed to add PMKSA cache entry based on ERP"); + } + } + } else if (pmksa) { + pmk = pmksa->pmk; + pmk_len = pmksa->pmk_len; + } + + if (!pmk) { + wpa_printf(MSG_DEBUG, "FILS: No PMK available"); + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + + if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len, + sta->fils_snonce, fils_nonce, + sta->fils_dh_ss ? + wpabuf_head(sta->fils_dh_ss) : NULL, + sta->fils_dh_ss ? + wpabuf_len(sta->fils_dh_ss) : 0, + sta->fils_g_sta, pub) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + +fail: + if (is_pub) + *is_pub = pub != NULL; + os_free(ie_buf); + wpabuf_free(pub); + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = NULL; +#endif /* CONFIG_FILS_SK_PFS */ + return data; +} + + +static void handle_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub) +{ + u16 auth_alg; + + auth_alg = (pub || + resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ? + WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; + send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp, + data ? wpabuf_head(data) : (u8 *) "", + data ? wpabuf_len(data) : 0, "auth-fils-finish"); + wpabuf_free(data); + + if (resp == WLAN_STATUS_SUCCESS) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication OK (FILS)"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; + mlme_authenticate_indication(hapd, sta); + } +} + + +void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, + struct sta_info *sta, int success, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len) +{ + struct wpabuf *data; + int pub = 0; + u16 resp; + + sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; + + if (!sta->fils_pending_cb) + return; + resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE; + data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, + msk, msk_len, &pub); + if (!data) { + wpa_printf(MSG_DEBUG, + "%s: prepare_auth_resp_fils() returned failure", + __func__); + } + sta->fils_pending_cb(hapd, sta, resp, data, pub); +} + +#endif /* CONFIG_FILS */ + + +int +ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui, int is_probe_req) +{ + int res; + + os_memset(vlan_id, 0, sizeof(*vlan_id)); + res = hostapd_allowed_address(hapd, addr, msg, len, + session_timeout, acct_interim_interval, + vlan_id, psk, identity, radius_cui, + is_probe_req); + + if (res == HOSTAPD_ACL_REJECT) { + if (!is_probe_req) + wpa_printf(MSG_DEBUG, + "Station " MACSTR + " not allowed to authenticate", + MAC2STR(addr)); + return HOSTAPD_ACL_REJECT; + } + + if (res == HOSTAPD_ACL_PENDING) { + wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR + " waiting for an external authentication", + MAC2STR(addr)); + /* Authentication code will re-send the authentication frame + * after it has received (and cached) information from the + * external source. */ + return HOSTAPD_ACL_PENDING; + } + + return res; +} + + +static int +ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, + int res, u32 session_timeout, + u32 acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) +{ + if (vlan_id->notempty && + !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from RADIUS server", + vlan_id->untagged, + vlan_id->tagged[0] ? "+" : ""); + return -1; + } + if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0) + return -1; + if (sta->vlan_id) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + + hostapd_free_psk_list(sta->psk); + if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { + sta->psk = *psk; + *psk = NULL; + } else { + sta->psk = NULL; + } + + os_free(sta->identity); + sta->identity = *identity; + *identity = NULL; + + os_free(sta->radius_cui); + sta->radius_cui = *radius_cui; + *radius_cui = NULL; + + if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) + sta->acct_interim_interval = acct_interim_interval; + if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) { + sta->session_timeout_set = 1; + os_get_reltime(&sta->session_timeout); + sta->session_timeout.sec += session_timeout; + ap_sta_session_timeout(hapd, sta, session_timeout); + } else { + sta->session_timeout_set = 0; + ap_sta_no_session_timeout(hapd, sta); + } + + return 0; +} + + static void handle_auth(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi, int from_queue) { u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; struct sta_info *sta = NULL; - int res; + int res, reply_res; u16 fc; const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; - int vlan_id = 0; + struct vlan_description vlan_id; struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; @@ -924,11 +2071,12 @@ wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " "auth_transaction=%d status_code=%d wep=%d%s " - "seq_ctrl=0x%x%s", + "seq_ctrl=0x%x%s%s", MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code, !!(fc & WLAN_FC_ISWEP), challenge ? " challenge" : "", - seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "", + from_queue ? " (from queue)" : ""); #ifdef CONFIG_NO_RC4 if (auth_alg == WLAN_AUTH_SHARED_KEY) { @@ -941,20 +2089,29 @@ #endif /* CONFIG_NO_RC4 */ if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + wpa_printf(MSG_DEBUG, + "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FT) || -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_SAE) || #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_FILS_SK) || + (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && + hapd->conf->fils_dh_group && + auth_alg == WLAN_AUTH_FILS_SK_PFS) || +#endif /* CONFIG_FILS */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", @@ -1033,29 +2190,40 @@ } } - res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, - &session_timeout, - &acct_interim_interval, &vlan_id, - &psk, &identity, &radius_cui); - + res = ieee802_11_allowed_address( + hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout, + &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui, + 0); if (res == HOSTAPD_ACL_REJECT) { - wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", - MAC2STR(mgmt->sa)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Authentication frame from " MACSTR + " due to ACL reject", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } - if (res == HOSTAPD_ACL_PENDING) { - wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR - " waiting for an external authentication", - MAC2STR(mgmt->sa)); - /* Authentication code will re-send the authentication frame - * after it has received (and cached) information from the - * external source. */ + if (res == HOSTAPD_ACL_PENDING) return; + +#ifdef CONFIG_SAE + if (auth_alg == WLAN_AUTH_SAE && !from_queue && + (auth_transaction == 1 || + (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) { + /* Handle SAE Authentication commit message through a queue to + * provide more control for postponing the needed heavy + * processing under a possible DoS attack scenario. In addition, + * queue SAE Authentication confirm message if there happens to + * be a queued commit message from the same peer. This is needed + * to avoid reordering Authentication frames within the same + * SAE exchange. */ + auth_sae_queue(hapd, mgmt, len, rssi); + return; } +#endif /* CONFIG_SAE */ sta = ap_get_sta(hapd, mgmt->sa); if (sta) { + sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; + sta->ft_over_ds = 0; if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && @@ -1067,6 +2235,15 @@ seq_ctrl); return; } +#ifdef CONFIG_MESH + if ((hapd->conf->mesh & MESH_ENABLED) && + sta->plink_state == PLINK_BLOCKED) { + wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR + " is blocked - drop Authentication frame", + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_MESH */ } else { #ifdef CONFIG_MESH if (hapd->conf->mesh & MESH_ENABLED) { @@ -1073,7 +2250,7 @@ /* if the mesh peer is not available, we don't do auth. */ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR - " not yet known - drop Authentiation frame", + " not yet known - drop Authentication frame", MAC2STR(mgmt->sa)); /* * Save a copy of the frame so that it can be processed @@ -1088,6 +2265,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; } @@ -1094,44 +2272,70 @@ } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_AUTH; +#ifdef CONFIG_MBO + sta->auth_rssi = rssi; +#endif /* CONFIG_MBO */ - if (vlan_id > 0) { - if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " - "%d received from RADIUS server", - vlan_id); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - sta->vlan_id = vlan_id; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + res = ieee802_11_set_radius_info( + hapd, sta, res, session_timeout, acct_interim_interval, + &vlan_id, &psk, &identity, &radius_cui); + if (res) { + wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; } - hostapd_free_psk_list(sta->psk); - if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { - sta->psk = psk; - psk = NULL; - } else { - sta->psk = NULL; - } - - sta->identity = identity; - identity = NULL; - sta->radius_cui = radius_cui; - radius_cui = NULL; - sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); - if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) - sta->acct_interim_interval = acct_interim_interval; - if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) - ap_sta_session_timeout(hapd, sta, session_timeout); - else - ap_sta_no_session_timeout(hapd, sta); + /* + * If the driver supports full AP client state, add a station to the + * driver before sending authentication reply to make sure the driver + * has resources, and not to go through the entire authentication and + * association handshake, and fail it at the end. + * + * If this is not the first transaction, in a multi-step authentication + * algorithm, the station already exists in the driver + * (sta->added_unassoc = 1) so skip it. + * + * In mesh mode, the station was already added to the driver when the + * NEW_PEER_CANDIDATE event is received. + * + * If PMF was negotiated for the existing association, skip this to + * avoid dropping the STA entry and the associated keys. This is needed + * to allow the original connection work until the attempt can complete + * (re)association, so that unprotected Authentication frame cannot be + * used to bypass PMF protection. + */ + if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && + (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && + !(hapd->conf->mesh & MESH_ENABLED) && + !(sta->added_unassoc)) { + /* + * If a station that is already associated to the AP, is trying + * to authenticate again, remove the STA entry, in order to make + * sure the STA PS state gets cleared and configuration gets + * updated. To handle this, station's added_unassoc flag is + * cleared once the station has completed association. + */ + ap_sta_set_authorized(hapd, sta, 0); + hostapd_drv_sta_remove(hapd, sta->addr); + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | + WLAN_STA_AUTHORIZED); + if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0, + NULL, NULL, sta->flags, 0, 0, 0, 0)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + sta->added_unassoc = 1; + } + switch (auth_alg) { case WLAN_AUTH_OPEN: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -1146,6 +2350,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) { @@ -1157,7 +2364,7 @@ } break; #endif /* CONFIG_NO_RC4 */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) @@ -1176,7 +2383,7 @@ handle_auth_ft_finish, hapd); /* handle_auth_ft_finish() callback will complete auth. */ return; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: #ifdef CONFIG_MESH @@ -1198,6 +2405,15 @@ status_code); return; #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WLAN_AUTH_FILS_SK: + case WLAN_AUTH_FILS_SK_PFS: + handle_auth_fils(hapd, sta, mgmt->u.auth.variable, + len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), + auth_alg, auth_transaction, status_code, + handle_auth_fils_finish); + return; +#endif /* CONFIG_FILS */ } fail: @@ -1205,12 +2421,19 @@ os_free(radius_cui); hostapd_free_psk_list(psk); - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, - auth_transaction + 1, resp, resp_ies, resp_ies_len); + reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, + auth_transaction + 1, resp, resp_ies, + resp_ies_len, "handle-auth"); + + if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || + reply_res != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } -static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) +int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) { int i, j = 32, aid; @@ -1220,6 +2443,9 @@ return 0; } + if (TEST_FAIL()) + return -1; + for (i = 0; i < AID_WORDS; i++) { if (hapd->sta_aid[i] == (u32) -1) continue; @@ -1286,10 +2512,68 @@ return WLAN_STATUS_SUCCESS; } +static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *multi_ap_ie, size_t multi_ap_len) +{ + u8 multi_ap_value = 0; + sta->flags &= ~WLAN_STA_MULTI_AP; + + if (!hapd->conf->multi_ap) + return WLAN_STATUS_SUCCESS; + + if (multi_ap_ie) { + const u8 *multi_ap_subelem; + + multi_ap_subelem = get_ie(multi_ap_ie + 4, + multi_ap_len - 4, + MULTI_AP_SUB_ELEM_TYPE); + if (multi_ap_subelem && multi_ap_subelem[1] == 1) { + multi_ap_value = multi_ap_subelem[2]; + } else { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Multi-AP IE has missing or invalid Multi-AP subelement"); + return WLAN_STATUS_INVALID_IE; + } + } + + if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Multi-AP IE with unexpected value 0x%02x", + multi_ap_value); + + if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) { + if (hapd->conf->multi_ap & FRONTHAUL_BSS) + return WLAN_STATUS_SUCCESS; + + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Non-Multi-AP STA tries to associate with backhaul-only BSS"); + return WLAN_STATUS_ASSOC_DENIED_UNSPEC; + } + + if (!(hapd->conf->multi_ap & BACKHAUL_BSS)) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Backhaul STA tries to associate with fronthaul-only BSS"); + + sta->flags |= WLAN_STA_MULTI_AP; + return WLAN_STATUS_SUCCESS; +} + + static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_11_elems *elems) { + /* Supported rates not used in IEEE 802.11ad/DMG */ + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) + return WLAN_STATUS_SUCCESS; + if (!elems->supp_rates) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1327,10 +2611,188 @@ } #endif /* CONFIG_INTERWORKING */ + if (ext_capab_ie_len > 0) { + sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); + os_free(sta->ext_capability); + sta->ext_capability = os_malloc(1 + ext_capab_ie_len); + if (sta->ext_capability) { + sta->ext_capability[0] = ext_capab_ie_len; + os_memcpy(sta->ext_capability + 1, ext_capab_ie, + ext_capab_ie_len); + } + } + return WLAN_STATUS_SUCCESS; } +#ifdef CONFIG_OWE + +static int owe_group_supported(struct hostapd_data *hapd, u16 group) +{ + int i; + int *groups = hapd->conf->owe_groups; + + if (group != 19 && group != 20 && group != 21) + return 0; + + if (!groups) + return 1; + + for (i = 0; groups[i] > 0; i++) { + if (groups[i] == group) + return 1; + } + + return 0; +} + + +static u16 owe_process_assoc_req(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *owe_dh, + u8 owe_dh_len) +{ + struct wpabuf *secret, *pub, *hkey; + int res; + u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; + const char *info = "OWE Key Generation"; + const u8 *addr[2]; + size_t len[2]; + u16 group; + size_t hash_len, prime_len; + + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); + return WLAN_STATUS_SUCCESS; + } + + group = WPA_GET_LE16(owe_dh); + if (!owe_group_supported(hapd, group)) { + wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + if (group == 19) + prime_len = 32; + else if (group == 20) + prime_len = 48; + else if (group == 21) + prime_len = 66; + else + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + + crypto_ecdh_deinit(sta->owe_ecdh); + sta->owe_ecdh = crypto_ecdh_init(group); + if (!sta->owe_ecdh) + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + sta->owe_group = group; + + secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2, + owe_dh_len - 2); + secret = wpabuf_zeropad(secret, prime_len); + if (!secret) { + wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); + + /* prk = HKDF-extract(C | A | group, z) */ + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* PMKID = Truncate-128(Hash(C | A)) */ + addr[0] = owe_dh + 2; + len[0] = owe_dh_len - 2; + addr[1] = wpabuf_head(pub); + len[1] = wpabuf_len(pub); + if (group == 19) { + res = sha256_vector(2, addr, len, pmkid); + hash_len = SHA256_MAC_LEN; + } else if (group == 20) { + res = sha384_vector(2, addr, len, pmkid); + hash_len = SHA384_MAC_LEN; + } else if (group == 21) { + res = sha512_vector(2, addr, len, pmkid); + hash_len = SHA512_MAC_LEN; + } else { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pub = wpabuf_zeropad(pub, prime_len); + if (res < 0 || !pub) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2); + if (!hkey) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */ + wpabuf_put_buf(hkey, pub); /* A */ + wpabuf_free(pub); + wpabuf_put_le16(hkey, group); /* group */ + if (group == 19) + res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 20) + res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 21) + res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + wpabuf_clear_free(hkey); + wpabuf_clear_free(secret); + if (res < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); + + /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ + + os_free(sta->owe_pmk); + sta->owe_pmk = os_malloc(hash_len); + if (!sta->owe_pmk) { + os_memset(prk, 0, SHA512_MAC_LEN); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (group == 19) + res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + else if (group == 20) + res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + else if (group == 21) + res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + os_memset(prk, 0, SHA512_MAC_LEN); + if (res < 0) { + os_free(sta->owe_pmk); + sta->owe_pmk = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + sta->owe_pmk_len = hash_len; + + wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len); + wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); + wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk, + sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE); + + return WLAN_STATUS_SUCCESS; +} + +#endif /* CONFIG_OWE */ + + static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { @@ -1359,6 +2821,11 @@ resp = copy_supp_rates(hapd, sta, &elems); if (resp != WLAN_STATUS_SUCCESS) return resp; + + resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + #ifdef CONFIG_IEEE80211N resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) @@ -1378,6 +2845,10 @@ if (resp != WLAN_STATUS_SUCCESS) return resp; + resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); if (resp != WLAN_STATUS_SUCCESS) return resp; @@ -1470,34 +2941,24 @@ "state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } + wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg); res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, wpa_ie, wpa_ie_len, - elems.mdie, elems.mdie_len); - if (res == WPA_INVALID_GROUP) - resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_PAIRWISE) - resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_AKMP) - resp = WLAN_STATUS_AKMP_NOT_VALID; - else if (res == WPA_ALLOC_FAIL) - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; -#ifdef CONFIG_IEEE80211W - else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) - resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; - else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) - resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; -#endif /* CONFIG_IEEE80211W */ - else if (res == WPA_INVALID_MDIE) - resp = WLAN_STATUS_INVALID_MDIE; - else if (res != WPA_IE_OK) - resp = WLAN_STATUS_INVALID_IE; + elems.mdie, elems.mdie_len, + elems.owe_dh, elems.owe_dh_len); + resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211W - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA @@ -1518,7 +2979,7 @@ sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { if (!reassoc) { wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " @@ -1533,9 +2994,13 @@ if (resp != WLAN_STATUS_SUCCESS) return resp; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae && + sta->sae->state == SAE_ACCEPTED) + wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid); + if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg == WLAN_AUTH_OPEN) { struct rsn_pmksa_cache_entry *sa; @@ -1559,6 +3024,48 @@ } #endif /* CONFIG_SAE */ +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && + elems.owe_dh) { + resp = owe_process_assoc_req(hapd, sta, elems.owe_dh, + elems.owe_dh_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + hapd->conf->dpp_netaccesskey && sta->wpa_sm && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && + elems.owe_dh) { + sta->dpp_pfs = dpp_pfs_init( + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey)); + if (!sta->dpp_pfs) { + wpa_printf(MSG_DEBUG, + "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + + if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, + elems.owe_dh_len) < 0) { + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } + + wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? + sta->dpp_pfs->secret : NULL); + pfs_fail: +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211N if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { @@ -1603,10 +3110,28 @@ #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { + int release; + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, elems.hs20_len - 4); - } else + release = ((elems.hs20[4] >> 4) & 0x0f) + 1; + if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, + "HS 2.0: PMF not negotiated by release %d station " + MACSTR, release, MAC2STR(sta->addr)); + return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + } + } 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 @@ -1617,6 +3142,64 @@ sta->mb_ies = NULL; #endif /* CONFIG_FST */ +#ifdef CONFIG_MBO + mbo_ap_check_sta_assoc(hapd, sta, &elems); + + if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && + elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && + hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_INFO, + "MBO: Reject WPA2 association without PMF"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_MBO */ + +#if defined(CONFIG_FILS) && defined(CONFIG_OCV) + if (wpa_auth_uses_ocv(sta->wpa_sm) && + (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (get_sta_tx_parameters(sta->wpa_sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } +#endif /* CONFIG_FILS && CONFIG_OCV */ + + ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, + elems.supp_op_classes_len); + + if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) && + elems.rrm_enabled && + elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa)) + 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; } @@ -1643,22 +3226,131 @@ } -static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, - u16 status_code, int reassoc, const u8 *ies, - size_t ies_len) +static int add_associated_sta(struct hostapd_data *hapd, + struct sta_info *sta, int reassoc) { + struct ieee80211_ht_capabilities ht_cap; + struct ieee80211_vht_capabilities vht_cap; + int set = 1; + + /* + * Remove the STA entry to ensure the STA PS state gets cleared and + * configuration gets updated. This is relevant for cases, such as + * FT-over-the-DS, where a station re-associates back to the same AP but + * skips the authentication flow, or if working with a driver that + * does not support full AP client state. + * + * Skip this if the STA has already completed FT reassociation and the + * TK has been configured since the TX/RX PN must not be reset to 0 for + * the same key. + * + * FT-over-the-DS has a special case where the STA entry (and as such, + * the TK) has not yet been configured to the driver depending on which + * driver interface is used. For that case, allow add-STA operation to + * be used (instead of set-STA). This is needed to allow mac80211-based + * drivers to accept the STA parameter configuration. Since this is + * after a new FT-over-DS exchange, a new TK has been derived, so key + * reinstallation is not a concern for this case. + */ + wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR + " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)", + MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg, + sta->ft_over_ds, reassoc, + !!(sta->flags & WLAN_STA_AUTHORIZED), + wpa_auth_sta_ft_tk_already_set(sta->wpa_sm), + wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)); + + if (!sta->added_unassoc && + (!(sta->flags & WLAN_STA_AUTHORIZED) || + (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) || + (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) && + !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) { + hostapd_drv_sta_remove(hapd, sta->addr); + wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED); + set = 0; + + /* Do not allow the FT-over-DS exception to be used more than + * once per authentication exchange to guarantee a new TK is + * used here */ + sta->ft_over_ds = 0; + } + +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) + hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); +#endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (sta->flags & WLAN_STA_VHT) + hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); +#endif /* CONFIG_IEEE80211AC */ + + /* + * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags + * will be set when the ACK frame for the (Re)Association Response frame + * is processed (TX status driver event). + */ + if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, + sta->supported_rates, sta->supported_rates_len, + sta->listen_interval, + sta->flags & WLAN_STA_HT ? &ht_cap : NULL, + sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags | WLAN_STA_ASSOC, sta->qosinfo, + sta->vht_opmode, sta->p2p_ie ? 1 : 0, + set)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, + "Could not %s STA to kernel driver", + set ? "set" : "add"); + + if (sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + + return -1; + } + + sta->added_unassoc = 0; + + return 0; +} + + +static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *addr, u16 status_code, int reassoc, + const u8 *ies, size_t ies_len, int rssi) +{ int send_len; - u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + u8 *buf; + size_t buflen; struct ieee80211_mgmt *reply; u8 *p; + u16 res = WLAN_STATUS_SUCCESS; - os_memset(buf, 0, sizeof(buf)); + buflen = sizeof(struct ieee80211_mgmt) + 1024; +#ifdef CONFIG_FILS + if (sta && sta->fils_hlp_resp) + buflen += wpabuf_len(sta->fils_hlp_resp); +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) + buflen += 150; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + if (sta && sta->dpp_pfs) + buflen += 5 + sta->dpp_pfs->curve->prime_len; +#endif /* CONFIG_DPP2 */ + buf = os_zalloc(buflen); + if (!buf) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : WLAN_FC_STYPE_ASSOC_RESP)); - os_memcpy(reply->da, sta->addr, ETH_ALEN); + os_memcpy(reply->da, addr, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN); @@ -1667,24 +3359,50 @@ reply->u.assoc_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd)); reply->u.assoc_resp.status_code = host_to_le16(status_code); - reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15)); + + reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) | + BIT(14) | BIT(15)); /* Supported rates */ p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); -#ifdef CONFIG_IEEE80211R - if (status_code == WLAN_STATUS_SUCCESS) { +#ifdef CONFIG_MBO + if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && + rssi != 0) { + int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi; + + p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p, + delta); + } +#endif /* CONFIG_MBO */ + +#ifdef CONFIG_IEEE80211R_AP + if (sta && status_code == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS * Transition Information, RSN, [RIC Response] */ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, - buf + sizeof(buf) - p, + buf + buflen - p, sta->auth_alg, ies, ies_len); + if (!p) { + wpa_printf(MSG_DEBUG, + "FT: Failed to write AssocResp IEs"); + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_OWE + if (sta && status_code == WLAN_STATUS_SUCCESS && + (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) + p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p, + buf + buflen - p, + ies, ies_len); +#endif /* CONFIG_OWE */ + #ifdef CONFIG_IEEE80211W - if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) + if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) p = hostapd_eid_assoc_comeback_time(hapd, sta, p); #endif /* CONFIG_IEEE80211W */ @@ -1695,7 +3413,23 @@ #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - p = hostapd_eid_vht_capabilities(hapd, p); + u32 nsts = 0, sta_nsts; + + if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) { + struct ieee80211_vht_capabilities *capa; + + nsts = (hapd->iface->conf->vht_capab >> + VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + capa = sta->vht_capabilities; + sta_nsts = (le_to_host32(capa->vht_capabilities_info) >> + VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + + if (nsts < sta_nsts) + nsts = 0; + else + nsts = sta_nsts; + } + p = hostapd_eid_vht_capabilities(hapd, p, nsts); p = hostapd_eid_vht_operation(hapd, p); } #endif /* CONFIG_IEEE80211AC */ @@ -1702,7 +3436,7 @@ p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); - if (sta->qos_map_enabled) + if (sta && sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); #ifdef CONFIG_FST @@ -1713,17 +3447,51 @@ } #endif /* CONFIG_FST */ +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + /* OWE Diffie-Hellman Parameter element */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ + WPA_PUT_LE16(p, sta->owe_group); + p += 2; + os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); + p += wpabuf_len(pub); + wpabuf_free(pub); + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) { + os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie), + wpabuf_len(sta->dpp_pfs->ie)); + p += wpabuf_len(sta->dpp_pfs->ie); + } +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211AC - if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) + if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); #endif /* CONFIG_IEEE80211AC */ - if (sta->flags & WLAN_STA_WMM) + if (sta && (sta->flags & WLAN_STA_WMM)) p = hostapd_eid_wmm(hapd, p); #ifdef CONFIG_WPS - if ((sta->flags & WLAN_STA_WPS) || - ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) { + if (sta && + ((sta->flags & WLAN_STA_WPS) || + ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) { struct wpabuf *wps = wps_build_assoc_resp_ie(); if (wps) { os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); @@ -1733,8 +3501,11 @@ } #endif /* CONFIG_WPS */ + if (sta && (sta->flags & WLAN_STA_MULTI_AP)) + p = hostapd_eid_multi_ap(hapd, p); + #ifdef CONFIG_P2P - if (sta->p2p_ie) { + if (sta && sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { @@ -1763,23 +3534,180 @@ p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ + p = hostapd_eid_mbo(hapd, p, buf + buflen - p); + + if (hapd->conf->assocresp_elements && + (size_t) (buf + buflen - p) >= + wpabuf_len(hapd->conf->assocresp_elements)) { + os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), + wpabuf_len(hapd->conf->assocresp_elements)); + p += wpabuf_len(hapd->conf->assocresp_elements); + } + send_len += p - reply->u.assoc_resp.variable; - if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) +#ifdef CONFIG_FILS + if (sta && + (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) && + status_code == WLAN_STATUS_SUCCESS) { + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == + ParseFailed || !elems.fils_session) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + + /* FILS Session */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + FILS_SESSION_LEN; /* Length */ + *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ + os_memcpy(p, elems.fils_session, FILS_SESSION_LEN); + send_len += 2 + 1 + FILS_SESSION_LEN; + + send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len, + buflen, sta->fils_hlp_resp); + if (send_len < 0) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + } +#endif /* CONFIG_FILS */ + + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + } + +done: + os_free(buf); + return res; } +#ifdef CONFIG_OWE +u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *owe_dh, u8 owe_dh_len, + u8 *owe_buf, size_t owe_buf_len, u16 *reason) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->own_ie_override) { + wpa_printf(MSG_DEBUG, "OWE: Using IE override"); + *reason = WLAN_STATUS_SUCCESS; + return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + *reason = WLAN_STATUS_SUCCESS; + return owe_buf; + } + + *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); + if (*reason != WLAN_STATUS_SUCCESS) + return NULL; + + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + + if (sta->owe_ecdh && owe_buf) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + *reason = WLAN_STATUS_UNSPECIFIED_FAILURE; + return owe_buf; + } + + /* OWE Diffie-Hellman Parameter element */ + *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ + *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension + */ + WPA_PUT_LE16(owe_buf, sta->owe_group); + owe_buf += 2; + os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); + owe_buf += wpabuf_len(pub); + wpabuf_free(pub); + } + + return owe_buf; +} +#endif /* CONFIG_OWE */ + + +#ifdef CONFIG_FILS + +void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) +{ + u16 reply_res; + + wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR, + MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + if (!sta->fils_pending_assoc_req) + return; + reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, + sta->fils_pending_assoc_is_reassoc, + sta->fils_pending_assoc_req, + sta->fils_pending_assoc_req_len, 0); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + + /* + * Remove the station in case transmission of a success response fails. + * At this point the station was already added associated to the driver. + */ + if (reply_res != WLAN_STATUS_SUCCESS) + hostapd_drv_sta_remove(hapd, sta->addr); +} + + +void fils_hlp_timeout(void *eloop_ctx, void *eloop_data) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = eloop_data; + + wpa_printf(MSG_DEBUG, + "FILS: HLP response timeout - continue with association response for " + MACSTR, MAC2STR(sta->addr)); + if (sta->fils_drv_assoc_finish) + hostapd_notify_assoc_fils_finish(hapd, sta); + else + fils_hlp_finish_assoc(hapd, sta); +} + +#endif /* CONFIG_FILS */ + + static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, - int reassoc) + int reassoc, int rssi) { u16 capab_info, listen_interval, seq_ctrl, fc; - u16 resp = WLAN_STATUS_SUCCESS; + u16 resp = WLAN_STATUS_SUCCESS, reply_res; const u8 *pos; int left, i; struct sta_info *sta; + u8 *tmp = NULL; + struct hostapd_sta_wpa_psk_short *psk = NULL; + char *identity = NULL; + char *radius_cui = NULL; +#ifdef CONFIG_FILS + int delay_assoc = 0; +#endif /* CONFIG_FILS */ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { @@ -1837,31 +3765,89 @@ } sta = ap_get_sta(hapd, mgmt->sa); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " "prior to authentication since it is using " "over-the-DS FT", MAC2STR(mgmt->sa)); + + /* + * Mark station as authenticated, to avoid adding station + * entry in the driver as associated and not authenticated + */ + sta->flags |= WLAN_STA_AUTH; } else -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "Station tried to " - "associate before authentication " - "(aid=%d flags=0x%x)", - sta ? sta->aid : -1, - sta ? sta->flags : 0); - send_deauth(hapd, mgmt->sa, - WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); - return; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == + HOSTAPD_MODE_IEEE80211AD) { + int acl_res; + u32 session_timeout, acct_interim_interval; + struct vlan_description vlan_id; + + acl_res = ieee802_11_allowed_address( + hapd, mgmt->sa, (const u8 *) mgmt, len, + &session_timeout, &acct_interim_interval, + &vlan_id, &psk, &identity, &radius_cui, 0); + if (acl_res == HOSTAPD_ACL_REJECT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Association Request frame from " + MACSTR " due to ACL reject", + MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (acl_res == HOSTAPD_ACL_PENDING) + return; + + /* DMG/IEEE 802.11ad does not use authentication. + * Allocate sta entry upon association. */ + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Failed to add STA"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + acl_res = ieee802_11_set_radius_info( + hapd, sta, acl_res, session_timeout, + acct_interim_interval, &vlan_id, &psk, + &identity, &radius_cui); + if (acl_res) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Skip authentication for DMG/IEEE 802.11ad"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_OPEN; + } else { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Station tried to associate before authentication (aid=%d flags=0x%x)", + sta ? sta->aid : -1, + sta ? sta->flags : 0); + send_deauth(hapd, mgmt->sa, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); + return; + } } if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && - sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ : - WLAN_FC_STYPE_ASSOC_REQ) { + sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ : + WLAN_FC_STYPE_ASSOC_REQ)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated association frame seq_ctrl=0x%x", @@ -1873,7 +3859,7 @@ WLAN_FC_STYPE_ASSOC_REQ; if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -1886,6 +3872,53 @@ goto fail; } +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + if (hapd->iconf->rssi_reject_assoc_rssi && rssi && + rssi < hapd->iconf->rssi_reject_assoc_rssi && + (sta->auth_rssi == 0 || + sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) { + resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS; + goto fail; + } +#endif /* CONFIG_MBO */ + + /* + * sta->capability is used in check_assoc_ies() for RRM enabled + * capability element. + */ + sta->capability = capab_info; + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + int res; + + /* The end of the payload is encrypted. Need to decrypt it + * before parsing. */ + + tmp = os_memdup(pos, left); + if (!tmp) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt, + len, tmp, left); + if (res < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + pos = tmp; + left = res; + } +#endif /* CONFIG_FILS */ + /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); @@ -1899,10 +3932,10 @@ goto fail; } - sta->capability = capab_info; 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) { @@ -1921,7 +3954,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); @@ -1936,7 +3970,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); } @@ -1969,8 +4004,99 @@ * remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; +#ifdef CONFIG_TAXONOMY + taxonomy_sta_info_assoc_req(hapd, sta, pos, left); +#endif /* CONFIG_TAXONOMY */ + + sta->pending_wds_enable = 0; + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + if (fils_process_hlp(hapd, sta, pos, left) > 0) + delay_assoc = 1; + } +#endif /* CONFIG_FILS */ + fail: - send_assoc_resp(hapd, sta, resp, reassoc, pos, left); + os_free(identity); + os_free(radius_cui); + hostapd_free_psk_list(psk); + + /* + * In case of a successful response, add the station to the driver. + * Otherwise, the kernel may ignore Data frames before we process the + * ACK frame (TX status). In case of a failure, this station will be + * removed. + * + * Note that this is not compliant with the IEEE 802.11 standard that + * states that a non-AP station should transition into the + * authenticated/associated state only after the station acknowledges + * the (Re)Association Response frame. However, still do this as: + * + * 1. In case the station does not acknowledge the (Re)Association + * Response frame, it will be removed. + * 2. Data frames will be dropped in the kernel until the station is + * set into authorized state, and there are no significant known + * issues with processing other non-Data Class 3 frames during this + * window. + */ + if (resp == WLAN_STATUS_SUCCESS && sta && + add_associated_sta(hapd, sta, reassoc)) + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + +#ifdef CONFIG_FILS + if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS && + eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) && + sta->fils_pending_assoc_req) { + /* Do not reschedule fils_hlp_timeout in case the station + * retransmits (Re)Association Request frame while waiting for + * the previously started FILS HLP wait, so that the timeout can + * be determined from the first pending attempt. */ + wpa_printf(MSG_DEBUG, + "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + os_free(tmp); + return; + } + if (sta) { + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + } + if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) { + sta->fils_pending_assoc_req = tmp; + sta->fils_pending_assoc_req_len = left; + sta->fils_pending_assoc_is_reassoc = reassoc; + sta->fils_drv_assoc_finish = 0; + wpa_printf(MSG_DEBUG, + "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024, + fils_hlp_timeout, hapd, sta); + return; + } +#endif /* CONFIG_FILS */ + + reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos, + left, rssi); + os_free(tmp); + + /* + * Remove the station in case tranmission of a success response fails + * (the STA was added associated to the driver) or if the station was + * previously added unassociated. + */ + if (sta && ((reply_res != WLAN_STATUS_SUCCESS && + resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } @@ -2007,11 +4133,12 @@ /* Stop Accounting and IEEE 802.1X sessions, but leave the STA * authenticated. */ accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); if (sta->ipaddr) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { @@ -2023,6 +4150,17 @@ mlme_disassociate_indication( hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); + + /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon + * disassociation. */ + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + sta->flags &= ~WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "deauthenticated"); + ap_free_sta(hapd, sta); + } } @@ -2086,28 +4224,6 @@ #ifdef CONFIG_IEEE80211W - -static int hostapd_sa_query_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, - size_t len) -{ - const u8 *end; - - end = mgmt->u.action.u.sa_query_resp.trans_id + - WLAN_SA_QUERY_TR_ID_LEN; - if (((u8 *) mgmt) + len < end) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " - "frame (len=%lu)", (unsigned long) len); - return 0; - } - - ieee802_11_sa_query_action(hapd, mgmt->sa, - mgmt->u.action.u.sa_query_resp.action, - mgmt->u.action.u.sa_query_resp.trans_id); - return 1; -} - - static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && @@ -2117,12 +4233,13 @@ static int handle_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) + const struct ieee80211_mgmt *mgmt, size_t len, + unsigned int freq) { struct sta_info *sta; - sta = ap_get_sta(hapd, mgmt->sa); + u8 *action __maybe_unused; - if (len < IEEE80211_HDRLEN + 1) { + if (len < IEEE80211_HDRLEN + 2 + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - too short payload (len=%lu)", @@ -2130,11 +4247,19 @@ return 0; } + action = (u8 *) &mgmt->u.action.u; + wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR + " da " MACSTR " len %d freq %u", + mgmt->u.action.category, *action, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq); + + sta = ap_get_sta(hapd, mgmt->sa); + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " "frame (category=%u) from unassociated STA " MACSTR, - MAC2STR(mgmt->sa), mgmt->u.action.category); + mgmt->u.action.category, MAC2STR(mgmt->sa)); return 0; } @@ -2171,7 +4296,7 @@ } switch (mgmt->u.action.category) { -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP case WLAN_ACTION_FT: if (!sta || wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, @@ -2178,19 +4303,20 @@ len - IEEE80211_HDRLEN)) break; return 1; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); return 1; #ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: - return hostapd_sa_query_action(hapd, mgmt, len); + ieee802_11_sa_query_action(hapd, mgmt, len); + return 1; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP case WLAN_ACTION_WNM: ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST case WLAN_ACTION_FST: if (hapd->iface->fst) @@ -2206,12 +4332,41 @@ if (len >= IEEE80211_HDRLEN + 2 && mgmt->u.action.u.public_action.action == WLAN_PA_20_40_BSS_COEX) { - wpa_printf(MSG_DEBUG, - "HT20/40 coex mgmt frame received from STA " - MACSTR, MAC2STR(mgmt->sa)); hostapd_2040_coex_action(hapd, mgmt, len); + return 1; } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_DPP + if (len >= IEEE80211_HDRLEN + 6 && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + + pos = mgmt->u.action.u.vs_public_action.oui; + end = ((const u8 *) mgmt) + len; + hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, + freq); + return 1; + } + if (len >= IEEE80211_HDRLEN + 2 && + (mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_INITIAL_RESP || + mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_COMEBACK_RESP)) { + const u8 *pos, *end; + + pos = &mgmt->u.action.u.public_action.action; + end = ((const u8 *) mgmt) + len; + gas_query_ap_rx(hapd->gas, mgmt->sa, + mgmt->u.action.category, + pos, end - pos, hapd->iface->freq); + return 1; + } +#endif /* CONFIG_DPP */ if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, @@ -2233,6 +4388,9 @@ return 1; } break; + case WLAN_ACTION_RADIO_MEASUREMENT: + hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len); + return 1; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -2240,8 +4398,9 @@ "handle_action - unknown action category %d or invalid " "frame", mgmt->u.action.category); - if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && - !(mgmt->sa[0] & 0x01)) { + if (!is_multicast_ether_addr(mgmt->da) && + !(mgmt->u.action.category & 0x80) && + !is_multicast_ether_addr(mgmt->sa)) { struct ieee80211_mgmt *resp; /* @@ -2251,10 +4410,9 @@ */ wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " "frame back to sender"); - resp = os_malloc(len); + resp = os_memdup(mgmt, len); if (resp == NULL) return 0; - os_memcpy(resp, mgmt, len); os_memcpy(resp->da, resp->sa, ETH_ALEN); os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); @@ -2288,13 +4446,19 @@ struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt; - int broadcast; 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); @@ -2304,11 +4468,7 @@ return 1; } - broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && - mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && - mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; - - if (!broadcast && + if (!is_broadcast_ether_addr(mgmt->bssid) && #ifdef CONFIG_P2P /* Invitation responses can be sent with the peer MAC as BSSID */ !((hapd->conf->p2p & P2P_GROUP_OWNER) && @@ -2325,11 +4485,13 @@ if (stype == WLAN_FC_STYPE_PROBE_REQ) { - handle_probe_req(hapd, mgmt, len, fi->ssi_signal); + handle_probe_req(hapd, mgmt, len, ssi_signal); return 1; } - if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { + if ((!is_broadcast_ether_addr(mgmt->da) || + stype != WLAN_FC_STYPE_ACTION) && + os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "MGMT: DA=" MACSTR " not our address", @@ -2338,22 +4500,22 @@ } if (hapd->iconf->track_sta_max_num) - sta_track_add(hapd->iface, mgmt->sa); + sta_track_add(hapd->iface, mgmt->sa, ssi_signal); switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); - handle_auth(hapd, mgmt, len); + handle_auth(hapd, mgmt, len, ssi_signal, 0); ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); - handle_assoc(hapd, mgmt, len, 0); + handle_assoc(hapd, mgmt, len, 0, ssi_signal); ret = 1; break; case WLAN_FC_STYPE_REASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); - handle_assoc(hapd, mgmt, len, 1); + handle_assoc(hapd, mgmt, len, 1, ssi_signal); ret = 1; break; case WLAN_FC_STYPE_DISASSOC: @@ -2368,7 +4530,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, @@ -2388,30 +4550,31 @@ u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR + " not found", + MAC2STR(mgmt->da)); + return; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "did not acknowledge authentication response"); - return; + goto fail; } if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", (unsigned long) len); - return; + goto fail; } - auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); - status_code = le_to_host16(mgmt->u.auth.status_code); - - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", - MAC2STR(mgmt->da)); - return; - } - if (status_code == WLAN_STATUS_SUCCESS && ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { @@ -2418,7 +4581,16 @@ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "authenticated"); sta->flags |= WLAN_STA_AUTH; + if (sta->added_unassoc) + hostapd_set_sta_flags(hapd, sta); + return; } + +fail: + if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } @@ -2453,16 +4625,7 @@ u16 status; struct sta_info *sta; int new_assoc = 1; - struct ieee80211_ht_capabilities ht_cap; - struct ieee80211_vht_capabilities vht_cap; - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : - sizeof(mgmt->u.assoc_resp))) { - wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", - reassoc, (unsigned long) len); - return; - } - sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found", @@ -2470,19 +4633,32 @@ return; } + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : + sizeof(mgmt->u.assoc_resp))) { + wpa_printf(MSG_INFO, + "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); + hostapd_drv_sta_remove(hapd, sta->addr); + return; + } + + if (reassoc) + status = le_to_host16(mgmt->u.reassoc_resp.status_code); + else + status = le_to_host16(mgmt->u.assoc_resp.status_code); + if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "did not acknowledge association response"); sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + /* The STA is added only in case of SUCCESS */ + if (status == WLAN_STATUS_SUCCESS) + hostapd_drv_sta_remove(hapd, sta->addr); + return; } - if (reassoc) - status = le_to_host16(mgmt->u.reassoc_resp.status_code); - else - status = le_to_host16(mgmt->u.assoc_resp.status_code); - if (status != WLAN_STATUS_SUCCESS) return; @@ -2499,11 +4675,15 @@ new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; - if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) || + if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && + !hapd->conf->osen) || + sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK || sta->auth_alg == WLAN_AUTH_FT) { /* - * Open, static WEP, or FT protocol; no separate authorization - * step. + * Open, static WEP, FT protocol, or FILS; no separate + * authorization step. */ ap_sta_set_authorized(hapd, sta, 1); } @@ -2517,48 +4697,6 @@ sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ - /* - * Remove the STA entry in order to make sure the STA PS state gets - * cleared and configuration gets updated in case of reassociation back - * to the same AP. - */ - hostapd_drv_sta_remove(hapd, sta->addr); - -#ifdef CONFIG_IEEE80211N - if (sta->flags & WLAN_STA_HT) - hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); -#endif /* CONFIG_IEEE80211N */ -#ifdef CONFIG_IEEE80211AC - if (sta->flags & WLAN_STA_VHT) - hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); -#endif /* CONFIG_IEEE80211AC */ - - if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, - sta->supported_rates, sta->supported_rates_len, - sta->listen_interval, - sta->flags & WLAN_STA_HT ? &ht_cap : NULL, - sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, - sta->flags, sta->qosinfo, sta->vht_opmode)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "Could not add STA to kernel driver"); - - ap_sta_disconnect(hapd, sta, sta->addr, - WLAN_REASON_DISASSOC_AP_BUSY); - - return; - } - - if (sta->flags & WLAN_STA_WDS) { - int ret; - char ifname_wds[IFNAMSIZ + 1]; - - ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, - sta->aid, 1); - if (!ret) - hostapd_set_wds_encryption(hapd, sta, ifname_wds); - } - if (sta->eapol_sm == NULL) { /* * This STA does not use RADIUS server for EAP authentication, @@ -2575,13 +4713,64 @@ hostapd_set_sta_flags(hapd, sta); + if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) { + wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA " + MACSTR " based on pending request", + MAC2STR(sta->addr)); + sta->pending_wds_enable = 0; + sta->flags |= WLAN_STA_WDS; + } + + if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; + + wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA " + MACSTR " (aid %u)", + MAC2STR(sta->addr), sta->aid); + ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, + sta->aid, 1); + if (!ret) + hostapd_set_wds_encryption(hapd, sta, ifname_wds); + } + if (sta->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); - ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); +#ifdef CONFIG_FILS + if ((sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) && + fils_set_tk(sta->wpa_sm) < 0) { + wpa_printf(MSG_DEBUG, "FILS: TK configuration failed"); + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + return; + } +#endif /* CONFIG_FILS */ + + if (sta->pending_eapol_rx) { + struct os_reltime now, age; + + os_get_reltime(&now); + os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age); + if (age.sec == 0 && age.usec < 200000) { + wpa_printf(MSG_DEBUG, + "Process pending EAPOL frame that was received from " MACSTR " just before association notification", + MAC2STR(sta->addr)); + ieee802_1x_receive( + hapd, mgmt->da, + wpabuf_head(sta->pending_eapol_rx->buf), + wpabuf_len(sta->pending_eapol_rx->buf)); + } + wpabuf_free(sta->pending_eapol_rx->buf); + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + } } @@ -2590,7 +4779,7 @@ size_t len, int ok) { struct sta_info *sta; - if (mgmt->da[0] & 0x01) + if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2614,7 +4803,7 @@ size_t len, int ok) { struct sta_info *sta; - if (mgmt->da[0] & 0x01) + if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2633,6 +4822,65 @@ } +static void handle_action_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + const struct rrm_measurement_report_element *report; + + if (is_multicast_ether_addr(mgmt->da)) + return; +#ifdef CONFIG_DPP + if (len >= IEEE80211_HDRLEN + 6 && + mgmt->u.action.category == WLAN_ACTION_PUBLIC && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + + pos = &mgmt->u.action.u.vs_public_action.variable[1]; + end = ((const u8 *) mgmt) + len; + hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok); + return; + } + if (len >= IEEE80211_HDRLEN + 2 && + mgmt->u.action.category == WLAN_ACTION_PUBLIC && + (mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_INITIAL_REQ || + mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_COMEBACK_REQ)) { + const u8 *pos, *end; + + pos = mgmt->u.action.u.public_action.variable; + end = ((const u8 *) mgmt) + len; + gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok); + return; + } +#endif /* CONFIG_DPP */ + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + + if (len < 24 + 5 + sizeof(*report)) + return; + report = (const struct rrm_measurement_report_element *) + &mgmt->u.action.u.rrm.variable[2]; + if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT && + mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST && + report->eid == WLAN_EID_MEASURE_REQUEST && + report->len >= 3 && + report->type == MEASURE_TYPE_BEACON) + hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok); +} + + /** * ieee802_11_mgmt_cb - Process management frame TX status callback * @hapd: hostapd BSS data structure (the BSS from which the management frame @@ -2650,8 +4898,16 @@ #ifdef CONFIG_TESTING_OPTIONS if (hapd->ext_mgmt_frame_handling) { - wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d", - stype, ok); + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + "MGMT-TX-STATUS stype=%u ok=%d buf=%s", + stype, ok, hex); + os_free(hex); + } return; } #endif /* CONFIG_TESTING_OPTIONS */ @@ -2670,7 +4926,7 @@ handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: - wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb"); + wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok); break; case WLAN_FC_STYPE_DEAUTH: wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); @@ -2681,7 +4937,8 @@ handle_disassoc_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ACTION: - wpa_printf(MSG_DEBUG, "mgmt::action cb"); + wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); + handle_action_cb(hapd, mgmt, len, ok); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); @@ -2779,6 +5036,8 @@ } if (sta == NULL) return; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR, + MAC2STR(sta->addr)); if (!(sta->flags & WLAN_STA_PENDING_POLL)) return; @@ -2794,10 +5053,22 @@ struct sta_info *sta; sta = ap_get_sta(hapd, src); - if (sta && (sta->flags & WLAN_STA_ASSOC)) { + if (sta && + ((sta->flags & WLAN_STA_ASSOC) || + ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) { if (!hapd->conf->wds_sta) return; + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) == + WLAN_STA_ASSOC_REQ_OK) { + wpa_printf(MSG_DEBUG, + "Postpone 4-address WDS mode enabling for STA " + MACSTR " since TX status for AssocResp is not yet known", + MAC2STR(sta->addr)); + sta->pending_wds_enable = 1; + return; + } + if (wds && !(sta->flags & WLAN_STA_WDS)) { int ret; char ifname_wds[IFNAMSIZ + 1]; @@ -2817,7 +5088,7 @@ wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); - if (src[0] & 0x01) { + if (is_multicast_ether_addr(src)) { /* Broadcast bit set in SA?! Ignore the frame silently. */ return; } --- contrib/wpa/src/ap/ieee802_11.h.orig +++ contrib/wpa/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); @@ -49,9 +51,16 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts); u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid); + int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id); @@ -61,6 +70,7 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); +int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab); u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, @@ -71,6 +81,8 @@ void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab); +u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_oper); u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_opmode); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, @@ -82,8 +94,8 @@ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, struct sta_info *sta, u8 *eid); void ieee802_11_sa_query_action(struct hostapd_data *hapd, - const u8 *sa, const u8 action_type, - const u8 *trans_id); + const struct ieee80211_mgmt *mgmt, + size_t len); u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); @@ -97,6 +109,7 @@ #ifdef CONFIG_SAE void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta); +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta); #else /* CONFIG_SAE */ static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) @@ -104,4 +117,64 @@ } #endif /* CONFIG_SAE */ +#ifdef CONFIG_MBO + +u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len); + +u8 hostapd_mbo_ie_len(struct hostapd_data *hapd); + +u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, + size_t len, int delta); + +#else /* CONFIG_MBO */ + +static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ + return eid; +} + +static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) +{ + return 0; +} + +#endif /* CONFIG_MBO */ + +void ap_copy_sta_supp_op_classes(struct sta_info *sta, + const u8 *supp_op_classes, + size_t supp_op_classes_len); + +u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid); +void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, + struct sta_info *sta, int success, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len); +u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *owe_dh, u8 owe_dh_len, + u8 *owe_buf, size_t owe_buf_len, u16 *reason); +void fils_hlp_timeout(void *eloop_ctx, void *eloop_data); +void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta); +void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, size_t len, u16 auth_alg, + u16 auth_transaction, u16 status_code, + void (*cb)(struct hostapd_data *hapd, + struct sta_info *sta, + u16 resp, struct wpabuf *data, int pub)); + +size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd); +u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len); +int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui, + int is_probe_req); + +int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx); + +void auth_sae_process_commit(void *eloop_ctx, void *user_ctx); + #endif /* IEEE802_11_H */ --- contrib/wpa/src/ap/ieee802_11_auth.c.orig +++ contrib/wpa/src/ap/ieee802_11_auth.c @@ -15,7 +15,6 @@ #include "utils/common.h" #include "utils/eloop.h" -#include "crypto/sha1.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" @@ -35,7 +34,7 @@ struct hostapd_cached_radius_acl *next; u32 session_timeout; u32 acct_interim_interval; - int vlan_id; + struct vlan_description vlan_id; struct hostapd_sta_wpa_psk_short *psk; char *identity; char *radius_cui; @@ -77,29 +76,20 @@ static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, struct hostapd_sta_wpa_psk_short *src) { - struct hostapd_sta_wpa_psk_short **copy_to; - struct hostapd_sta_wpa_psk_short *copy_from; + if (!psk) + return; - /* Copy PSK linked list */ - copy_to = psk; - copy_from = src; - while (copy_from && copy_to) { - *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); - if (*copy_to == NULL) - break; - os_memcpy(*copy_to, copy_from, - sizeof(struct hostapd_sta_wpa_psk_short)); - copy_from = copy_from->next; - copy_to = &((*copy_to)->next); - } - if (copy_to) - *copy_to = NULL; + if (src) + src->ref++; + + *psk = src; } static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui) { @@ -165,7 +155,10 @@ if (msg == NULL) return -1; - radius_msg_make_authenticator(msg, addr, ETH_ALEN); + if (radius_msg_make_authenticator(msg) < 0) { + wpa_printf(MSG_INFO, "Could not make Request Authenticator"); + goto fail; + } os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, @@ -213,6 +206,33 @@ /** + * hostapd_check_acl - Check a specified STA against accept/deny ACLs + * @hapd: hostapd BSS data + * @addr: MAC address of the STA + * @vlan_id: Buffer for returning VLAN ID + * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + */ +int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, + struct vlan_description *vlan_id) +{ + if (hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, addr, vlan_id)) + return HOSTAPD_ACL_ACCEPT; + + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, addr, vlan_id)) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) + return HOSTAPD_ACL_ACCEPT; + if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) + return HOSTAPD_ACL_REJECT; + + return HOSTAPD_ACL_PENDING; +} + + +/** * hostapd_allowed_address - Check whether a specified STA can be authenticated * @hapd: hostapd BSS data * @addr: MAC address of the STA @@ -224,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 @@ -231,16 +252,20 @@ */ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + 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; + if (session_timeout) *session_timeout = 0; if (acct_interim_interval) *acct_interim_interval = 0; if (vlan_id) - *vlan_id = 0; + os_memset(vlan_id, 0, sizeof(*vlan_id)); if (psk) *psk = NULL; if (identity) @@ -248,19 +273,10 @@ if (radius_cui) *radius_cui = NULL; - if (hostapd_maclist_found(hapd->conf->accept_mac, - hapd->conf->num_accept_mac, addr, vlan_id)) - return HOSTAPD_ACL_ACCEPT; + res = hostapd_check_acl(hapd, addr, vlan_id); + if (res != HOSTAPD_ACL_PENDING) + return res; - if (hostapd_maclist_found(hapd->conf->deny_mac, - hapd->conf->num_deny_mac, addr, vlan_id)) - return HOSTAPD_ACL_REJECT; - - if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) - return HOSTAPD_ACL_ACCEPT; - if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) - return HOSTAPD_ACL_REJECT; - if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { #ifdef CONFIG_NO_RADIUS return HOSTAPD_ACL_REJECT; @@ -267,11 +283,19 @@ #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; + }; + + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) + vlan_id = NULL; + /* Check whether ACL cache has an entry for this station */ - int res = hostapd_acl_cache_get(hapd, addr, session_timeout, - acct_interim_interval, - vlan_id, psk, - identity, radius_cui); + res = hostapd_acl_cache_get(hapd, addr, session_timeout, + acct_interim_interval, vlan_id, psk, + identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; @@ -314,7 +338,7 @@ return HOSTAPD_ACL_REJECT; } - query->auth_msg = os_malloc(len); + query->auth_msg = os_memdup(msg, len); if (query->auth_msg == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "auth frame."); @@ -321,7 +345,6 @@ hostapd_acl_query_free(query); return HOSTAPD_ACL_REJECT; } - os_memcpy(query->auth_msg, msg, len); query->auth_msg_len = len; query->next = hapd->acl_queries; hapd->acl_queries = query; @@ -419,7 +442,7 @@ struct hostapd_cached_radius_acl *cache) { int passphraselen; - char *passphrase, *strpassphrase; + char *passphrase; size_t i; struct hostapd_sta_wpa_psk_short *psk; @@ -436,24 +459,42 @@ */ if (passphrase == NULL) break; + /* + * Passphase should be 8..63 chars (to be hashed with SSID) + * or 64 chars hex string (no separate hashing with SSID). + */ + + if (passphraselen < MIN_PASSPHRASE_LEN || + passphraselen > MAX_PASSPHRASE_LEN + 1) + goto free_pass; + + /* * passphrase does not contain the NULL termination. * Add it here as pbkdf2_sha1() requires it. */ - strpassphrase = os_zalloc(passphraselen + 1); psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); - if (strpassphrase && psk) { - os_memcpy(strpassphrase, passphrase, passphraselen); - pbkdf2_sha1(strpassphrase, - hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len, 4096, - psk->psk, PMK_LEN); + if (psk) { + if ((passphraselen == MAX_PASSPHRASE_LEN + 1) && + (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) { + hostapd_logger(hapd, cache->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_WARNING, + "invalid hex string (%d chars) in Tunnel-Password", + passphraselen); + goto skip; + } else if (passphraselen <= MAX_PASSPHRASE_LEN) { + os_memcpy(psk->passphrase, passphrase, + passphraselen); + psk->is_passphrase = 1; + } psk->next = cache->psk; cache->psk = psk; psk = NULL; } - os_free(strpassphrase); +skip: os_free(psk); +free_pass: os_free(passphrase); } } @@ -535,7 +576,10 @@ cache->acct_interim_interval = 0; } - cache->vlan_id = radius_msg_get_vlanid(msg); + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) + cache->vlan_id.notempty = !!radius_msg_get_vlanid( + msg, &cache->vlan_id.untagged, + MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged); decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, msg, req, cache); @@ -558,17 +602,18 @@ !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; - if (cache->vlan_id && - !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) { + if (cache->vlan_id.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &cache->vlan_id)) { hostapd_logger(hapd, query->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "Invalid VLAN ID %d received from RADIUS server", - cache->vlan_id); - cache->vlan_id = 0; + "Invalid VLAN %d%s received from RADIUS server", + cache->vlan_id.untagged, + cache->vlan_id.tagged[0] ? "+" : ""); + os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id)); } if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && - !cache->vlan_id) + !cache->vlan_id.notempty) cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; @@ -627,9 +672,11 @@ #ifndef CONFIG_NO_RADIUS hostapd_acl_cache_free(hapd->acl_cache); + hapd->acl_cache = NULL; #endif /* CONFIG_NO_RADIUS */ query = hapd->acl_queries; + hapd->acl_queries = NULL; while (query) { prev = query; query = query->next; @@ -640,6 +687,12 @@ void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) { + if (psk && psk->ref) { + /* This will be freed when the last reference is dropped. */ + psk->ref--; + return; + } + while (psk) { struct hostapd_sta_wpa_psk_short *prev = psk; psk = psk->next; --- contrib/wpa/src/ap/ieee802_11_auth.h.orig +++ contrib/wpa/src/ap/ieee802_11_auth.h @@ -16,11 +16,15 @@ HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 }; +int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, + struct vlan_description *vlan_id); int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + 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); --- contrib/wpa/src/ap/ieee802_11_he.c.orig +++ contrib/wpa/src/ap/ieee802_11_he.c @@ -0,0 +1,119 @@ +/* + * hostapd / IEEE 802.11ax HE + * Copyright (c) 2016-2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_config.h" +#include "beacon.h" +#include "ieee802_11.h" +#include "dfs.h" + +u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_capabilities *cap; + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(struct ieee80211_he_capabilities); + *pos++ = WLAN_EID_EXT_HE_CAPABILITIES; + + cap = (struct ieee80211_he_capabilities *) pos; + os_memset(cap, 0, sizeof(*cap)); + + if (hapd->iface->conf->he_phy_capab.he_su_beamformer) + cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |= + HE_PHYCAP_SU_BEAMFORMER_CAPAB; + + if (hapd->iface->conf->he_phy_capab.he_su_beamformee) + cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |= + HE_PHYCAP_SU_BEAMFORMEE_CAPAB; + + if (hapd->iface->conf->he_phy_capab.he_mu_beamformer) + cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |= + HE_PHYCAP_MU_BEAMFORMER_CAPAB; + + pos += sizeof(*cap); + + return pos; +} + + +u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_operation *oper; + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(struct ieee80211_he_operation); + *pos++ = WLAN_EID_EXT_HE_OPERATION; + + oper = (struct ieee80211_he_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + if (hapd->iface->conf->he_op.he_bss_color) + oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color; + + if (hapd->iface->conf->he_op.he_default_pe_duration) + oper->he_oper_params |= + (hapd->iface->conf->he_op.he_default_pe_duration << + HE_OPERATION_DFLT_PE_DURATION_OFFSET); + + if (hapd->iface->conf->he_op.he_twt_required) + oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED; + + if (hapd->iface->conf->he_op.he_rts_threshold) + oper->he_oper_params |= + (hapd->iface->conf->he_op.he_rts_threshold << + HE_OPERATION_RTS_THRESHOLD_OFFSET); + + /* TODO: conditional MaxBSSID Indicator subfield */ + + pos += sizeof(*oper); + + return pos; +} + + +u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_mu_edca_parameter_set *edca; + u8 *pos; + size_t i; + + pos = (u8 *) &hapd->iface->conf->he_mu_edca; + for (i = 0; i < sizeof(*edca); i++) { + if (pos[i]) + break; + } + if (i == sizeof(*edca)) + return eid; /* no MU EDCA Parameters configured */ + + pos = eid; + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(*edca); + *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS; + + edca = (struct ieee80211_he_mu_edca_parameter_set *) pos; + os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca)); + + wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element", + pos, sizeof(*edca)); + + pos += sizeof(*edca); + + return pos; +} --- contrib/wpa/src/ap/ieee802_11_ht.c.orig +++ contrib/wpa/src/ap/ieee802_11_ht.c @@ -108,6 +108,29 @@ } +u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +{ + u8 sec_ch; + + if (!hapd->cs_freq_params.channel || + !hapd->cs_freq_params.sec_channel_offset) + return eid; + + if (hapd->cs_freq_params.sec_channel_offset == -1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; + else if (hapd->cs_freq_params.sec_channel_offset == 1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; + else + return eid; + + *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; + *eid++ = 1; + *eid++ = sec_ch; + + return eid; +} + + /* op_mode Set to 0 (HT pure) under the followign conditions @@ -213,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) { @@ -231,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, @@ -246,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, @@ -253,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", @@ -269,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, @@ -278,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); @@ -317,8 +384,8 @@ * that did not specify a valid WMM IE in the (Re)Association Request * frame. */ - if (!ht_capab || - !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) { + if (!ht_capab || !(sta->flags & WLAN_STA_WMM) || + !hapd->iconf->ieee80211n || hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; --- contrib/wpa/src/ap/ieee802_11_shared.c.orig +++ contrib/wpa/src/ap/ieee802_11_shared.c @@ -10,10 +10,12 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/ocv.h" #include "hostapd.h" #include "sta_info.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "wpa_auth.h" #include "ieee802_11.h" @@ -49,7 +51,12 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id) { - struct ieee80211_mgmt mgmt; +#ifdef CONFIG_OCV + struct sta_info *sta; +#endif /* CONFIG_OCV */ + struct ieee80211_mgmt *mgmt; + u8 *oci_ie = NULL; + u8 oci_ie_len = 0; u8 *end; wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " @@ -57,19 +64,61 @@ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", trans_id, WLAN_SA_QUERY_TR_ID_LEN); - os_memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(mgmt.da, addr, ETH_ALEN); - os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); - mgmt.u.action.category = WLAN_ACTION_SA_QUERY; - mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; - os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, +#ifdef CONFIG_OCV + sta = ap_get_sta(hapd, addr); + if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in SA Query Request"); + return; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for OCI element in SA Query Request"); + return; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(oci_ie); + return; + } + } +#endif /* CONFIG_OCV */ + + mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len); + if (!mgmt) { + wpa_printf(MSG_DEBUG, + "Failed to allocate buffer for SA Query Response frame"); + os_free(oci_ie); + return; + } + + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category = WLAN_ACTION_SA_QUERY; + mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id, WLAN_SA_QUERY_TR_ID_LEN); - end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + end = mgmt->u.action.u.sa_query_req.variable; +#ifdef CONFIG_OCV + if (oci_ie_len > 0) { + os_memcpy(end, oci_ie, oci_ie_len); + end += oci_ie_len; + } +#endif /* CONFIG_OCV */ + if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0) wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); + + os_free(mgmt); + os_free(oci_ie); } @@ -77,7 +126,9 @@ const u8 *sa, const u8 *trans_id) { struct sta_info *sta; - struct ieee80211_mgmt resp; + struct ieee80211_mgmt *resp; + u8 *oci_ie = NULL; + u8 oci_ie_len = 0; u8 *end; wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " @@ -92,31 +143,124 @@ return; } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in SA Query Response"); + return; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for for OCI element in SA Query Response"); + return; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(oci_ie); + return; + } + } +#endif /* CONFIG_OCV */ + + resp = os_zalloc(sizeof(*resp) + oci_ie_len); + if (!resp) { + wpa_printf(MSG_DEBUG, + "Failed to allocate buffer for SA Query Response frame"); + os_free(oci_ie); + return; + } + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " MACSTR, MAC2STR(sa)); - os_memset(&resp, 0, sizeof(resp)); - resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(resp.da, sa, ETH_ALEN); - os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); - resp.u.action.category = WLAN_ACTION_SA_QUERY; - resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; - os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, + resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(resp->da, sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.action.category = WLAN_ACTION_SA_QUERY; + resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id, WLAN_SA_QUERY_TR_ID_LEN); - end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) + end = resp->u.action.u.sa_query_req.variable; +#ifdef CONFIG_OCV + if (oci_ie_len > 0) { + os_memcpy(end, oci_ie, oci_ie_len); + end += oci_ie_len; + } +#endif /* CONFIG_OCV */ + if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0) wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); + + os_free(resp); + os_free(oci_ie); } -void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, - const u8 action_type, const u8 *trans_id) +void ieee802_11_sa_query_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len) { struct sta_info *sta; int i; + const u8 *sa = mgmt->sa; + const u8 action_type = mgmt->u.action.u.sa_query_resp.action; + const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id; + if (((const u8 *) mgmt) + len < + mgmt->u.action.u.sa_query_resp.variable) { + wpa_printf(MSG_DEBUG, + "IEEE 802.11: Too short SA Query Action frame (len=%lu)", + (unsigned long) len); + return; + } + + sta = ap_get_sta(hapd, sa); + +#ifdef CONFIG_OCV + if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct ieee802_11_elems elems; + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + size_t ies_len; + const u8 *ies; + + ies = mgmt->u.action.u.sa_query_resp.variable; + ies_len = len - (ies - (u8 *) mgmt); + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "SA Query: Failed to parse elements"); + return; + } + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in SA Query Action frame"); + return; + } + + if (get_sta_tx_parameters(sta->wpa_sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (action_type == WLAN_SA_QUERY_REQUEST) { ieee802_11_send_sa_query_resp(hapd, sa, trans_id); return; @@ -135,7 +279,6 @@ /* MLME-SAQuery.confirm */ - sta = ap_get_sta(hapd, sa); if (sta == NULL || sta->sa_query_trans_id == NULL) { wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " "pending SA Query request found"); @@ -172,10 +315,16 @@ case 0: /* Bits 0-7 */ if (hapd->iconf->obss_interval) *pos |= 0x01; /* Bit 0 - Coexistence management */ + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) + *pos |= 0x04; /* Bit 2 - Extended Channel Switching */ break; case 1: /* Bits 8-15 */ if (hapd->conf->proxy_arp) *pos |= 0x10; /* Bit 12 - Proxy ARP */ + if (hapd->conf->coloc_intf_reporting) { + /* Bit 13 - Collocated Interference Reporting */ + *pos |= 0x20; + } break; case 2: /* Bits 16-23 */ if (hapd->conf->wnm_sleep_mode) @@ -184,9 +333,9 @@ *pos |= 0x08; /* Bit 19 - BSS Transition */ break; case 3: /* Bits 24-31 */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP *pos |= 0x02; /* Bit 25 - SSID List */ -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ if (hapd->conf->time_advertisement == 2) *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ if (hapd->conf->interworking) @@ -207,11 +356,45 @@ if (hapd->conf->hs20) *pos |= 0x40; /* Bit 46 - WNM-Notification */ #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_MBO */ break; case 6: /* Bits 48-55 */ if (hapd->conf->ssid.utf8_ssid) *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ break; + case 7: /* Bits 56-63 */ + break; + case 8: /* Bits 64-71 */ + if (hapd->conf->ftm_responder) + *pos |= 0x40; /* Bit 70 - FTM responder */ + if (hapd->conf->ftm_initiator) + *pos |= 0x80; /* Bit 71 - FTM initiator */ + break; + case 9: /* Bits 72-79 */ +#ifdef CONFIG_FILS + if ((hapd->conf->wpa & WPA_PROTO_RSN) && + wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) + *pos |= 0x01; +#endif /* CONFIG_FILS */ + break; + case 10: /* Bits 80-87 */ +#ifdef CONFIG_SAE + if (hapd->conf->wpa && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) { + int in_use = hostapd_sae_pw_id_in_use(hapd->conf); + + if (in_use) + *pos |= 0x02; /* Bit 81 - SAE Password + * Identifiers In Use */ + if (in_use == 2) + *pos |= 0x04; /* Bit 82 - SAE Password + * Identifiers Used Exclusively */ + } +#endif /* CONFIG_SAE */ + break; } } @@ -231,14 +414,32 @@ len = 1; if (len < 7 && hapd->conf->ssid.utf8_ssid) len = 7; -#ifdef CONFIG_WNM + if (len < 9 && + (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) + len = 9; +#ifdef CONFIG_WNM_AP if (len < 4) len = 4; -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_HS20 if (hapd->conf->hs20 && len < 6) len = 6; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && len < 6) + len = 6; +#endif /* CONFIG_MBO */ +#ifdef CONFIG_FILS + if ((!(hapd->conf->wpa & WPA_PROTO_RSN) || + !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10) + len = 10; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_SAE + if (len < 11 && hapd->conf->wpa && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + hostapd_sae_pw_id_in_use(hapd->conf)) + len = 11; +#endif /* CONFIG_SAE */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0) @@ -413,7 +614,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); @@ -484,7 +685,7 @@ { u8 *pos = eid; -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (hapd->conf->ap_max_inactivity > 0) { unsigned int val; *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; @@ -502,7 +703,300 @@ pos += 2; *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ } -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ return pos; } + + +#ifdef CONFIG_MBO + +u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, + size_t len, int delta) +{ + u8 mbo[4]; + + mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT; + mbo[1] = 2; + /* Delta RSSI */ + mbo[2] = delta; + /* Retry delay */ + mbo[3] = hapd->iconf->rssi_reject_assoc_timeout; + + return eid + mbo_add_ie(eid, len, mbo, 4); +} + + +u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) +{ + u8 mbo[9], *mbo_pos = mbo; + u8 *pos = eid; + + if (!hapd->conf->mbo_enabled && + !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) + return eid; + + if (hapd->conf->mbo_enabled) { + *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; + *mbo_pos++ = 1; + /* Not Cellular aware */ + *mbo_pos++ = 0; + } + + if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { + *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; + *mbo_pos++ = 1; + *mbo_pos++ = hapd->mbo_assoc_disallow; + } + + if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { + u8 ctrl; + + ctrl = OCE_RELEASE; + if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) + ctrl |= OCE_IS_STA_CFON; + + *mbo_pos++ = OCE_ATTR_ID_CAPA_IND; + *mbo_pos++ = 1; + *mbo_pos++ = ctrl; + } + + pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); + + return pos; +} + + +u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) +{ + u8 len; + + if (!hapd->conf->mbo_enabled && + !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) + return 0; + + /* + * MBO IE header (6) + Capability Indication attribute (3) + + * Association Disallowed attribute (3) = 12 + */ + len = 6; + if (hapd->conf->mbo_enabled) + len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0); + + /* OCE capability indication attribute (3) */ + if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) + len += 3; + + return len; +} + +#endif /* CONFIG_MBO */ + + +#ifdef CONFIG_OWE +static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd) +{ + return hapd->conf->owe_transition_ssid_len > 0 && + !is_zero_ether_addr(hapd->conf->owe_transition_bssid); +} +#endif /* CONFIG_OWE */ + + +size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd) +{ +#ifdef CONFIG_OWE + if (!hostapd_eid_owe_trans_enabled(hapd)) + return 0; + return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len; +#else /* CONFIG_OWE */ + return 0; +#endif /* CONFIG_OWE */ +} + + +u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ +#ifdef CONFIG_OWE + u8 *pos = eid; + size_t elen; + + if (hapd->conf->owe_transition_ifname[0] && + !hostapd_eid_owe_trans_enabled(hapd)) + hostapd_owe_trans_get_info(hapd); + + if (!hostapd_eid_owe_trans_enabled(hapd)) + return pos; + + elen = hostapd_eid_owe_trans_len(hapd); + if (len < elen) { + wpa_printf(MSG_DEBUG, + "OWE: Not enough room in the buffer for OWE IE"); + return pos; + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = elen - 2; + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = OWE_OUI_TYPE; + os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN); + pos += ETH_ALEN; + *pos++ = hapd->conf->owe_transition_ssid_len; + os_memcpy(pos, hapd->conf->owe_transition_ssid, + hapd->conf->owe_transition_ssid_len); + pos += hapd->conf->owe_transition_ssid_len; + + return pos; +#else /* CONFIG_OWE */ + return eid; +#endif /* CONFIG_OWE */ +} + + +void ap_copy_sta_supp_op_classes(struct sta_info *sta, + const u8 *supp_op_classes, + size_t supp_op_classes_len) +{ + if (!supp_op_classes) + return; + os_free(sta->supp_op_classes); + sta->supp_op_classes = os_malloc(1 + supp_op_classes_len); + if (!sta->supp_op_classes) + return; + + sta->supp_op_classes[0] = supp_op_classes_len; + os_memcpy(sta->supp_op_classes + 1, supp_op_classes, + supp_op_classes_len); +} + + +u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) +{ + u8 *pos = eid; +#ifdef CONFIG_FILS + u8 *len; + u16 fils_info = 0; + size_t realms; + struct fils_realm *realm; + + if (!(hapd->conf->wpa & WPA_PROTO_RSN) || + !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) + return pos; + + realms = dl_list_len(&hapd->conf->fils_realms); + if (realms > 7) + realms = 7; /* 3 bit count field limits this to max 7 */ + + *pos++ = WLAN_EID_FILS_INDICATION; + len = pos++; + /* TODO: B0..B2: Number of Public Key Identifiers */ + if (hapd->conf->erp_domain) { + /* B3..B5: Number of Realm Identifiers */ + fils_info |= realms << 3; + } + /* TODO: B6: FILS IP Address Configuration */ + if (hapd->conf->fils_cache_id_set) + fils_info |= BIT(7); + if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) + fils_info |= BIT(8); /* HESSID Included */ + /* FILS Shared Key Authentication without PFS Supported */ + fils_info |= BIT(9); + if (hapd->conf->fils_dh_group) { + /* FILS Shared Key Authentication with PFS Supported */ + fils_info |= BIT(10); + } + /* TODO: B11: FILS Public Key Authentication Supported */ + /* B12..B15: Reserved */ + WPA_PUT_LE16(pos, fils_info); + pos += 2; + if (hapd->conf->fils_cache_id_set) { + os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN); + pos += FILS_CACHE_ID_LEN; + } + if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) { + os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); + pos += ETH_ALEN; + } + + dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm, + list) { + if (realms == 0) + break; + realms--; + os_memcpy(pos, realm->hash, 2); + pos += 2; + } + *len = pos - len - 1; +#endif /* CONFIG_FILS */ + + return pos; +} + + +#ifdef CONFIG_OCV +int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx) +{ + int ht_40mhz = 0; + int vht_80p80 = 0; + int requested_bw; + + if (sta->ht_capabilities) + ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); + + if (sta->vht_operation) { + struct ieee80211_vht_operation *oper = sta->vht_operation; + + /* + * If a VHT Operation element was present, use it to determine + * the supported channel bandwidth. + */ + if (oper->vht_op_info_chwidth == 0) { + requested_bw = ht_40mhz ? 40 : 20; + } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) { + requested_bw = 80; + } else { + int diff; + + requested_bw = 160; + diff = abs((int) + oper->vht_op_info_chan_center_freq_seg0_idx - + (int) + oper->vht_op_info_chan_center_freq_seg1_idx); + vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx + != 0 && diff > 16; + } + } else if (sta->vht_capabilities) { + struct ieee80211_vht_capabilities *capab; + int vht_chanwidth; + + capab = sta->vht_capabilities; + + /* + * If only the VHT Capabilities element is present (e.g., for + * normal clients), use it to determine the supported channel + * bandwidth. + */ + vht_chanwidth = capab->vht_capabilities_info & + VHT_CAP_SUPP_CHAN_WIDTH_MASK; + vht_80p80 = capab->vht_capabilities_info & + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + + /* TODO: Also take into account Extended NSS BW Support field */ + requested_bw = vht_chanwidth ? 160 : 80; + } else { + requested_bw = ht_40mhz ? 40 : 20; + } + + *bandwidth = requested_bw < ap_max_chanwidth ? + requested_bw : ap_max_chanwidth; + + *seg1_idx = 0; + if (ap_seg1_idx && vht_80p80) + *seg1_idx = ap_seg1_idx; + + return 0; +} +#endif /* CONFIG_OCV */ --- contrib/wpa/src/ap/ieee802_11_vht.c.orig +++ contrib/wpa/src/ap/ieee802_11_vht.c @@ -17,9 +17,10 @@ #include "sta_info.h" #include "beacon.h" #include "ieee802_11.h" +#include "dfs.h" -u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts) { struct ieee80211_vht_capabilities *cap; struct hostapd_hw_modes *mode = hapd->iface->current_mode; @@ -49,6 +50,18 @@ cap->vht_capabilities_info = host_to_le32( hapd->iface->conf->vht_capab); + if (nsts != 0) { + u32 hapd_nsts; + + hapd_nsts = le_to_host32(cap->vht_capabilities_info); + hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + cap->vht_capabilities_info &= + ~(host_to_le32(hapd_nsts << + VHT_CAP_BEAMFORMEE_STS_OFFSET)); + cap->vht_capabilities_info |= + host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET); + } + /* Supported MCS set comes from hw */ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); @@ -80,6 +93,26 @@ hapd->iconf->vht_oper_centr_freq_seg1_idx; oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; + if (hapd->iconf->vht_oper_chwidth == 2) { + /* + * Convert 160 MHz channel width to new style as interop + * workaround. + */ + oper->vht_op_info_chwidth = 1; + oper->vht_op_info_chan_center_freq_seg1_idx = + oper->vht_op_info_chan_center_freq_seg0_idx; + if (hapd->iconf->channel < + hapd->iconf->vht_oper_centr_freq_seg0_idx) + oper->vht_op_info_chan_center_freq_seg0_idx -= 8; + else + oper->vht_op_info_chan_center_freq_seg0_idx += 8; + } else if (hapd->iconf->vht_oper_chwidth == 3) { + /* + * Convert 80+80 MHz channel width to new style as interop + * workaround. + */ + oper->vht_op_info_chwidth = 1; + } /* VHT Basic MCS set comes from hw */ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ @@ -131,12 +164,177 @@ } +u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) +{ + u8 bw, chan1, chan2 = 0; + int freq1; + + if (!hapd->cs_freq_params.channel || + !hapd->cs_freq_params.vht_enabled) + return eid; + + /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */ + switch (hapd->cs_freq_params.bandwidth) { + case 40: + bw = 0; + break; + case 80: + /* check if it's 80+80 */ + if (!hapd->cs_freq_params.center_freq2) + bw = 1; + else + bw = 3; + break; + case 160: + bw = 2; + break; + default: + /* not valid VHT bandwidth or not in CSA */ + return eid; + } + + freq1 = hapd->cs_freq_params.center_freq1 ? + hapd->cs_freq_params.center_freq1 : + hapd->cs_freq_params.freq; + if (ieee80211_freq_to_chan(freq1, &chan1) != + HOSTAPD_MODE_IEEE80211A) + return eid; + + if (hapd->cs_freq_params.center_freq2 && + ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2, + &chan2) != HOSTAPD_MODE_IEEE80211A) + return eid; + + *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER; + *eid++ = 5; /* Length of Channel Switch Wrapper */ + *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH; + *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */ + *eid++ = bw; /* New Channel Width */ + *eid++ = chan1; /* New Channel Center Frequency Segment 0 */ + *eid++ = chan2; /* New Channel Center Frequency Segment 1 */ + + return eid; +} + + +u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) +{ + struct hostapd_iface *iface = hapd->iface; + struct hostapd_config *iconf = iface->conf; + struct hostapd_hw_modes *mode = iface->current_mode; + struct hostapd_channel_data *chan; + int dfs, i; + u8 channel, tx_pwr_count, local_pwr_constraint; + int max_tx_power; + u8 tx_pwr; + + if (!mode) + return eid; + + if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES) + return eid; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].freq == iface->freq) + break; + } + if (i == mode->num_channels) + return eid; + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (iconf->secondary_channel == 0) { + /* Max Transmit Power count = 0 (20 MHz) */ + tx_pwr_count = 0; + } else { + /* Max Transmit Power count = 1 (20, 40 MHz) */ + tx_pwr_count = 1; + } + break; + case VHT_CHANWIDTH_80MHZ: + /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ + tx_pwr_count = 2; + break; + case VHT_CHANWIDTH_80P80MHZ: + case VHT_CHANWIDTH_160MHZ: + /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ + tx_pwr_count = 3; + break; + default: + return eid; + } + + /* + * Below local_pwr_constraint logic is referred from + * hostapd_eid_pwr_constraint. + * + * Check if DFS is required by regulatory. + */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) + dfs = 0; + + /* + * In order to meet regulations when TPC is not implemented using + * a transmit power that is below the legal maximum (including any + * mitigation factor) should help. In this case, indicate 3 dB below + * maximum allowed transmit power. + */ + if (hapd->iconf->local_pwr_constraint == -1) + local_pwr_constraint = (dfs == 0) ? 0 : 3; + else + local_pwr_constraint = hapd->iconf->local_pwr_constraint; + + /* + * A STA that is not an AP shall use a transmit power less than or + * equal to the local maximum transmit power level for the channel. + * The local maximum transmit power can be calculated from the formula: + * local max TX pwr = max TX pwr - local pwr constraint + * Where max TX pwr is maximum transmit power level specified for + * channel in Country element and local pwr constraint is specified + * for channel in this Power Constraint element. + */ + chan = &mode->channels[i]; + max_tx_power = chan->max_tx_power - local_pwr_constraint; + + /* + * Local Maximum Transmit power is encoded as two's complement + * with a 0.5 dB step. + */ + max_tx_power *= 2; /* in 0.5 dB steps */ + if (max_tx_power > 127) { + /* 63.5 has special meaning of 63.5 dBm or higher */ + max_tx_power = 127; + } + if (max_tx_power < -128) + max_tx_power = -128; + if (max_tx_power < 0) + tx_pwr = 0x80 + max_tx_power + 128; + else + tx_pwr = max_tx_power; + + *eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE; + *eid++ = 2 + tx_pwr_count; + + /* + * Max Transmit Power count and + * Max Transmit Power units = 0 (EIRP) + */ + *eid++ = tx_pwr_count; + + for (i = 0; i <= tx_pwr_count; i++) + *eid++ = tx_pwr; + + return eid; +} + + u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab) { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || - hapd->conf->disable_11ac || + !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac || !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { sta->flags &= ~WLAN_STA_VHT; os_free(sta->vht_capabilities); @@ -159,6 +357,29 @@ } +u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_oper) +{ + if (!vht_oper) { + os_free(sta->vht_operation); + sta->vht_operation = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (!sta->vht_operation) { + sta->vht_operation = + os_zalloc(sizeof(struct ieee80211_vht_operation)); + if (!sta->vht_operation) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + os_memcpy(sta->vht_operation, vht_oper, + sizeof(struct ieee80211_vht_operation)); + + return WLAN_STATUS_SUCCESS; +} + + u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ie, size_t len) { @@ -212,7 +433,7 @@ WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); pos += 4; *pos++ = VENDOR_VHT_SUBTYPE; - pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); return pos; --- contrib/wpa/src/ap/ieee802_1x.c.orig +++ contrib/wpa/src/ap/ieee802_1x.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -31,9 +31,14 @@ #include "ap_drv_ops.h" #include "wps_hostapd.h" #include "hs20.h" +/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */ +#include "ieee802_11.h" #include "ieee802_1x.h" +#ifdef CONFIG_HS20 +static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx); +#endif /* CONFIG_HS20 */ static void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success, int remediation); @@ -219,7 +224,7 @@ MAC2STR(sta->addr)); #ifndef CONFIG_NO_VLAN - if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { + if (sta->vlan_id > 0) { wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); return; } @@ -313,6 +318,7 @@ hdr->code != EAP_CODE_INITIATE)) return; + eap_erp_update_identity(sm->eap, eap, len); identity = eap_get_identity(sm->eap, &identity_len); if (identity == NULL) return; @@ -402,7 +408,16 @@ char buf[128]; if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_SERVICE_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE, + RADIUS_SERVICE_TYPE_FRAMED)) { + wpa_printf(MSG_ERROR, "Could not add Service-Type"); + return -1; + } + + if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_PORT) && + sta->aid > 0 && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { wpa_printf(MSG_ERROR, "Could not add NAS-Port"); return -1; @@ -435,9 +450,9 @@ return -1; } - if (sta->acct_session_id_hi || sta->acct_session_id_lo) { - os_snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); + if (sta->acct_session_id) { + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) sta->acct_session_id); if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); @@ -445,7 +460,22 @@ } } -#ifdef CONFIG_IEEE80211R + if ((hapd->conf->wpa & 2) && + !hapd->conf->disable_pmksa_caching && + sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) { + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) + sta->eapol_sm->acct_multi_session_id); + if (!radius_msg_add_attr( + msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_INFO, + "Could not add Acct-Multi-Session-Id"); + return -1; + } + } + +#ifdef CONFIG_IEEE80211R_AP if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm && (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) || @@ -458,7 +488,7 @@ wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id"); return -1; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) @@ -475,6 +505,7 @@ { char buf[128]; struct hostapd_radius_attr *attr; + int len; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IP_ADDRESS) && @@ -506,15 +537,15 @@ return -1; } - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), - wpa_ssid_txt(hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len)); - buf[sizeof(buf) - 1] = '\0'; + len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":", + MAC2STR(hapd->own_addr)); + os_memcpy(&buf[len], hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + len += hapd->conf->ssid.ssid_len; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_CALLED_STATION_ID) && !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { + (u8 *) buf, len)) { wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); return -1; } @@ -560,9 +591,9 @@ } -static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, - struct sta_info *sta, - const u8 *eap, size_t len) +void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len) { struct radius_msg *msg; struct eapol_state_machine *sm = sta->eapol_sm; @@ -583,7 +614,10 @@ return; } - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + if (radius_msg_make_authenticator(msg) < 0) { + wpa_printf(MSG_INFO, "Could not make Request Authenticator"); + goto fail; + } if (sm->identity && !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, @@ -648,7 +682,8 @@ #ifdef CONFIG_HS20 if (hapd->conf->hs20) { - u8 ver = 1; /* Release 2 */ + u8 ver = hapd->conf->hs20_release - 1; + if (!radius_msg_add_wfa( msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION, &ver, 1)) { @@ -678,6 +713,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 */ @@ -814,7 +884,7 @@ } -static struct eapol_state_machine * +struct eapol_state_machine * ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) { int flags = 0; @@ -831,6 +901,29 @@ } +static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf, + size_t len) +{ + if (sta->pending_eapol_rx) { + wpabuf_free(sta->pending_eapol_rx->buf); + } else { + sta->pending_eapol_rx = + os_malloc(sizeof(*sta->pending_eapol_rx)); + if (!sta->pending_eapol_rx) + return; + } + + sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len); + if (!sta->pending_eapol_rx->buf) { + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + return; + } + + os_get_reltime(&sta->pending_eapol_rx->rx_time); +} + + /** * ieee802_1x_receive - Process the EAPOL frames from the Supplicant * @hapd: hostapd BSS data @@ -861,6 +954,13 @@ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " "associated/Pre-authenticating STA"); + + if (sta && (sta->flags & WLAN_STA_AUTH)) { + wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR + " for later use", MAC2STR(sta->addr)); + ieee802_1x_save_eapol(sta, buf, len); + } + return; } @@ -909,7 +1009,9 @@ } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); - if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + if (key_mgmt != -1 && + (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || + key_mgmt == WPA_KEY_MGMT_DPP)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " "STA is using PSK"); return; @@ -1047,18 +1149,20 @@ * Clear any possible EAPOL authenticator state to support * reassociation change from WPS to PSK. */ - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); return; } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); - if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + if (key_mgmt != -1 && + (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || + key_mgmt == WPA_KEY_MGMT_DPP)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); /* * Clear any possible EAPOL authenticator state to support * reassociation change from WPA-EAP to PSK. */ - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); return; } @@ -1093,7 +1197,7 @@ sta->eapol_sm->eap_if->portEnabled = TRUE; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, @@ -1106,13 +1210,37 @@ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; sta->eapol_sm->authFail = FALSE; + sta->eapol_sm->portValid = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - /* TODO: get vlan_id from R0KH using RRB message */ + ap_sta_bind_vlan(hapd, sta); return; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from FILS - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing FILS information. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; + sta->eapol_sm->portValid = TRUE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + wpa_auth_set_ptk_rekey_timer(sta->wpa_sm); + return; + } +#endif /* CONFIG_FILS */ + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -1128,7 +1256,7 @@ sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); + pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm); ap_sta_bind_vlan(hapd, sta); } else { if (reassoc) { @@ -1144,10 +1272,20 @@ } -void ieee802_1x_free_station(struct sta_info *sta) +void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; +#ifdef CONFIG_HS20 + eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); +#endif /* CONFIG_HS20 */ + + if (sta->pending_eapol_rx) { + wpabuf_free(sta->pending_eapol_rx->buf); + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + } + if (sm == NULL) return; @@ -1156,10 +1294,8 @@ #ifndef CONFIG_NO_RADIUS radius_msg_free(sm->last_recv_radius); radius_free_class(&sm->radius_class); - wpabuf_free(sm->radius_cui); #endif /* CONFIG_NO_RADIUS */ - os_free(sm->identity); eapol_auth_free(sm); } @@ -1325,11 +1461,10 @@ } } while (class_len < 1); - nclass[nclass_count].data = os_malloc(class_len); + nclass[nclass_count].data = os_memdup(attr_class, class_len); if (nclass[nclass_count].data == NULL) break; - os_memcpy(nclass[nclass_count].data, attr_class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } @@ -1489,6 +1624,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 */ @@ -1536,6 +1698,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 */ @@ -1575,6 +1743,45 @@ } +#ifndef CONFIG_NO_VLAN +static int ieee802_1x_update_vlan(struct radius_msg *msg, + struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct vlan_description vlan_desc; + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged, + MAX_NUM_TAGGED_VLAN, + vlan_desc.tagged); + + if (vlan_desc.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from RADIUS server", + vlan_desc.untagged, + vlan_desc.tagged[0] ? "+" : ""); + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + ap_sta_set_vlan(hapd, sta, &vlan_desc); + return -1; + } + + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && + !vlan_desc.notempty) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "authentication server did not include required VLAN ID in Access-Accept"); + return -1; + } + + return ap_sta_set_vlan(hapd, sta, &vlan_desc); +} +#endif /* CONFIG_NO_VLAN */ + + /** * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server * @msg: RADIUS response message @@ -1592,7 +1799,8 @@ struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set, vlan_id = 0; + 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); @@ -1659,51 +1867,35 @@ switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - vlan_id = 0; #ifndef CONFIG_NO_VLAN - else - vlan_id = radius_msg_get_vlanid(msg); - if (vlan_id > 0 && - hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + ieee802_1x_update_vlan(msg, hapd, sta) < 0) + break; + + if (sta->vlan_id > 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "VLAN ID %d", vlan_id); - } else if (vlan_id > 0) { - sta->eapol_sm->authFail = TRUE; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "Invalid VLAN ID %d received from RADIUS server", - vlan_id); - break; - } else if (hapd->conf->ssid.dynamic_vlan == - DYNAMIC_VLAN_REQUIRED) { - sta->eapol_sm->authFail = TRUE; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, "authentication " - "server did not include required VLAN " - "ID in Access-Accept"); - break; + "VLAN ID %d", sta->vlan_id); } -#endif /* CONFIG_NO_VLAN */ - sta->vlan_id = vlan_id; if ((sta->flags & WLAN_STA_ASSOC) && ap_sta_bind_vlan(hapd, sta) < 0) break; +#endif /* CONFIG_NO_VLAN */ 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; @@ -1715,19 +1907,17 @@ ieee802_1x_check_hs20(hapd, sta, msg, session_timeout_set ? (int) session_timeout : -1); - if (sm->eap_if->eapKeyAvailable && !sta->remediation && - !sta->hs20_deauth_requested && - wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, - session_timeout_set ? - (int) session_timeout : -1, sm) == 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "Added PMKSA cache entry"); - } break; 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; @@ -1754,6 +1944,19 @@ if (override_eapReq) sm->eap_if->aaaEapReq = FALSE; +#ifdef CONFIG_FILS +#ifdef NEED_AP_MLME + if (sta->flags & WLAN_STA_PENDING_FILS_ERP) { + /* TODO: Add a PMKSA entry on success? */ + ieee802_11_finish_fils_auth( + hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT, + sm->eap_if->aaaEapReqData, + sm->eap_if->aaaEapKeyData, + sm->eap_if->aaaEapKeyDataLen); + } +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_FILS */ + eapol_auth_step(sm); return RADIUS_RX_QUEUED; @@ -1841,7 +2044,7 @@ wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", eapol->default_wep_key_idx); - + if (ieee802_1x_rekey_broadcast(hapd)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to generate a " @@ -1951,13 +2154,19 @@ } if (eap_user->password) { - user->password = os_malloc(eap_user->password_len); + user->password = os_memdup(eap_user->password, + eap_user->password_len); if (user->password == NULL) goto out; - os_memcpy(user->password, eap_user->password, - eap_user->password_len); user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; + if (eap_user->salt && eap_user->salt_len) { + user->salt = os_memdup(eap_user->salt, + eap_user->salt_len); + if (!user->salt) + goto out; + user->salt_len = eap_user->salt_len; + } } user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; @@ -2107,6 +2316,7 @@ conf.erp_domain = hapd->conf->erp_domain; conf.erp = hapd->conf->eap_server_erp; conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + conf.tls_flags = hapd->conf->tls_flags; conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; @@ -2190,7 +2400,7 @@ { eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); - if (hapd->driver != NULL && + if (hapd->driver && hapd->drv_priv && (hapd->conf->ieee802_1x || hapd->conf->wpa)) hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); @@ -2243,6 +2453,16 @@ MAC2STR(sta->addr), xhdr->version, xhdr->type, be_to_host16(xhdr->length), ack); +#ifdef CONFIG_WPS + if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack && + (sta->flags & WLAN_STA_WPS) && + ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) { + wpa_printf(MSG_DEBUG, + "WPS: Indicate EAP completion on ACK for EAP-Failure"); + hostapd_wps_eap_completed(hapd); + } +#endif /* CONFIG_WPS */ + if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) return 0; @@ -2375,6 +2595,7 @@ struct os_reltime diff; const char *name1; const char *name2; + char *identity_buf = NULL; if (sm == NULL) return 0; @@ -2490,31 +2711,41 @@ /* dot1xAuthSessionStatsTable */ os_reltime_age(&sta->acct_session_start, &diff); + if (sm->eap && !sm->identity) { + const u8 *id; + size_t id_len; + + id = eap_get_identity(sm->eap, &id_len); + if (id) + identity_buf = dup_binstr(id, id_len); + } ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ /* TODO: dot1xAuthSessionFramesRx */ /* TODO: dot1xAuthSessionFramesTx */ - "dot1xAuthSessionId=%08X-%08X\n" + "dot1xAuthSessionId=%016llX\n" "dot1xAuthSessionAuthenticMethod=%d\n" "dot1xAuthSessionTime=%u\n" "dot1xAuthSessionTerminateCause=999\n" "dot1xAuthSessionUserName=%s\n", - sta->acct_session_id_hi, sta->acct_session_id_lo, + (unsigned long long) sta->acct_session_id, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, (unsigned int) diff.sec, - sm->identity); + sm->identity ? (char *) sm->identity : + (identity_buf ? identity_buf : "N/A")); + os_free(identity_buf); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; - if (sm->acct_multi_session_id_hi) { + if (sm->acct_multi_session_id) { ret = os_snprintf(buf + len, buflen - len, - "authMultiSessionId=%08X+%08X\n", - sm->acct_multi_session_id_hi, - sm->acct_multi_session_id_lo); + "authMultiSessionId=%016llX\n", + (unsigned long long) + sm->acct_multi_session_id); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2535,6 +2766,43 @@ } +#ifdef CONFIG_HS20 +static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (sta->remediation) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate Subscription Remediation", + MAC2STR(sta->addr)); + hs20_send_wnm_notification(hapd, sta->addr, + sta->remediation_method, + sta->remediation_url); + os_free(sta->remediation_url); + sta->remediation_url = NULL; + } + + if (sta->hs20_deauth_req) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate imminent deauthentication", + MAC2STR(sta->addr)); + 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 */ + + static void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success, int remediation) @@ -2544,6 +2812,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) { @@ -2554,37 +2823,27 @@ sta->remediation_method = 1; /* SOAP-XML SPP */ } - if (success) { - if (sta->remediation) { - wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " - "to " MACSTR " to indicate Subscription " - "Remediation", - MAC2STR(sta->addr)); - hs20_send_wnm_notification(hapd, sta->addr, - sta->remediation_method, - sta->remediation_url); - os_free(sta->remediation_url); - sta->remediation_url = NULL; - } - - if (sta->hs20_deauth_req) { - wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " - "to " MACSTR " to indicate imminent " - "deauthentication", MAC2STR(sta->addr)); - hs20_send_wnm_notification_deauth_req( - hapd, sta->addr, 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); + eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send, + hapd, sta); } #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, session_timeout, + wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout, sta->eapol_sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, @@ -2602,15 +2861,6 @@ * EAP-FAST with anonymous provisioning, may require another * EAPOL authentication to be started to complete connection. */ - wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force " - "disconnection after EAP-Failure"); - /* Add a small sleep to increase likelihood of previously - * requested EAP-Failure TX getting out before this should the - * driver reorder operations. - */ - os_sleep(0, 10000); - ap_sta_disconnect(hapd, sta, sta->addr, - WLAN_REASON_IEEE_802_1X_AUTH_FAILED); - hostapd_wps_eap_completed(hapd); + ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta); } } --- contrib/wpa/src/ap/ieee802_1x.h.orig +++ contrib/wpa/src/ap/ieee802_1x.h @@ -21,7 +21,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len); void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); -void ieee802_1x_free_station(struct sta_info *sta); +void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, @@ -57,5 +57,10 @@ struct hostapd_radius_attr *req_attr, struct sta_info *sta, struct radius_msg *msg); +void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len); +struct eapol_state_machine * +ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta); #endif /* IEEE802_1X_H */ --- contrib/wpa/src/ap/mbo_ap.c.orig +++ contrib/wpa/src/ap/mbo_ap.c @@ -0,0 +1,244 @@ +/* + * hostapd - MBO + * Copyright (c) 2016, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "hostapd.h" +#include "sta_info.h" +#include "mbo_ap.h" + + +void mbo_ap_sta_free(struct sta_info *sta) +{ + struct mbo_non_pref_chan_info *info, *prev; + + info = sta->non_pref_chan; + sta->non_pref_chan = NULL; + while (info) { + prev = info; + info = info->next; + os_free(prev); + } +} + + +static void mbo_ap_parse_non_pref_chan(struct sta_info *sta, + const u8 *buf, size_t len) +{ + struct mbo_non_pref_chan_info *info, *tmp; + char channels[200], *pos, *end; + size_t num_chan, i; + int ret; + + if (len <= 3) + return; /* Not enough room for any channels */ + + num_chan = len - 3; + info = os_zalloc(sizeof(*info) + num_chan); + if (!info) + return; + info->op_class = buf[0]; + info->pref = buf[len - 2]; + info->reason_code = buf[len - 1]; + info->num_channels = num_chan; + buf++; + os_memcpy(info->channels, buf, num_chan); + if (!sta->non_pref_chan) { + sta->non_pref_chan = info; + } else { + tmp = sta->non_pref_chan; + while (tmp->next) + tmp = tmp->next; + tmp->next = info; + } + + pos = channels; + end = pos + sizeof(channels); + *pos = '\0'; + for (i = 0; i < num_chan; i++) { + ret = os_snprintf(pos, end - pos, "%s%u", + i == 0 ? "" : " ", buf[i]); + if (os_snprintf_error(end - pos, ret)) { + *pos = '\0'; + break; + } + pos += ret; + } + + wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR + " non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)", + MAC2STR(sta->addr), info->op_class, info->pref, + info->reason_code, channels); +} + + +void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta, + struct ieee802_11_elems *elems) +{ + const u8 *pos, *attr, *end; + size_t len; + + if (!hapd->conf->mbo_enabled || !elems->mbo) + return; + + pos = elems->mbo + 4; + len = elems->mbo_len - 4; + wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len); + + attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA); + if (attr && attr[1] >= 1) + sta->cell_capa = attr[2]; + + mbo_ap_sta_free(sta); + end = pos + len; + while (end - pos > 1) { + u8 ie_len = pos[1]; + + if (2 + ie_len > end - pos) + break; + + if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT) + mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len); + pos += 2 + pos[1]; + } +} + + +int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen) +{ + char *pos = buf, *end = buf + buflen; + int ret; + struct mbo_non_pref_chan_info *info; + u8 i; + unsigned int count = 0; + + if (!sta->cell_capa) + return 0; + + ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + for (info = sta->non_pref_chan; info; info = info->next) { + char *pos2 = pos; + + ret = os_snprintf(pos2, end - pos2, + "non_pref_chan[%u]=%u:%u:%u:", + count, info->op_class, info->pref, + info->reason_code); + count++; + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + + for (i = 0; i < info->num_channels; i++) { + ret = os_snprintf(pos2, end - pos2, "%u%s", + info->channels[i], + i + 1 < info->num_channels ? + "," : ""); + if (os_snprintf_error(end - pos2, ret)) { + pos2 = NULL; + break; + } + pos2 += ret; + } + + if (!pos2) + break; + ret = os_snprintf(pos2, end - pos2, "\n"); + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + pos = pos2; + } + + return pos - buf; +} + + +static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta, + const u8 *buf, size_t len) +{ + if (len < 1) + return; + wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR + " updated cellular data capability: %u", + MAC2STR(sta->addr), buf[0]); + sta->cell_capa = buf[0]; +} + + +static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type, + const u8 *buf, size_t len, + int *first_non_pref_chan) +{ + switch (type) { + case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT: + if (*first_non_pref_chan) { + /* + * Need to free the previously stored entries now to + * allow the update to replace all entries. + */ + *first_non_pref_chan = 0; + mbo_ap_sta_free(sta); + } + mbo_ap_parse_non_pref_chan(sta, buf, len); + break; + case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA: + mbo_ap_wnm_notif_req_cell_capa(sta, buf, len); + break; + default: + wpa_printf(MSG_DEBUG, + "MBO: Ignore unknown WNM Notification WFA subelement %u", + type); + break; + } +} + + +void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + u8 ie_len; + struct sta_info *sta; + int first_non_pref_chan = 1; + + if (!hapd->conf->mbo_enabled) + return; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return; + + pos = buf; + end = buf + len; + + while (end - pos > 1) { + ie_len = pos[1]; + + if (2 + ie_len > end - pos) + break; + + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA) + mbo_ap_wnm_notif_req_elem(sta, pos[5], + pos + 6, ie_len - 4, + &first_non_pref_chan); + else + wpa_printf(MSG_DEBUG, + "MBO: Ignore unknown WNM Notification element %u (len=%u)", + pos[0], pos[1]); + + pos += 2 + pos[1]; + } +} --- contrib/wpa/src/ap/mbo_ap.h.orig +++ contrib/wpa/src/ap/mbo_ap.h @@ -0,0 +1,51 @@ +/* + * MBO related functions and structures + * Copyright (c) 2016, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MBO_AP_H +#define MBO_AP_H + +struct hostapd_data; +struct sta_info; +struct ieee802_11_elems; + +#ifdef CONFIG_MBO + +void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta, + struct ieee802_11_elems *elems); +int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen); +void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len); +void mbo_ap_sta_free(struct sta_info *sta); + +#else /* CONFIG_MBO */ + +static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, + struct sta_info *sta, + struct ieee802_11_elems *elems) +{ +} + +static inline int mbo_ap_get_info(struct sta_info *sta, char *buf, + size_t buflen) +{ + return 0; +} + +static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, + const u8 *addr, + const u8 *buf, size_t len) +{ +} + +static inline void mbo_ap_sta_free(struct sta_info *sta) +{ +} + +#endif /* CONFIG_MBO */ + +#endif /* MBO_AP_H */ --- contrib/wpa/src/ap/ndisc_snoop.c.orig +++ contrib/wpa/src/ap/ndisc_snoop.c @@ -17,6 +17,7 @@ #include "ap_drv_ops.h" #include "list.h" #include "x_snoop.h" +#include "ndisc_snoop.h" struct ip6addr { struct in6_addr addr; @@ -181,4 +182,5 @@ void ndisc_snoop_deinit(struct hostapd_data *hapd) { l2_packet_deinit(hapd->sock_ndisc); + hapd->sock_ndisc = NULL; } --- contrib/wpa/src/ap/neighbor_db.c.orig +++ contrib/wpa/src/ap/neighbor_db.c @@ -0,0 +1,257 @@ +/* + * hostapd / Neighboring APs DB + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "neighbor_db.h" + + +struct hostapd_neighbor_entry * +hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid) +{ + struct hostapd_neighbor_entry *nr; + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 && + (!ssid || + (ssid->ssid_len == nr->ssid.ssid_len && + os_memcmp(ssid->ssid, nr->ssid.ssid, + ssid->ssid_len) == 0))) + return nr; + } + return NULL; +} + + +static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr) +{ + wpabuf_free(nr->nr); + nr->nr = NULL; + wpabuf_free(nr->lci); + nr->lci = NULL; + wpabuf_free(nr->civic); + nr->civic = NULL; + os_memset(nr->bssid, 0, sizeof(nr->bssid)); + os_memset(&nr->ssid, 0, sizeof(nr->ssid)); + nr->stationary = 0; +} + + +static struct hostapd_neighbor_entry * +hostapd_neighbor_add(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr; + + nr = os_zalloc(sizeof(struct hostapd_neighbor_entry)); + if (!nr) + return NULL; + + dl_list_add(&hapd->nr_db, &nr->list); + + return nr; +} + + +int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid, + const struct wpabuf *nr, const struct wpabuf *lci, + const struct wpabuf *civic, int stationary) +{ + struct hostapd_neighbor_entry *entry; + + entry = hostapd_neighbor_get(hapd, bssid, ssid); + if (!entry) + entry = hostapd_neighbor_add(hapd); + if (!entry) + return -1; + + hostapd_neighbor_clear_entry(entry); + + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid)); + + entry->nr = wpabuf_dup(nr); + if (!entry->nr) + goto fail; + + if (lci && wpabuf_len(lci)) { + entry->lci = wpabuf_dup(lci); + if (!entry->lci || os_get_time(&entry->lci_date)) + goto fail; + } + + if (civic && wpabuf_len(civic)) { + entry->civic = wpabuf_dup(civic); + if (!entry->civic) + goto fail; + } + + entry->stationary = stationary; + + return 0; + +fail: + hostapd_neighbor_remove(hapd, bssid, ssid); + return -1; +} + + +int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid) +{ + struct hostapd_neighbor_entry *nr; + + nr = hostapd_neighbor_get(hapd, bssid, ssid); + if (!nr) + return -1; + + hostapd_neighbor_clear_entry(nr); + dl_list_del(&nr->list); + os_free(nr); + + return 0; +} + + +void hostapd_free_neighbor_db(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr, *prev; + + dl_list_for_each_safe(nr, prev, &hapd->nr_db, + struct hostapd_neighbor_entry, list) { + hostapd_neighbor_clear_entry(nr); + dl_list_del(&nr->list); + os_free(nr); + } +} + + +#ifdef NEED_AP_MLME +static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, + int ht, int vht) +{ + if (!ht && !vht) + return NR_CHAN_WIDTH_20; + if (!hapd->iconf->secondary_channel) + return NR_CHAN_WIDTH_20; + if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) + return NR_CHAN_WIDTH_40; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + return NR_CHAN_WIDTH_80; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ) + return NR_CHAN_WIDTH_160; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) + return NR_CHAN_WIDTH_80P80; + return NR_CHAN_WIDTH_20; +} +#endif /* NEED_AP_MLME */ + + +void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) +{ +#ifdef NEED_AP_MLME + u16 capab = hostapd_own_capab_info(hapd); + int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; + int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; + struct wpa_ssid_value ssid; + u8 channel, op_class; + u8 center_freq1_idx = 0, center_freq2_idx = 0; + enum nr_chan_width width; + u32 bssid_info; + struct wpabuf *nr; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return; + + bssid_info = 3; /* AP is reachable */ + bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */ + bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */ + + if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) + bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; + + bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */ + + if (hapd->conf->wmm_enabled) { + bssid_info |= NEI_REP_BSSID_INFO_QOS; + + if (hapd->conf->wmm_uapsd && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) + bssid_info |= NEI_REP_BSSID_INFO_APSD; + } + + if (ht) { + bssid_info |= NEI_REP_BSSID_INFO_HT | + NEI_REP_BSSID_INFO_DELAYED_BA; + + /* VHT bit added in IEEE P802.11-REVmc/D4.3 */ + if (vht) + bssid_info |= NEI_REP_BSSID_INFO_VHT; + } + + /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ + + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return; + width = hostapd_get_nr_chan_width(hapd, ht, vht); + if (vht) { + center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; + if (width == NR_CHAN_WIDTH_80P80) + center_freq2_idx = + hapd->iconf->vht_oper_centr_freq_seg1_idx; + } else if (ht) { + ieee80211_freq_to_chan(hapd->iface->freq + + 10 * hapd->iconf->secondary_channel, + ¢er_freq1_idx); + } + + ssid.ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + + /* + * Neighbor Report element size = BSSID + BSSID info + op_class + chan + + * phy type + wide bandwidth channel subelement. + */ + nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5); + if (!nr) + return; + + wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN); + wpabuf_put_le32(nr, bssid_info); + wpabuf_put_u8(nr, op_class); + wpabuf_put_u8(nr, channel); + wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); + + /* + * Wide Bandwidth Channel subelement may be needed to allow the + * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0 + * Figure 9-301. + */ + wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); + wpabuf_put_u8(nr, 3); + wpabuf_put_u8(nr, width); + wpabuf_put_u8(nr, center_freq1_idx); + wpabuf_put_u8(nr, center_freq2_idx); + + hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, + hapd->iconf->civic, hapd->iconf->stationary_ap); + + wpabuf_free(nr); +#endif /* NEED_AP_MLME */ +} --- contrib/wpa/src/ap/neighbor_db.h.orig +++ contrib/wpa/src/ap/neighbor_db.h @@ -0,0 +1,25 @@ +/* + * hostapd / Neighboring APs DB + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NEIGHBOR_DB_H +#define NEIGHBOR_DB_H + +struct hostapd_neighbor_entry * +hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid); +int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid, + const struct wpabuf *nr, const struct wpabuf *lci, + const struct wpabuf *civic, int stationary); +void hostapd_neighbor_set_own_report(struct hostapd_data *hapd); +int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid); +void hostapd_free_neighbor_db(struct hostapd_data *hapd); + +#endif /* NEIGHBOR_DB_H */ --- contrib/wpa/src/ap/peerkey_auth.c.orig +++ contrib/wpa/src/ap/peerkey_auth.c @@ -1,396 +0,0 @@ -/* - * hostapd - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2009, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "utils/includes.h" - -#include "utils/common.h" -#include "utils/eloop.h" -#include "crypto/sha1.h" -#include "crypto/sha256.h" -#include "crypto/random.h" -#include "wpa_auth.h" -#include "wpa_auth_i.h" -#include "wpa_auth_ie.h" - -#ifdef CONFIG_PEERKEY - -static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) -{ -#if 0 - struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_stsl_negotiation *neg = timeout_ctx; -#endif - - /* TODO: ? */ -} - - -struct wpa_stsl_search { - const u8 *addr; - struct wpa_state_machine *sm; -}; - - -static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) -{ - struct wpa_stsl_search *search = ctx; - if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { - search->sm = sm; - return 1; - } - return 0; -} - - -static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, const u8 *peer, - u16 mui, u16 error_type) -{ - u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; - u8 *pos; - struct rsn_error_kde error; - - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK Error"); - - pos = kde; - - if (peer) { - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, - NULL, 0); - } - - error.mui = host_to_be16(mui); - error.error_type = host_to_be16(error_type); - pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, - (u8 *) &error, sizeof(error), NULL, 0); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, - NULL, NULL, kde, pos - kde, 0, 0, 0); -} - - -void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - u8 *buf, *pos; - size_t buf_len; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); - return; - } - - if (kde.rsn_ie == NULL || kde.mac_addr == NULL || - kde.mac_addr_len < ETH_ALEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " - "SMK M1"); - return; - } - - /* Initiator = sm->addr; Peer = kde.mac_addr */ - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR - " aborted - STA not associated anymore", - MAC2STR(kde.mac_addr)); - wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, - STK_ERR_STA_NR); - /* FIX: wpa_stsl_remove(wpa_auth, neg); */ - return; - } - - buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; - buf = os_malloc(buf_len); - if (buf == NULL) - return; - /* Initiator RSN IE */ - os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); - pos = buf + kde.rsn_ie_len; - /* Initiator MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, - NULL, 0); - - /* SMK M2: - * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) - */ - - wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, - "Sending SMK M2"); - - __wpa_send_eapol(wpa_auth, search.sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, - NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); - - os_free(buf); -} - - -static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key, - struct wpa_eapol_ie_parse *kde, - const u8 *smk) -{ - u8 *buf, *pos; - size_t buf_len; - u32 lifetime; - - /* SMK M4: - * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, - * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, - * Lifetime KDE) - */ - - buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - pos = buf = os_malloc(buf_len); - if (buf == NULL) - return; - - /* Initiator MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, - NULL, 0); - - /* Initiator Nonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, - NULL, 0); - - /* SMK with PNonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, - key->key_nonce, WPA_NONCE_LEN); - - /* Lifetime */ - lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime), NULL, 0); - - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK M4"); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, - NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); - - os_free(buf); -} - - -static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key, - struct wpa_eapol_ie_parse *kde, - const u8 *smk, const u8 *peer) -{ - u8 *buf, *pos; - size_t buf_len; - u32 lifetime; - - /* SMK M5: - * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, - * Lifetime KDE)) - */ - - buf_len = kde->rsn_ie_len + - 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - pos = buf = os_malloc(buf_len); - if (buf == NULL) - return; - - /* Peer RSN IE */ - os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len); - pos += kde->rsn_ie_len; - - /* Peer MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); - - /* PNonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, - WPA_NONCE_LEN, NULL, 0); - - /* SMK and INonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, - kde->nonce, WPA_NONCE_LEN); - - /* Lifetime */ - lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime), NULL, 0); - - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK M5"); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SMK_MESSAGE, - NULL, kde->nonce, buf, pos - buf, 0, 1, 0); - - os_free(buf); -} - - -void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); - return; - } - - if (kde.rsn_ie == NULL || - kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " - "Nonce KDE in SMK M3"); - return; - } - - /* Peer = sm->addr; Initiator = kde.mac_addr; - * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR - " aborted - STA not associated anymore", - MAC2STR(kde.mac_addr)); - wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, - STK_ERR_STA_NR); - /* FIX: wpa_stsl_remove(wpa_auth, neg); */ - return; - } - - if (random_get_bytes(smk, PMK_LEN)) { - wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); - return; - } - - /* SMK = PRF-256(Random number, "SMK Derivation", - * AA || Time || INonce || PNonce) - */ - os_memcpy(buf, wpa_auth->addr, ETH_ALEN); - pos = buf + ETH_ALEN; - wpa_get_ntp_timestamp(pos); - pos += 8; - os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); - pos += WPA_NONCE_LEN; - os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); -#ifdef CONFIG_IEEE80211W - sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), - smk, PMK_LEN); -#else /* CONFIG_IEEE80211W */ - sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), - smk, PMK_LEN); -#endif /* CONFIG_IEEE80211W */ - - wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); - - wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); - wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); - - /* Authenticator does not need SMK anymore and it is required to forget - * it. */ - os_memset(smk, 0, sizeof(*smk)); -} - - -void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - struct rsn_error_kde error; - u16 mui, error_type; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); - return; - } - - if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.error == NULL || kde.error_len < sizeof(error)) { - wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " - "SMK Error"); - return; - } - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " - "associated for SMK Error message from " MACSTR, - MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); - return; - } - - os_memcpy(&error, kde.error, sizeof(error)); - mui = be_to_host16(error.mui); - error_type = be_to_host16(error.error_type); - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "STA reported SMK Error: Peer " MACSTR - " MUI %d Error Type %d", - MAC2STR(kde.mac_addr), mui, error_type); - - wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); -} - - -int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, - struct wpa_stsl_negotiation *neg) -{ - struct wpa_stsl_negotiation *pos, *prev; - - if (wpa_auth == NULL) - return -1; - pos = wpa_auth->stsl_negotiations; - prev = NULL; - while (pos) { - if (pos == neg) { - if (prev) - prev->next = pos->next; - else - wpa_auth->stsl_negotiations = pos->next; - - eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); - os_free(pos); - return 0; - } - prev = pos; - pos = pos->next; - } - - return -1; -} - -#endif /* CONFIG_PEERKEY */ --- contrib/wpa/src/ap/pmksa_cache_auth.c.orig +++ contrib/wpa/src/ap/pmksa_cache_auth.c @@ -38,6 +38,7 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { + os_free(entry->vlan_desc); os_free(entry->identity); wpabuf_free(entry->cui); #ifndef CONFIG_NO_RADIUS @@ -91,6 +92,20 @@ } +/** + * pmksa_cache_auth_flush - Flush all PMKSA cache entries + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + */ +void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa) +{ + while (pmksa->pmksa) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for " + MACSTR, MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); + } +} + + static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; @@ -126,6 +141,8 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol) { + struct vlan_description *vlan_desc; + if (eapol == NULL) return; @@ -146,14 +163,22 @@ #endif /* CONFIG_NO_RADIUS */ entry->eap_type_authsrv = eapol->eap_type_authsrv; - entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; - entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; - entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo; + vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc; + if (vlan_desc && vlan_desc->notempty) { + entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); + if (entry->vlan_desc) + *entry->vlan_desc = *vlan_desc; + } else { + entry->vlan_desc = NULL; + } + + entry->acct_multi_session_id = eapol->acct_multi_session_id; } -void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, +void pmksa_cache_to_eapol_data(struct hostapd_data *hapd, + struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol) { if (entry == NULL || eapol == NULL) @@ -186,10 +211,11 @@ } eapol->eap_type_authsrv = entry->eap_type_authsrv; - ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; +#ifndef CONFIG_NO_VLAN + ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc); +#endif /* CONFIG_NO_VLAN */ - eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi; - eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo; + eapol->acct_multi_session_id = entry->acct_multi_session_id; } @@ -234,6 +260,7 @@ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID * @kck: Key confirmation key or %NULL if not yet derived * @kck_len: KCK length in bytes * @aa: Authenticator address @@ -250,15 +277,50 @@ */ struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, - const u8 *pmk, size_t pmk_len, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp) { - struct rsn_pmksa_cache_entry *entry, *pos; + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len, + aa, spa, session_timeout, eapol, + akmp); + + if (pmksa_cache_auth_add_entry(pmksa, entry) < 0) + return NULL; + + return entry; +} + + +/** + * pmksa_cache_auth_create_entry - Create a PMKSA cache entry + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @session_timeout: Session timeout + * @eapol: Pointer to EAPOL state machine data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function creates a PMKSA entry. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) +{ + struct rsn_pmksa_cache_entry *entry; struct os_reltime now; - if (pmk_len > PMK_LEN) + if (pmk_len > PMK_LEN_MAX) return NULL; if (wpa_key_mgmt_suite_b(akmp) && !kck) @@ -269,13 +331,14 @@ return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (pmkid) + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); else - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); os_get_reltime(&now); entry->expiration = now.sec; if (session_timeout > 0) @@ -286,9 +349,30 @@ os_memcpy(entry->spa, spa, ETH_ALEN); pmksa_cache_from_eapol_data(entry, eapol); + return entry; +} + + +/** + * pmksa_cache_auth_add_entry - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @entry: Pointer to PMKSA cache entry + * + * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is + * already in the cache for the same Supplicant, this entry will be replaced + * with the new entry. PMKID will be calculated based on the PMK. + */ +int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos; + + if (entry == NULL) + return -1; + /* Replace an old entry for the same STA (if found) with the new entry */ - pos = pmksa_cache_auth_get(pmksa, spa, NULL); + pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL); if (pos) pmksa_cache_free_entry(pmksa, pos); @@ -302,7 +386,7 @@ pmksa_cache_link_entry(pmksa, entry); - return entry; + return 0; } @@ -337,7 +421,13 @@ radius_copy_class(&entry->radius_class, &old_entry->radius_class); #endif /* CONFIG_NO_RADIUS */ entry->eap_type_authsrv = old_entry->eap_type_authsrv; - entry->vlan_id = old_entry->vlan_id; + if (old_entry->vlan_desc) { + entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); + if (entry->vlan_desc) + *entry->vlan_desc = *old_entry->vlan_desc; + } else { + entry->vlan_desc = NULL; + } entry->opportunistic = 1; pmksa_cache_link_entry(pmksa, entry); @@ -427,7 +517,7 @@ if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) continue; rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, - wpa_key_mgmt_sha256(entry->akmp)); + entry->akmp); if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) return entry; } @@ -471,12 +561,11 @@ if (attr->acct_multi_session_id) { char buf[20]; - if (attr->acct_multi_session_id_len != 17) + if (attr->acct_multi_session_id_len != 16) return 0; - os_snprintf(buf, sizeof(buf), "%08X+%08X", - entry->acct_multi_session_id_hi, - entry->acct_multi_session_id_lo); - if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0) + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) entry->acct_multi_session_id); + if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) return 0; match++; } @@ -526,3 +615,115 @@ return found ? 0 : -1; } + + +/** + * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA command. + */ +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) +{ + int i, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + os_get_reltime(&now); + ret = os_snprintf(pos, buf + len - pos, + "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + i = 0; + entry = pmksa->pmksa; + while (entry) { + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->spa)); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, + PMKID_LEN); + ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + (int) (entry->expiration - now.sec), + entry->opportunistic); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + entry = entry->next; + } + return pos - buf; +} + + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +/** + * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @addr: MAC address of the peer (NULL means any) + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store + * in external storage. + */ +int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, + char *buf, size_t len) +{ + int ret; + char *pos, *end; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + pos = buf; + end = buf + len; + os_get_reltime(&now); + + + /* + * Entry format: + * + */ + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0) + continue; + + ret = os_snprintf(pos, end - pos, MACSTR " ", + MAC2STR(entry->spa)); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid, + PMKID_LEN); + + ret = os_snprintf(pos, end - pos, " "); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, entry->pmk, + entry->pmk_len); + + ret = os_snprintf(pos, end - pos, " %d\n", + (int) (entry->expiration - now.sec)); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + + return pos - buf; +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ --- contrib/wpa/src/ap/pmksa_cache_auth.h.orig +++ contrib/wpa/src/ap/pmksa_cache_auth.h @@ -17,7 +17,7 @@ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry *next, *hnext; u8 pmkid[PMKID_LEN]; - u8 pmk[PMK_LEN]; + u8 pmk[PMK_LEN_MAX]; size_t pmk_len; os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ @@ -28,14 +28,14 @@ struct wpabuf *cui; struct radius_class_data radius_class; u8 eap_type_authsrv; - int vlan_id; + struct vlan_description *vlan_desc; int opportunistic; - u32 acct_multi_session_id_hi; - u32 acct_multi_session_id_lo; + u64 acct_multi_session_id; }; struct rsn_pmksa_cache; +struct radius_das_attrs; struct rsn_pmksa_cache * pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -49,19 +49,31 @@ const u8 *pmkid); struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, - const u8 *pmk, size_t pmk_len, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp); struct rsn_pmksa_cache_entry * +pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp); +int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa, const u8 *pmkid); -void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, +void pmksa_cache_to_eapol_data(struct hostapd_data *hapd, + struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol); void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, struct radius_das_attrs *attr); +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); +void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa); +int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, + char *buf, size_t len); #endif /* PMKSA_CACHE_H */ --- contrib/wpa/src/ap/rrm.c.orig +++ contrib/wpa/src/ap/rrm.c @@ -0,0 +1,674 @@ +/* + * hostapd / Radio Measurement (RRM) + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * Copyright (c) 2016-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "eloop.h" +#include "neighbor_db.h" +#include "rrm.h" + +#define HOSTAPD_RRM_REQUEST_TIMEOUT 5 + + +static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out", + hapd->lci_req_token); + hapd->lci_req_active = 0; +} + + +static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token, + const u8 *pos, size_t len) +{ + if (!hapd->lci_req_active || hapd->lci_req_token != token) { + wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token); + return; + } + + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); + wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len); +} + + +static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out", + hapd->range_req_token); + hapd->range_req_active = 0; +} + + +static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token, + const u8 *pos, size_t len) +{ + if (!hapd->range_req_active || hapd->range_req_token != token) { + wpa_printf(MSG_DEBUG, "Unexpected range report, token %u", + token); + return; + } + + hapd->range_req_active = 0; + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); + wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len); +} + + +static void hostapd_handle_beacon_report(struct hostapd_data *hapd, + const u8 *addr, u8 token, u8 rep_mode, + const u8 *pos, size_t len) +{ + char report[2 * 255 + 1]; + + wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR, + token, len, MAC2STR(addr)); + /* Skip to the beginning of the Beacon report */ + if (len < 3) + return; + pos += 3; + len -= 3; + report[0] = '\0'; + if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s", + MAC2STR(addr), token, rep_mode, report); +} + + +static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + const u8 *pos, *ie, *end; + u8 token, rep_mode; + + end = buf + len; + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + + while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { + if (ie[1] < 3) { + wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); + break; + } + + rep_mode = ie[3]; + wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u", + rep_mode, ie[4]); + + switch (ie[4]) { + case MEASURE_TYPE_LCI: + hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]); + break; + case MEASURE_TYPE_FTM_RANGE: + hostapd_handle_range_report(hapd, token, ie + 2, ie[1]); + break; + case MEASURE_TYPE_BEACON: + hostapd_handle_beacon_report(hapd, mgmt->sa, token, + rep_mode, ie + 2, ie[1]); + break; + default: + wpa_printf(MSG_DEBUG, + "Measurement report type %u is not supported", + ie[4]); + break; + } + + pos = ie + ie[1] + 2; + } +} + + +static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) +{ + const u8 *subelem; + + /* Range Request element + Location Subject + Maximum Age subelement */ + if (len < 3 + 1 + 4) + return 0; + + /* Subelements are arranged as IEs */ + subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE); + if (subelem && subelem[1] == 2) + return WPA_GET_LE16(subelem + 2); + + return 0; +} + + +static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age) +{ + struct os_time curr, diff; + unsigned long diff_l; + + if (nr->stationary || max_age == 0xffff) + return 1; + + if (!max_age) + return 0; + + if (os_get_time(&curr)) + return 0; + + os_time_sub(&curr, &nr->lci_date, &diff); + + /* avoid overflow */ + if (diff.sec > 0xffff) + return 0; + + /* LCI age is calculated in 10th of a second units. */ + diff_l = diff.sec * 10 + diff.usec / 100000; + + return max_age > diff_l; +} + + +static size_t hostapd_neighbor_report_len(struct wpabuf *buf, + struct hostapd_neighbor_entry *nr, + int send_lci, int send_civic) +{ + size_t len = 2 + wpabuf_len(nr->nr); + + if (send_lci && nr->lci) + len += 2 + wpabuf_len(nr->lci); + + if (send_civic && nr->civic) + len += 2 + wpabuf_len(nr->civic); + + return len; +} + + +static void hostapd_send_nei_report_resp(struct hostapd_data *hapd, + const u8 *addr, u8 dialog_token, + struct wpa_ssid_value *ssid, u8 lci, + u8 civic, u16 lci_max_age) +{ + struct hostapd_neighbor_entry *nr; + struct wpabuf *buf; + u8 *msmt_token; + + /* + * The number and length of the Neighbor Report elements in a Neighbor + * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes + * of RRM header. + */ + buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE); + if (!buf) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE); + wpabuf_put_u8(buf, dialog_token); + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + int send_lci; + size_t len; + + if (ssid->ssid_len != nr->ssid.ssid_len || + os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0) + continue; + + send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age); + len = hostapd_neighbor_report_len(buf, nr, send_lci, civic); + + if (len - 2 > 0xff) { + wpa_printf(MSG_DEBUG, + "NR entry for " MACSTR " exceeds 0xFF bytes", + MAC2STR(nr->bssid)); + continue; + } + + if (len > wpabuf_tailroom(buf)) + break; + + wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); + wpabuf_put_u8(buf, len - 2); + wpabuf_put_buf(buf, nr->nr); + + if (send_lci && nr->lci) { + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->lci)); + /* + * Override measurement token - the first byte of the + * Measurement Report element. + */ + msmt_token = wpabuf_put(buf, 0); + wpabuf_put_buf(buf, nr->lci); + *msmt_token = lci; + } + + if (civic && nr->civic) { + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->civic)); + /* + * Override measurement token - the first byte of the + * Measurement Report element. + */ + msmt_token = wpabuf_put(buf, 0); + wpabuf_put_buf(buf, nr->civic); + *msmt_token = civic; + } + } + + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); +} + + +static void hostapd_handle_nei_report_req(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + const u8 *pos, *ie, *end; + struct wpa_ssid_value ssid = { + .ssid_len = 0 + }; + u8 token; + u8 lci = 0, civic = 0; /* Measurement tokens */ + u16 lci_max_age = 0; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return; + + end = buf + len; + + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + len = end - pos; + + ie = get_ie(pos, len, WLAN_EID_SSID); + if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) { + ssid.ssid_len = ie[1]; + os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len); + } else { + ssid.ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + } + + while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) { + if (ie[1] < 3) + break; + + wpa_printf(MSG_DEBUG, + "Neighbor report request, measure type %u", + ie[4]); + + switch (ie[4]) { /* Measurement Type */ + case MEASURE_TYPE_LCI: + lci = ie[2]; /* Measurement Token */ + lci_max_age = hostapd_parse_location_lci_req_age(ie + 2, + ie[1]); + break; + case MEASURE_TYPE_LOCATION_CIVIC: + civic = ie[2]; /* Measurement token */ + break; + } + + pos = ie + ie[1] + 2; + len = end - pos; + } + + hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic, + lci_max_age); +} + + +void hostapd_handle_radio_measurement(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + + /* + * Check for enough bytes: header + (1B)Category + (1B)Action + + * (1B)Dialog Token. + */ + if (len < IEEE80211_HDRLEN + 3) + return; + + wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR, + mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa)); + + switch (mgmt->u.action.u.rrm.action) { + case WLAN_RRM_RADIO_MEASUREMENT_REPORT: + hostapd_handle_radio_msmt_report(hapd, buf, len); + break; + case WLAN_RRM_NEIGHBOR_REPORT_REQUEST: + hostapd_handle_nei_report_req(hapd, buf, len); + break; + default: + wpa_printf(MSG_DEBUG, "RRM action %u is not supported", + mgmt->u.action.u.rrm.action); + break; + } +} + + +int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr) +{ + struct wpabuf *buf; + struct sta_info *sta = ap_get_sta(hapd, addr); + int ret; + + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Request LCI: Destination address is not connected"); + return -1; + } + + if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) { + wpa_printf(MSG_INFO, + "Request LCI: Station does not support LCI in RRM"); + return -1; + } + + if (hapd->lci_req_active) { + wpa_printf(MSG_DEBUG, + "Request LCI: LCI request is already in process, overriding"); + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, + NULL); + } + + /* Measurement request (5) + Measurement element with LCI (10) */ + buf = wpabuf_alloc(5 + 10); + if (!buf) + return -1; + + hapd->lci_req_token++; + /* For wraparounds - the token must be nonzero */ + if (!hapd->lci_req_token) + hapd->lci_req_token++; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->lci_req_token); + wpabuf_put_le16(buf, 0); /* Number of repetitions */ + + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + 1 + 4); + + wpabuf_put_u8(buf, 1); /* Measurement Token */ + /* + * Parallel and Enable bits are 0, Duration, Request, and Report are + * reserved. + */ + wpabuf_put_u8(buf, 0); + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); + + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret) + return ret; + + hapd->lci_req_active = 1; + + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hostapd_lci_rep_timeout_handler, hapd, NULL); + + return 0; +} + + +int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, + u16 random_interval, u8 min_ap, + const u8 *responders, unsigned int n_responders) +{ + struct wpabuf *buf; + struct sta_info *sta; + u8 *len; + unsigned int i; + int ret; + + wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR + " rand interval %u min AP %u n_responders %u", MAC2STR(addr), + random_interval, min_ap, n_responders); + + if (min_ap == 0 || min_ap > n_responders) { + wpa_printf(MSG_INFO, "Request range: Wrong min AP count"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Request range: Destination address is not connected"); + return -1; + } + + if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) { + wpa_printf(MSG_ERROR, + "Request range: Destination station does not support FTM range report in RRM"); + return -1; + } + + if (hapd->range_req_active) { + wpa_printf(MSG_DEBUG, + "Request range: Range request is already in process; overriding"); + hapd->range_req_active = 0; + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, + NULL); + } + + /* Action + measurement type + token + reps + EID + len = 7 */ + buf = wpabuf_alloc(7 + 255); + if (!buf) + return -1; + + hapd->range_req_token++; + if (!hapd->range_req_token) /* For wraparounds */ + hapd->range_req_token++; + + /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */ + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */ + wpabuf_put_le16(buf, 0); /* Number of Repetitions */ + + /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + len = wpabuf_put(buf, 1); /* Length will be set later */ + + wpabuf_put_u8(buf, 1); /* Measurement Token */ + /* + * Parallel and Enable bits are 0; Duration, Request, and Report are + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */ + + /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */ + wpabuf_put_le16(buf, random_interval); /* Randomization Interval */ + wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */ + + /* FTM Range Subelements */ + + /* + * Taking the neighbor report part of the range request from neighbor + * database instead of requesting the separate bits of data from the + * user. + */ + for (i = 0; i < n_responders; i++) { + struct hostapd_neighbor_entry *nr; + + nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i, + NULL); + if (!nr) { + wpa_printf(MSG_INFO, "Missing neighbor report for " + MACSTR, MAC2STR(responders + ETH_ALEN * i)); + wpabuf_free(buf); + return -1; + } + + if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) { + wpa_printf(MSG_ERROR, "Too long range request"); + wpabuf_free(buf); + return -1; + } + + wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->nr)); + wpabuf_put_buf(buf, nr->nr); + } + + /* Action + measurement type + token + reps + EID + len = 7 */ + *len = wpabuf_len(buf) - 7; + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret) + return ret; + + hapd->range_req_active = 1; + + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hostapd_range_rep_timeout_handler, hapd, NULL); + + return 0; +} + + +void hostapd_clean_rrm(struct hostapd_data *hapd) +{ + hostapd_free_neighbor_db(hapd); + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); + hapd->range_req_active = 0; +} + + +int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, + u8 req_mode, const struct wpabuf *req) +{ + struct wpabuf *buf; + struct sta_info *sta = ap_get_sta(hapd, addr); + int ret; + enum beacon_report_mode mode; + const u8 *pos; + + /* Request data: + * Operating Class (1), Channel Number (1), Randomization Interval (2), + * Measurement Duration (2), Measurement Mode (1), BSSID (6), + * Optional Subelements (variable) + */ + if (wpabuf_len(req) < 13) { + wpa_printf(MSG_INFO, "Beacon request: Too short request data"); + return -1; + } + pos = wpabuf_head(req); + mode = pos[6]; + + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR " is not connected", + MAC2STR(addr)); + return -1; + } + + switch (mode) { + case BEACON_REPORT_MODE_PASSIVE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support passive beacon report", + MAC2STR(addr)); + return -1; + } + break; + case BEACON_REPORT_MODE_ACTIVE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support active beacon report", + MAC2STR(addr)); + return -1; + } + break; + case BEACON_REPORT_MODE_TABLE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support table beacon report", + MAC2STR(addr)); + return -1; + } + break; + default: + wpa_printf(MSG_INFO, + "Beacon request: Unknown measurement mode %d", mode); + return -1; + } + + buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req)); + if (!buf) + return -1; + + hapd->beacon_req_token++; + if (!hapd->beacon_req_token) + hapd->beacon_req_token++; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->beacon_req_token); + wpabuf_put_le16(buf, 0); /* Number of repetitions */ + + /* Measurement Request element */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + wpabuf_len(req)); + wpabuf_put_u8(buf, 1); /* Measurement Token */ + wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */ + wpabuf_put_buf(buf, req); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret < 0) + return ret; + + return hapd->beacon_req_token; +} + + +void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + if (len < 24 + 3) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR + " %u ack=%d", MAC2STR(mgmt->da), + mgmt->u.action.u.rrm.dialog_token, ok); +} --- contrib/wpa/src/ap/rrm.h.orig +++ contrib/wpa/src/ap/rrm.h @@ -0,0 +1,33 @@ +/* + * hostapd / Radio Measurement (RRM) + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RRM_H +#define RRM_H + +/* + * Max measure request length is 255, -6 of the body we have 249 for the + * neighbor report elements. Each neighbor report element is at least 2 + 13 + * bytes, so we can't have more than 16 responders in the request. + */ +#define RRM_RANGE_REQ_MAX_RESPONDERS 16 + +void hostapd_handle_radio_measurement(struct hostapd_data *hapd, + const u8 *buf, size_t len); +int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr); +int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, + u16 random_interval, u8 min_ap, + const u8 *responders, unsigned int n_responders); +void hostapd_clean_rrm(struct hostapd_data *hapd); +int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, + u8 req_mode, const struct wpabuf *req); +void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok); + +#endif /* RRM_H */ --- contrib/wpa/src/ap/sta_info.c.orig +++ contrib/wpa/src/ap/sta_info.c @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2002-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,10 +13,12 @@ #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "common/sae.h" +#include "common/dpp.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" #include "fst/fst.h" +#include "crypto/crypto.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" @@ -32,8 +34,11 @@ #include "ap_drv_ops.h" #include "gas_serv.h" #include "wnm_ap.h" +#include "mbo_ap.h" #include "ndisc_snoop.h" #include "sta_info.h" +#include "vlan.h" +#include "wps_hostapd.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); @@ -45,6 +50,7 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); #endif /* CONFIG_IEEE80211W */ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta); +static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx); int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, @@ -161,7 +167,7 @@ /* just in case */ ap_sta_set_authorized(hapd, sta, 0); - if (sta->flags & WLAN_STA_WDS) + if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); if (sta->ipaddr) @@ -169,21 +175,10 @@ ap_sta_ip6addr_del(hapd, sta); if (!hapd->iface->driver_ap_teardown && - !(sta->flags & WLAN_STA_PREAUTH)) + !(sta->flags & WLAN_STA_PREAUTH)) { hostapd_drv_sta_remove(hapd, sta->addr); - -#ifndef CONFIG_NO_VLAN - if (sta->vlan_id_bound) { - /* - * Need to remove the STA entry before potentially removing the - * VLAN. - */ - if (hapd->iface->driver_ap_teardown && - !(sta->flags & WLAN_STA_PREAUTH)) - hostapd_drv_sta_remove(hapd, sta->addr); - vlan_remove_dynamic(hapd, sta->vlan_id_bound); + sta->added_unassoc = 0; } -#endif /* CONFIG_NO_VLAN */ ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); @@ -203,7 +198,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++; } @@ -211,7 +207,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++; } @@ -231,6 +228,13 @@ hapd->iface->num_sta_ht_20mhz--; } +#ifdef CONFIG_TAXONOMY + wpabuf_free(sta->probe_ie_taxonomy); + sta->probe_ie_taxonomy = NULL; + wpabuf_free(sta->assoc_ie_taxonomy); + sta->assoc_ie_taxonomy = NULL; +#endif /* CONFIG_TAXONOMY */ + #ifdef CONFIG_IEEE80211N ht40_intolerant_remove(hapd->iface, sta); #endif /* CONFIG_IEEE80211N */ @@ -251,7 +255,7 @@ #ifdef CONFIG_MESH if (hapd->mesh_sta_free_cb) - hapd->mesh_sta_free_cb(sta); + hapd->mesh_sta_free_cb(hapd, sta); #endif /* CONFIG_MESH */ if (set_beacon) @@ -262,11 +266,10 @@ eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); - eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); - eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); sae_clear_retransmit_timer(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); wpa_auth_sta_deinit(sta->wpa_sm); rsn_preauth_free_station(hapd, sta); #ifndef CONFIG_NO_RADIUS @@ -274,6 +277,28 @@ radius_client_flush_auth(hapd->radius, sta->addr); #endif /* CONFIG_NO_RADIUS */ +#ifndef CONFIG_NO_VLAN + /* + * sta->wpa_sm->group needs to be released before so that + * vlan_remove_dynamic() can check that no stations are left on the + * AP_VLAN netdev. + */ + if (sta->vlan_id) + vlan_remove_dynamic(hapd, sta->vlan_id); + if (sta->vlan_id_bound) { + /* + * Need to remove the STA entry before potentially removing the + * VLAN. + */ + if (hapd->iface->driver_ap_teardown && + !(sta->flags & WLAN_STA_PREAUTH)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + vlan_remove_dynamic(hapd, sta->vlan_id_bound); + } +#endif /* CONFIG_NO_VLAN */ + os_free(sta->challenge); #ifdef CONFIG_IEEE80211W @@ -297,6 +322,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 */ @@ -303,10 +329,12 @@ os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); + os_free(sta->vht_operation); hostapd_free_psk_list(sta->psk); 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); @@ -315,6 +343,39 @@ os_free(sta->sae); #endif /* CONFIG_SAE */ + mbo_ap_sta_free(sta); + os_free(sta->supp_op_classes); + +#ifdef CONFIG_FILS + os_free(sta->fils_pending_assoc_req); + wpabuf_free(sta->fils_hlp_resp); + wpabuf_free(sta->hlp_dhcp_discover); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sta->fils_ecdh); + wpabuf_clear_free(sta->fils_dh_ss); + wpabuf_free(sta->fils_g_sta); +#endif /* CONFIG_FILS_SK_PFS */ +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + bin_clear_free(sta->owe_pmk, sta->owe_pmk_len); + crypto_ecdh_deinit(sta->owe_ecdh); +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; +#endif /* CONFIG_DPP2 */ + + 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); } @@ -354,8 +415,8 @@ unsigned long next_time = 0; int reason; - wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d", - __func__, MAC2STR(sta->addr), sta->flags, + wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d", + hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags, sta->timeout_next); if (sta->timeout_next == STA_REMOVE) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -446,6 +507,13 @@ } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; + if (!deauth && !(sta->flags & WLAN_STA_ASSOC)) { + /* Cannot disassociate not-associated STA, so move + * directly to deauthentication. */ + sta->timeout_next = STA_DEAUTH; + deauth = 1; + } + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Timeout, sending %s info to STA " MACSTR, deauth ? "deauthentication" : "disassociation", @@ -482,7 +550,7 @@ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated due to " "inactivity"); @@ -519,6 +587,8 @@ struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Session timer for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); if (!(sta->flags & WLAN_STA_AUTH)) { if (sta->flags & WLAN_STA_GAS) { wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA " @@ -573,18 +643,18 @@ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) { -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; - wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR, - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: WNM: Session warning time reached for " + MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); if (sta->hs20_session_info_url == NULL) return; wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url, sta->hs20_disassoc_timer); -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ } @@ -619,7 +689,10 @@ return NULL; } sta->acct_interim_interval = hapd->conf->acct_interim_interval; - accounting_sta_get_id(hapd, sta); + if (accounting_sta_get_id(hapd, sta) < 0) { + os_free(sta); + return NULL; + } if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " @@ -640,6 +713,11 @@ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; dl_list_init(&sta->ip6addr); +#ifdef CONFIG_TAXONOMY + sta_track_claim_taxonomy_info(hapd->iface, addr, + &sta->probe_ie_taxonomy); +#endif /* CONFIG_TAXONOMY */ + return sta; } @@ -652,14 +730,16 @@ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); ap_sta_ip6addr_del(hapd, sta); - wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver", + hapd->conf->iface, MAC2STR(sta->addr)); if (hostapd_drv_sta_remove(hapd, sta->addr) && sta->flags & WLAN_STA_ASSOC) { - wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR - " from kernel driver.", MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR + " from kernel driver", + hapd->conf->iface, MAC2STR(sta->addr)); return -1; } + sta->added_unassoc = 0; return 0; } @@ -683,6 +763,10 @@ if (!sta2) continue; + wpa_printf(MSG_DEBUG, "%s: disconnect old STA " MACSTR + " association from another BSS %s", + hapd->conf->iface, MAC2STR(sta2->addr), + bss->conf->iface); ap_sta_disconnect(bss, sta2, sta2->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -694,6 +778,8 @@ struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Disassociation callback for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); ap_sta_remove(hapd, sta); mlme_disassociate_indication(hapd, sta, sta->disassoc_reason); } @@ -705,9 +791,17 @@ wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; - sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Skip deauthentication in DMG/IEEE 802.11ad */ + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_ASSOC_REQ_OK); + sta->timeout_next = STA_REMOVE; + } else { + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + sta->timeout_next = STA_DEAUTH; + } ap_sta_set_authorized(hapd, sta, 0); - sta->timeout_next = STA_DEAUTH; wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DISASSOC)", @@ -717,7 +811,9 @@ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); + wpa_auth_sta_deinit(sta->wpa_sm); + sta->wpa_sm = NULL; sta->disassoc_reason = reason; sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; @@ -733,6 +829,8 @@ struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Deauthentication callback for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); ap_sta_remove(hapd, sta); mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason); } @@ -741,6 +839,14 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + ap_sta_disassociate(hapd, sta, reason); + return; + } + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; @@ -756,7 +862,7 @@ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; @@ -784,6 +890,125 @@ #endif /* CONFIG_WPS */ +static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd) +{ + struct hostapd_vlan *vlan; + int vlan_id = MAX_VLAN_ID + 2; + +retry: + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == vlan_id) { + vlan_id++; + goto retry; + } + } + return vlan_id; +} + + +int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, + struct vlan_description *vlan_desc) +{ + struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL; + int old_vlan_id, vlan_id = 0, ret = 0; + + /* Check if there is something to do */ + if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) { + /* This sta is lacking its own vif */ + } else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED && + !hapd->conf->ssid.per_sta_vif && sta->vlan_id) { + /* sta->vlan_id needs to be reset */ + } else if (!vlan_compare(vlan_desc, sta->vlan_desc)) { + return 0; /* nothing to change */ + } + + /* Now the real VLAN changed or the STA just needs its own vif */ + if (hapd->conf->ssid.per_sta_vif) { + /* Assign a new vif, always */ + /* find a free vlan_id sufficiently big */ + vlan_id = ap_sta_get_free_vlan_id(hapd); + /* Get wildcard VLAN */ + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == VLAN_ID_WILDCARD) + break; + } + if (!vlan) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "per_sta_vif missing wildcard"); + vlan_id = 0; + ret = -1; + goto done; + } + } else if (vlan_desc && vlan_desc->notempty) { + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (!vlan_compare(&vlan->vlan_desc, vlan_desc)) + break; + if (vlan->vlan_id == VLAN_ID_WILDCARD) + wildcard_vlan = vlan; + } + if (vlan) { + vlan_id = vlan->vlan_id; + } else if (wildcard_vlan) { + vlan = wildcard_vlan; + vlan_id = vlan_desc->untagged; + if (vlan_desc->tagged[0]) { + /* Tagged VLAN configuration */ + vlan_id = ap_sta_get_free_vlan_id(hapd); + } + } else { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "missing vlan and wildcard for vlan=%d%s", + vlan_desc->untagged, + vlan_desc->tagged[0] ? "+" : ""); + vlan_id = 0; + ret = -1; + goto done; + } + } + + if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) { + vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc); + if (vlan == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "could not add dynamic VLAN interface for vlan=%d%s", + vlan_desc ? vlan_desc->untagged : -1, + (vlan_desc && vlan_desc->tagged[0]) ? + "+" : ""); + vlan_id = 0; + ret = -1; + goto done; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "added new dynamic VLAN interface '%s'", + vlan->ifname); + } else if (vlan && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "updated existing dynamic VLAN interface '%s'", + vlan->ifname); + } +done: + old_vlan_id = sta->vlan_id; + sta->vlan_id = vlan_id; + sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL; + + if (vlan_id != old_vlan_id && old_vlan_id) + vlan_remove_dynamic(hapd, old_vlan_id); + + return ret; +} + + int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) { #ifndef CONFIG_NO_VLAN @@ -796,20 +1021,11 @@ if (hapd->conf->ssid.vlan[0]) iface = hapd->conf->ssid.vlan; - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; - else if (sta->vlan_id > 0) { - struct hostapd_vlan *wildcard_vlan = NULL; - vlan = hapd->conf->vlan; - while (vlan) { + if (sta->vlan_id > 0) { + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == sta->vlan_id) break; - if (vlan->vlan_id == VLAN_ID_WILDCARD) - wildcard_vlan = vlan; - vlan = vlan->next; } - if (!vlan) - vlan = wildcard_vlan; if (vlan) iface = vlan->ifname; } @@ -829,54 +1045,13 @@ sta->vlan_id); ret = -1; goto done; - } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { - vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); - if (vlan == NULL) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not add " - "dynamic VLAN interface for vlan_id=%d", - sta->vlan_id); - ret = -1; - goto done; - } - - iface = vlan->ifname; - if (vlan_setup_encryption_dyn(hapd, iface) != 0) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not " - "configure encryption for dynamic VLAN " - "interface for vlan_id=%d", - sta->vlan_id); - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " - "interface '%s'", iface); - } else if (vlan && vlan->vlan_id == sta->vlan_id) { - if (vlan->dynamic_vlan > 0) { - vlan->dynamic_vlan++; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "updated existing " - "dynamic VLAN interface '%s'", iface); - } - - /* - * Update encryption configuration for statically generated - * VLAN interface. This is only used for static WEP - * configuration for the case where hostapd did not yet know - * which keys are to be used when the interface was added. - */ - if (vlan_setup_encryption_dyn(hapd, iface) != 0) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not " - "configure encryption for VLAN " - "interface for vlan_id=%d", - sta->vlan_id); - } + } else if (vlan && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "updated existing dynamic VLAN interface '%s'", + iface); } /* ref counters have been increased, so mark the station */ @@ -942,6 +1117,10 @@ unsigned int timeout, sec, usec; u8 *trans_id, *nbuf; + wpa_printf(MSG_DEBUG, "%s: SA Query timer for STA " MACSTR + " (count=%d)", + hapd->conf->iface, MAC2STR(sta->addr), sta->sa_query_count); + if (sta->sa_query_count > 0 && ap_check_sa_query_timeout(hapd, sta)) return; @@ -999,6 +1178,32 @@ #endif /* CONFIG_IEEE80211W */ +const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct hostapd_wpa_psk *psk; + struct hostapd_ssid *ssid; + const u8 *pmk; + int pmk_len; + + ssid = &hapd->conf->ssid; + + pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); + if (!pmk || pmk_len != PMK_LEN) + return NULL; + + for (psk = ssid->wpa_psk; psk; psk = psk->next) + if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0) + break; + if (!psk) + return NULL; + if (!psk || !psk->keyid[0]) + return NULL; + + return psk->keyid; +} + + void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { @@ -1037,7 +1242,11 @@ sta->addr, authorized, dev_addr); if (authorized) { + const char *keyid; + char keyid_buf[100]; char ip_addr[100]; + + keyid_buf[0] = '\0'; ip_addr[0] = '\0'; #ifdef CONFIG_P2P if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { @@ -1048,14 +1257,20 @@ } #endif /* CONFIG_P2P */ - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s", - buf, ip_addr); + keyid = ap_sta_wpa_get_keyid(hapd, sta); + if (keyid) { + os_snprintf(keyid_buf, sizeof(keyid_buf), + " keyid=%s", keyid); + } + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s", + buf, ip_addr, keyid_buf); + if (hapd->msg_ctx_parent && hapd->msg_ctx_parent != hapd->msg_ctx) wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_CONNECTED "%s%s", - buf, ip_addr); + AP_STA_CONNECTED "%s%s%s", + buf, ip_addr, keyid_buf); } else { wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); @@ -1080,6 +1295,14 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason) { + if (sta) + wpa_printf(MSG_DEBUG, "%s: %s STA " MACSTR " reason=%u", + hapd->conf->iface, __func__, MAC2STR(sta->addr), + reason); + else if (addr) + wpa_printf(MSG_DEBUG, "%s: %s addr " MACSTR " reason=%u", + hapd->conf->iface, __func__, MAC2STR(addr), + reason); if (sta == NULL && addr) sta = ap_get_sta(hapd, addr); @@ -1093,10 +1316,10 @@ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DEAUTH)", - __func__, MAC2STR(sta->addr), + hapd->conf->iface, __func__, MAC2STR(sta->addr), AP_MAX_INACTIVITY_AFTER_DEAUTH); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, @@ -1103,6 +1326,20 @@ ap_handle_timer, hapd, sta); sta->timeout_next = STA_REMOVE; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + sta->disassoc_reason = reason; + sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? + 2 : 0, 0, ap_sta_disassoc_cb_timeout, + hapd, sta); + return; + } + sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); @@ -1136,12 +1373,37 @@ } +void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, + struct sta_info *sta) +{ + if (eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta) > 0) + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_deauth_cb_timeout timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + if (eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta) > 0) + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_disassoc_cb_timeout timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0) + { + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + if (sta->flags & WLAN_STA_WPS) + hostapd_wps_eap_completed(hapd); + } +} + + int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) { int res; buf[0] = '\0'; - res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), @@ -1158,6 +1420,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 ? @@ -1167,3 +1430,48 @@ return res; } + + +static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + u16 reason; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "IEEE 802.1X: Scheduled disconnection of " MACSTR + " after EAP-Failure", MAC2STR(sta->addr)); + + reason = sta->disconnect_reason_code; + if (!reason) + reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED; + ap_sta_disconnect(hapd, sta, sta->addr, reason); + if (sta->flags & WLAN_STA_WPS) + hostapd_wps_eap_completed(hapd); +} + + +void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta) +{ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "IEEE 802.1X: Force disconnection of " MACSTR + " after EAP-Failure in 10 ms", MAC2STR(sta->addr)); + + /* + * Add a small sleep to increase likelihood of previously requested + * EAP-Failure TX getting out before this should the driver reorder + * operations. + */ + eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta); + eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb, + hapd, sta); +} + + +int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta) +{ + return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb, + hapd, sta); +} --- contrib/wpa/src/ap/sta_info.h.orig +++ contrib/wpa/src/ap/sta_info.h @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,12 +9,11 @@ #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" +#include "common/ieee802_11_defs.h" /* STA flags */ #define WLAN_STA_AUTH BIT(0) @@ -36,6 +35,8 @@ #define WLAN_STA_WNM_SLEEP_MODE BIT(19) #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) #define WLAN_STA_VENDOR_VHT BIT(21) +#define WLAN_STA_PENDING_FILS_ERP BIT(22) +#define WLAN_STA_MULTI_AP BIT(23) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -44,7 +45,22 @@ * Supported Rates IEs). */ #define WLAN_SUPP_RATES_MAX 32 +struct hostapd_data; +struct mbo_non_pref_chan_info { + struct mbo_non_pref_chan_info *next; + u8 op_class; + u8 pref; + u8 reason_code; + u8 num_channels; + u8 channels[]; +}; + +struct pending_eapol_rx { + struct wpabuf *buf; + struct os_reltime rx_time; +}; + struct sta_info { struct sta_info *next; /* next entry in sta list */ struct sta_info *hnext; /* next entry in hash table list */ @@ -52,6 +68,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 */ @@ -63,13 +80,22 @@ enum mesh_plink_state plink_state; u16 peer_lid; u16 my_lid; + u16 peer_aid; u16 mpm_close_reason; int mpm_retries; - u8 my_nonce[32]; - u8 peer_nonce[32]; + u8 my_nonce[WPA_NONCE_LEN]; + u8 peer_nonce[WPA_NONCE_LEN]; u8 aek[32]; /* SHA256 digest length */ - u8 mtk[16]; - u8 mgtk[16]; + u8 mtk[WPA_TK_MAX_LEN]; + size_t mtk_len; + u8 mgtk_rsc[6]; + u8 mgtk_key_id; + u8 mgtk[WPA_TK_MAX_LEN]; + size_t mgtk_len; + u8 igtk_rsc[6]; + u8 igtk[WPA_TK_MAX_LEN]; + size_t igtk_len; + u16 igtk_key_id; u8 sae_auth_retry; #endif /* CONFIG_MESH */ @@ -86,6 +112,13 @@ unsigned int hs20_deauth_requested:1; unsigned int session_timeout_set:1; unsigned int radius_das_match:1; + unsigned int ecsa_supported:1; + unsigned int added_unassoc:1; + unsigned int pending_wds_enable:1; + unsigned int power_capab:1; + unsigned int agreed_to_steer:1; + unsigned int hs20_t_c_filtering:1; + unsigned int ft_over_ds:1; u16 auth_alg; @@ -100,17 +133,20 @@ /* IEEE 802.1X related data */ struct eapol_state_machine *eapol_sm; - u32 acct_session_id_hi; - u32 acct_session_id_lo; + struct pending_eapol_rx *pending_eapol_rx; + + u64 acct_session_id; struct os_reltime acct_session_start; int acct_session_started; int acct_terminate_cause; /* Acct-Terminate-Cause */ int acct_interim_interval; /* Acct-Interim-Interval */ + unsigned int acct_interim_errors; - unsigned long last_rx_bytes; - unsigned long last_tx_bytes; - u32 acct_input_gigawords; /* Acct-Input-Gigawords */ - u32 acct_output_gigawords; /* Acct-Output-Gigawords */ + /* For extending 32-bit driver counters to 64-bit counters */ + u32 last_rx_bytes_hi; + u32 last_rx_bytes_lo; + u32 last_tx_bytes_hi; + u32 last_tx_bytes_lo; u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ @@ -118,6 +154,7 @@ struct rsn_preauth_interface *preauth_iface; int vlan_id; /* 0: none, >0: VID */ + struct vlan_description *vlan_desc; int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ /* PSKs from RADIUS authentication server */ struct hostapd_sta_wpa_psk_short *psk; @@ -127,6 +164,7 @@ struct ieee80211_ht_capabilities *ht_capabilities; struct ieee80211_vht_capabilities *vht_capabilities; + struct ieee80211_vht_operation *vht_operation; u8 vht_opmode; #ifdef CONFIG_IEEE80211W @@ -139,17 +177,20 @@ struct os_reltime sa_query_start; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_INTERWORKING +#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ struct gas_dialog_info *gas_dialog; u8 gas_dialog_next; -#endif /* CONFIG_INTERWORKING */ +#endif /* CONFIG_INTERWORKING || CONFIG_DPP */ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ + /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */ + struct wpabuf *roaming_consortium; u8 remediation_method; char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ + char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */ struct wpabuf *hs20_deauth_req; char *hs20_session_info_url; int hs20_disassoc_timer; @@ -161,9 +202,11 @@ #ifdef CONFIG_SAE struct sae_data *sae; + 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 */ @@ -170,6 +213,68 @@ u16 last_seq_ctrl; /* Last Authentication/(Re)Association Request/Action frame subtype */ u8 last_subtype; + +#ifdef CONFIG_MBO + u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise, + * enum mbo_cellular_capa values */ + struct mbo_non_pref_chan_info *non_pref_chan; + int auth_rssi; /* Last Authentication frame RSSI */ +#endif /* CONFIG_MBO */ + + u8 *supp_op_classes; /* Supported Operating Classes element, if + * received, starting from the Length field */ + + u8 rrm_enabled_capa[5]; + + s8 min_tx_power; + s8 max_tx_power; + +#ifdef CONFIG_TAXONOMY + struct wpabuf *probe_ie_taxonomy; + struct wpabuf *assoc_ie_taxonomy; +#endif /* CONFIG_TAXONOMY */ + +#ifdef CONFIG_FILS + u8 fils_snonce[FILS_NONCE_LEN]; + u8 fils_session[FILS_SESSION_LEN]; + u8 fils_erp_pmkid[PMKID_LEN]; + u8 *fils_pending_assoc_req; + size_t fils_pending_assoc_req_len; + unsigned int fils_pending_assoc_is_reassoc:1; + unsigned int fils_dhcp_rapid_commit_proxy:1; + unsigned int fils_erp_pmkid_set:1; + unsigned int fils_drv_assoc_finish:1; + struct wpabuf *fils_hlp_resp; + struct wpabuf *hlp_dhcp_discover; + void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta, + u16 resp, struct wpabuf *data, int pub); +#ifdef CONFIG_FILS_SK_PFS + struct crypto_ecdh *fils_ecdh; +#endif /* CONFIG_FILS_SK_PFS */ + struct wpabuf *fils_dh_ss; + struct wpabuf *fils_g_sta; +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + u8 *owe_pmk; + size_t owe_pmk_len; + struct crypto_ecdh *owe_ecdh; + u16 owe_group; +#endif /* CONFIG_OWE */ + + u8 *ext_capability; + char *ifname_wds; /* WDS ifname, if in use */ + +#ifdef CONFIG_DPP2 + struct dpp_pfs *dpp_pfs; +#endif /* CONFIG_DPP2 */ + +#ifdef CONFIG_TESTING_OPTIONS + enum wpa_alg last_tk_alg; + int last_tk_key_idx; + u8 last_tk[WPA_TK_MAX_LEN]; + size_t last_tk_len; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -180,7 +285,7 @@ * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ #define AP_MAX_INACTIVITY (5 * 60) -#define AP_DISASSOC_DELAY (1) +#define AP_DISASSOC_DELAY (3) #define AP_DEAUTH_DELAY (1) /* Number of seconds to keep STA entry with Authenticated flag after it has * been disassociated. */ @@ -189,8 +294,6 @@ #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) -struct hostapd_data; - int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, void *ctx), @@ -220,9 +323,13 @@ struct sta_info *sta, void *ctx); #endif /* CONFIG_WPS */ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta); +int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, + struct vlan_description *vlan_desc); void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); +const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, + struct sta_info *sta); void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason); @@ -235,7 +342,13 @@ void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, + struct sta_info *sta); int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); +void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta); +int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta); #endif /* STA_INFO_H */ --- contrib/wpa/src/ap/taxonomy.c.orig +++ contrib/wpa/src/ap/taxonomy.c @@ -0,0 +1,292 @@ +/* + * hostapd / Client taxonomy + * Copyright (c) 2015 Google, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * Parse a series of IEs, as in Probe Request or (Re)Association Request frames, + * and render them to a descriptive string. The tag number of standard options + * is written to the string, while the vendor ID and subtag are written for + * vendor options. + * + * Example strings: + * 0,1,50,45,221(00904c,51) + * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2) + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "sta_info.h" +#include "taxonomy.h" + + +/* Copy a string with no funny schtuff allowed; only alphanumerics. */ +static void no_mischief_strncpy(char *dst, const char *src, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + unsigned char s = src[i]; + int is_lower = s >= 'a' && s <= 'z'; + int is_upper = s >= 'A' && s <= 'Z'; + int is_digit = s >= '0' && s <= '9'; + + if (is_lower || is_upper || is_digit) { + /* TODO: if any manufacturer uses Unicode within the + * WPS header, it will get mangled here. */ + dst[i] = s; + } else { + /* Note that even spaces will be transformed to + * underscores, so 'Nexus 7' will turn into 'Nexus_7'. + * This is deliberate, to make the string easier to + * parse. */ + dst[i] = '_'; + } + } +} + + +static int get_wps_name(char *name, size_t name_len, + const u8 *data, size_t data_len) +{ + /* Inside the WPS IE are a series of attributes, using two byte IDs + * and two byte lengths. We're looking for the model name, if + * present. */ + while (data_len >= 4) { + u16 id, elen; + + id = WPA_GET_BE16(data); + elen = WPA_GET_BE16(data + 2); + data += 4; + data_len -= 4; + + if (elen > data_len) + return 0; + + if (id == 0x1023) { + /* Model name, like 'Nexus 7' */ + size_t n = (elen < name_len) ? elen : name_len; + no_mischief_strncpy(name, (const char *) data, n); + return n; + } + + data += elen; + data_len -= elen; + } + + return 0; +} + + +static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies) +{ + char *fpos = fstr; + char *fend = fstr + fstr_len; + char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */ + char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */ + char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */ + char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */ + char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */ + char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */ +#define MAX_EXTCAP 254 + char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL + */ + char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */ +#define WPS_NAME_LEN 32 + char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing + * NUL */ + int num = 0; + const u8 *ie; + size_t ie_len; + int ret; + + os_memset(htcap, 0, sizeof(htcap)); + os_memset(htagg, 0, sizeof(htagg)); + os_memset(htmcs, 0, sizeof(htmcs)); + os_memset(vhtcap, 0, sizeof(vhtcap)); + os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs)); + os_memset(vhttxmcs, 0, sizeof(vhttxmcs)); + os_memset(extcap, 0, sizeof(extcap)); + os_memset(txpow, 0, sizeof(txpow)); + os_memset(wps, 0, sizeof(wps)); + *fpos = '\0'; + + if (!ies) + return; + ie = wpabuf_head(ies); + ie_len = wpabuf_len(ies); + + while (ie_len >= 2) { + u8 id, elen; + char *sep = (num++ == 0) ? "" : ","; + + id = *ie++; + elen = *ie++; + ie_len -= 2; + + if (elen > ie_len) + break; + + if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) { + /* Vendor specific */ + if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) { + /* WPS */ + char model_name[WPS_NAME_LEN + 1]; + const u8 *data = &ie[4]; + size_t data_len = elen - 4; + + os_memset(model_name, 0, sizeof(model_name)); + if (get_wps_name(model_name, WPS_NAME_LEN, data, + data_len)) { + os_snprintf(wps, sizeof(wps), + ",wps:%s", model_name); + } + } + + ret = os_snprintf(fpos, fend - fpos, + "%s%d(%02x%02x%02x,%d)", + sep, id, ie[0], ie[1], ie[2], ie[3]); + } else { + if (id == WLAN_EID_HT_CAP && elen >= 2) { + /* HT Capabilities (802.11n) */ + os_snprintf(htcap, sizeof(htcap), + ",htcap:%04hx", + WPA_GET_LE16(ie)); + } + if (id == WLAN_EID_HT_CAP && elen >= 3) { + /* HT Capabilities (802.11n), A-MPDU information + */ + os_snprintf(htagg, sizeof(htagg), + ",htagg:%02hx", (u16) ie[2]); + } + if (id == WLAN_EID_HT_CAP && elen >= 7) { + /* HT Capabilities (802.11n), MCS information */ + os_snprintf(htmcs, sizeof(htmcs), + ",htmcs:%08hx", + (u16) WPA_GET_LE32(ie + 3)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 4) { + /* VHT Capabilities (802.11ac) */ + os_snprintf(vhtcap, sizeof(vhtcap), + ",vhtcap:%08x", + WPA_GET_LE32(ie)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 8) { + /* VHT Capabilities (802.11ac), RX MCS + * information */ + os_snprintf(vhtrxmcs, sizeof(vhtrxmcs), + ",vhtrxmcs:%08x", + WPA_GET_LE32(ie + 4)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 12) { + /* VHT Capabilities (802.11ac), TX MCS + * information */ + os_snprintf(vhttxmcs, sizeof(vhttxmcs), + ",vhttxmcs:%08x", + WPA_GET_LE32(ie + 8)); + } + if (id == WLAN_EID_EXT_CAPAB) { + /* Extended Capabilities */ + int i; + int len = (elen < MAX_EXTCAP) ? elen : + MAX_EXTCAP; + char *p = extcap; + + p += os_snprintf(extcap, sizeof(extcap), + ",extcap:"); + for (i = 0; i < len; i++) { + int lim; + + lim = sizeof(extcap) - + os_strlen(extcap); + if (lim <= 0) + break; + p += os_snprintf(p, lim, "%02x", + *(ie + i)); + } + } + if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) { + /* TX Power */ + os_snprintf(txpow, sizeof(txpow), + ",txpow:%04hx", + WPA_GET_LE16(ie)); + } + + ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id); + } + if (os_snprintf_error(fend - fpos, ret)) + goto fail; + fpos += ret; + + ie += elen; + ie_len -= elen; + } + + ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s", + htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs, + txpow, extcap, wps); + if (os_snprintf_error(fend - fpos, ret)) { + fail: + fstr[0] = '\0'; + } +} + + +int retrieve_sta_taxonomy(const struct hostapd_data *hapd, + struct sta_info *sta, char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + + if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy) + return 0; + + ret = os_snprintf(buf, buflen, "wifi4|probe:"); + if (os_snprintf_error(buflen, ret)) + return 0; + pos = buf + ret; + end = buf + buflen; + + ie_to_string(pos, end - pos, sta->probe_ie_taxonomy); + pos = os_strchr(pos, '\0'); + if (pos >= end) + return 0; + ret = os_snprintf(pos, end - pos, "|assoc:"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy); + pos = os_strchr(pos, '\0'); + return pos - buf; +} + + +void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(sta->probe_ie_taxonomy); + sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} + + +void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, + struct hostapd_sta_info *info, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(info->probe_ie_taxonomy); + info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} + + +void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(sta->assoc_ie_taxonomy); + sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} --- contrib/wpa/src/ap/taxonomy.h.orig +++ contrib/wpa/src/ap/taxonomy.h @@ -0,0 +1,24 @@ +/* + * hostapd / Station client taxonomy + * Copyright (c) 2015 Google, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TAXONOMY_H +#define TAXONOMY_H + +void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len); +void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, + struct hostapd_sta_info *sta, + const u8 *ie, size_t ie_len); +void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len); +int retrieve_sta_taxonomy(const struct hostapd_data *hapd, + struct sta_info *sta, char *buf, size_t buflen); + +#endif /* TAXONOMY_H */ --- contrib/wpa/src/ap/tkip_countermeasures.c.orig +++ contrib/wpa/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) { --- contrib/wpa/src/ap/vlan.c.orig +++ contrib/wpa/src/ap/vlan.c @@ -0,0 +1,34 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "ap/vlan.h" + +/* compare the two arguments, NULL is treated as empty + * return zero iff they are equal + */ +int vlan_compare(struct vlan_description *a, struct vlan_description *b) +{ + int i; + const int a_empty = !a || !a->notempty; + const int b_empty = !b || !b->notempty; + + if (a_empty && b_empty) + return 0; + if (a_empty || b_empty) + return 1; + if (a->untagged != b->untagged) + return 1; + for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { + if (a->tagged[i] != b->tagged[i]) + return 1; + } + return 0; +} --- contrib/wpa/src/ap/vlan.h.orig +++ contrib/wpa/src/ap/vlan.h @@ -0,0 +1,30 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef VLAN_H +#define VLAN_H + +#define MAX_NUM_TAGGED_VLAN 32 + +struct vlan_description { + int notempty; /* 0 : no vlan information present, 1: else */ + int untagged; /* >0 802.1q vid */ + int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */ +}; + +#ifndef CONFIG_NO_VLAN +int vlan_compare(struct vlan_description *a, struct vlan_description *b); +#else /* CONFIG_NO_VLAN */ +static inline int +vlan_compare(struct vlan_description *a, struct vlan_description *b) +{ + return 0; +} +#endif /* CONFIG_NO_VLAN */ + +#endif /* VLAN_H */ --- contrib/wpa/src/ap/vlan_full.c.orig +++ contrib/wpa/src/ap/vlan_full.c @@ -0,0 +1,801 @@ +/* + * hostapd / VLAN initialization - full dynamic VLAN + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */ +#undef if_type +#include + +#include "utils/common.h" +#include "drivers/priv_netlink.h" +#include "drivers/linux_ioctl.h" +#include "common/linux_bridge.h" +#include "common/linux_vlan.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "wpa_auth.h" +#include "vlan_init.h" +#include "vlan_util.h" + + +struct full_dynamic_vlan { + int s; /* socket on which to listen for new/removed interfaces. */ +}; + +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 + +struct dynamic_iface { + char ifname[IFNAMSIZ + 1]; + int usage; + int clean; + struct dynamic_iface *next; +}; + + +/* Increment ref counter for ifname and add clean flag. + * If not in list, add it only if some flags are given. + */ +static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, + int clean) +{ + struct dynamic_iface *next, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + } + + if (next) { + next->usage++; + next->clean |= clean; + return; + } + + if (!clean) + return; + + next = os_zalloc(sizeof(*next)); + if (!next) + return; + os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); + next->usage = 1; + next->clean = clean; + next->next = *dynamic_ifaces; + *dynamic_ifaces = next; +} + + +/* Decrement reference counter for given ifname. + * Return clean flag iff reference counter was decreased to zero, else zero + */ +static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) +{ + struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + int clean; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + prev = next; + } + + if (!next) + return 0; + + next->usage--; + if (next->usage) + return 0; + + if (prev) + prev->next = next->next; + else + *dynamic_ifaces = next->next; + clean = next->clean; + os_free(next); + + return clean; +} + + +static int ifconfig_down(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); + return ifconfig_helper(if_name, 0); +} + + +/* This value should be 256 ONLY. If it is something else, then hostapd + * might crash!, as this value has been hard-coded in 2.4.x kernel + * bridging code. + */ +#define MAX_BR_PORTS 256 + +static int br_delif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if (linux_br_del_if(fd, br_name, if_name) == 0) + goto done; + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_DEL_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { + /* No error if interface already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + +done: + close(fd); + return 0; +} + + +/* + Add interface 'if_name' to the bridge 'br_name' + + returns -1 on error + returns 1 if the interface is already part of the bridge + returns 0 otherwise +*/ +static int br_addif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if (linux_br_add_if(fd, br_name, if_name) == 0) + goto done; + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_ADD_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + +done: + close(fd); + return 0; +} + + +static int br_delbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if (linux_br_del(fd, br_name) == 0) + goto done; + + arg[0] = BRCTL_DEL_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { + /* No error if bridge already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " + "%s: %s", __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + +done: + close(fd); + return 0; +} + + +/* + Add a bridge with the name 'br_name'. + + returns -1 on error + returns 1 if the bridge already exists + returns 0 otherwise +*/ +static int br_addbr(const char *br_name) +{ + int fd; + unsigned long arg[4]; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if (linux_br_add(fd, br_name) == 0) + goto done; + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } + + arg[0] = BRCTL_ADD_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0) { + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } else { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + } + +done: + /* Decrease forwarding delay to avoid EAPOL timeouts. */ + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); + arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; + arg[1] = 1; + arg[2] = 0; + arg[3] = 0; + ifr.ifr_data = (char *) &arg; + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: " + "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " + "%s: %s", __func__, br_name, strerror(errno)); + /* Continue anyway */ + } + + close(fd); + return 0; +} + + +static int br_getnumports(const char *br_name) +{ + int fd; + int i; + int port_cnt = 0; + unsigned long arg[4]; + int ifindices[MAX_BR_PORTS]; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_GET_PORT_LIST; + arg[1] = (unsigned long) ifindices; + arg[2] = MAX_BR_PORTS; + arg[3] = 0; + + os_memset(ifindices, 0, sizeof(ifindices)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) arg; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + for (i = 1; i < MAX_BR_PORTS; i++) { + if (ifindices[i] > 0) { + port_cnt++; + } + } + + close(fd); + return port_cnt; +} + + +static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, + const char *br_name, int vid, + struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + int clean; + int ret; + + if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); + else + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", + vid); + if (ret >= (int) sizeof(vlan_ifname)) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan_ifname); + + clean = 0; + ifconfig_up(tagged_interface); + if (!vlan_add(tagged_interface, vid, vlan_ifname)) + clean |= DVLAN_CLEAN_VLAN; + + if (!br_addif(br_name, vlan_ifname)) + clean |= DVLAN_CLEAN_VLAN_PORT; + + dyn_iface_get(hapd, vlan_ifname, clean); + + ifconfig_up(vlan_ifname); +} + + +static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, + struct hostapd_vlan *vlan, int vid) +{ + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int ret; + + if (vlan->bridge[0]) { + os_strlcpy(br_name, vlan->bridge, IFNAMSIZ); + ret = 0; + } else if (hapd->conf->vlan_bridge[0]) { + ret = os_snprintf(br_name, IFNAMSIZ, "%s%d", + hapd->conf->vlan_bridge, vid); + } else if (tagged_interface) { + ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d", + tagged_interface, vid); + } else { + ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); + } + if (ret >= IFNAMSIZ) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + br_name); +} + + +static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd, + int vid) +{ + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; + + dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); + + ifconfig_up(br_name); + + if (tagged_interface) + vlan_newlink_tagged(vlan_naming, tagged_interface, br_name, + vid, hapd); +} + + +void vlan_newlink(const char *ifname, struct hostapd_data *hapd) +{ + char br_name[IFNAMSIZ]; + struct hostapd_vlan *vlan; + int untagged, *tagged, i, notempty; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); + + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->configured || + os_strcmp(ifname, vlan->ifname) != 0) + continue; + break; + } + if (!vlan) + return; + + vlan->configured = 1; + + notempty = vlan->vlan_desc.notempty; + untagged = vlan->vlan_desc.untagged; + tagged = vlan->vlan_desc.tagged; + + if (!notempty) { + /* Non-VLAN STA */ + if (hapd->conf->bridge[0] && + !br_addif(hapd->conf->bridge, ifname)) + vlan->clean |= DVLAN_CLEAN_WLAN_PORT; + } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { + vlan_bridge_name(br_name, hapd, vlan, untagged); + + vlan_get_bridge(br_name, hapd, untagged); + + if (!br_addif(br_name, ifname)) + vlan->clean |= DVLAN_CLEAN_WLAN_PORT; + } + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { + if (tagged[i] == untagged || + tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || + (i > 0 && tagged[i] == tagged[i - 1])) + continue; + vlan_bridge_name(br_name, hapd, vlan, tagged[i]); + vlan_get_bridge(br_name, hapd, tagged[i]); + vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, + ifname, br_name, tagged[i], hapd); + } + + ifconfig_up(ifname); +} + + +static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface, + const char *br_name, int vid, + struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + int clean; + int ret; + + if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); + else + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", + vid); + if (ret >= (int) sizeof(vlan_ifname)) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan_ifname); + + + clean = dyn_iface_put(hapd, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN_PORT) + br_delif(br_name, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN) { + ifconfig_down(vlan_ifname); + vlan_rem(vlan_ifname); + } +} + + +static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd, + int vid) +{ + int clean; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; + + if (tagged_interface) + vlan_dellink_tagged(vlan_naming, tagged_interface, br_name, + vid, hapd); + + clean = dyn_iface_put(hapd, br_name); + if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) { + ifconfig_down(br_name); + br_delbr(br_name); + } +} + + +void vlan_dellink(const char *ifname, struct hostapd_data *hapd) +{ + struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); + + first = prev = vlan; + + while (vlan) { + if (os_strcmp(ifname, vlan->ifname) != 0) { + prev = vlan; + vlan = vlan->next; + continue; + } + break; + } + if (!vlan) + return; + + if (vlan->configured) { + int notempty = vlan->vlan_desc.notempty; + int untagged = vlan->vlan_desc.untagged; + int *tagged = vlan->vlan_desc.tagged; + char br_name[IFNAMSIZ]; + int i; + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { + if (tagged[i] == untagged || + tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || + (i > 0 && tagged[i] == tagged[i - 1])) + continue; + vlan_bridge_name(br_name, hapd, vlan, tagged[i]); + vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, + ifname, br_name, tagged[i], hapd); + vlan_put_bridge(br_name, hapd, tagged[i]); + } + + if (!notempty) { + /* Non-VLAN STA */ + if (hapd->conf->bridge[0] && + (vlan->clean & DVLAN_CLEAN_WLAN_PORT)) + br_delif(hapd->conf->bridge, ifname); + } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { + vlan_bridge_name(br_name, hapd, vlan, untagged); + + if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) + br_delif(br_name, vlan->ifname); + + vlan_put_bridge(br_name, hapd, untagged); + } + } + + /* + * Ensure this VLAN interface is actually removed even if + * NEWLINK message is only received later. + */ + if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan)) + wpa_printf(MSG_ERROR, + "VLAN: Could not remove VLAN iface: %s: %s", + vlan->ifname, strerror(errno)); + + if (vlan == first) + hapd->conf->vlan = vlan->next; + else + prev->next = vlan->next; + + os_free(vlan); +} + + +static void +vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, + struct hostapd_data *hapd) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + char ifname[IFNAMSIZ + 1]; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + os_memset(ifname, 0, sizeof(ifname)); + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + int n = attr->rta_len - rta_len; + if (n < 0) + break; + + if ((size_t) n >= sizeof(ifname)) + n = sizeof(ifname) - 1; + os_memcpy(ifname, ((char *) attr) + rta_len, n); + + } + + attr = RTA_NEXT(attr, attrlen); + } + + if (!ifname[0]) + return; + if (del && if_nametoindex(ifname)) { + /* interface still exists, race condition -> + * iface has just been recreated */ + return; + } + + wpa_printf(MSG_DEBUG, + "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", + del ? "DEL" : "NEW", + ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (del) + vlan_dellink(ifname, hapd); + else + vlan_newlink(ifname, hapd); +} + + +static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostapd_data *hapd = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", + __func__, strerror(errno)); + return; + } + + h = (struct nlmsghdr *) buf; + while (NLMSG_OK(h, left)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " + "message: len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + vlan_read_ifnames(h, plen, 0, hapd); + break; + case RTM_DELLINK: + vlan_read_ifnames(h, plen, 1, hapd); + break; + } + + h = NLMSG_NEXT(h, left); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " + "netlink message", __func__, left); + } +} + + +struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd) +{ + struct sockaddr_nl local; + struct full_dynamic_vlan *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + + vlan_set_name_type(hapd->conf->ssid.vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE ? + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : + VLAN_NAME_TYPE_PLUS_VID_NO_PAD); + + priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (priv->s < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," + "NETLINK_ROUTE) failed: %s", + __func__, strerror(errno)); + os_free(priv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", + __func__, strerror(errno)); + close(priv->s); + os_free(priv); + return NULL; + } + + if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) + { + close(priv->s); + os_free(priv); + return NULL; + } + + return priv; +} + + +void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) +{ + if (priv == NULL) + return; + eloop_unregister_read_sock(priv->s); + close(priv->s); + os_free(priv); +} --- contrib/wpa/src/ap/vlan_ifconfig.c.orig +++ contrib/wpa/src/ap/vlan_ifconfig.c @@ -0,0 +1,69 @@ +/* + * hostapd / VLAN ifconfig helpers + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include + +#include "utils/common.h" +#include "vlan_util.h" + + +int ifconfig_helper(const char *if_name, int up) +{ + int fd; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " + "for interface %s: %s", + __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " + "for interface %s (up=%d): %s", + __func__, if_name, up, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +int ifconfig_up(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); + return ifconfig_helper(if_name, 1); +} + + +int iface_exists(const char *ifname) +{ + return if_nametoindex(ifname); +} --- contrib/wpa/src/ap/vlan_init.c.orig +++ contrib/wpa/src/ap/vlan_init.c @@ -9,921 +9,88 @@ */ #include "utils/includes.h" -#ifdef CONFIG_FULL_DYNAMIC_VLAN -#include -#include -#include -#include -#include -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #include "utils/common.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "wpa_auth.h" #include "vlan_init.h" #include "vlan_util.h" -#ifdef CONFIG_FULL_DYNAMIC_VLAN - -#include "drivers/priv_netlink.h" -#include "utils/eloop.h" - - -struct full_dynamic_vlan { - int s; /* socket on which to listen for new/removed interfaces. */ -}; - -#define DVLAN_CLEAN_BR 0x1 -#define DVLAN_CLEAN_VLAN 0x2 -#define DVLAN_CLEAN_VLAN_PORT 0x4 - -struct dynamic_iface { - char ifname[IFNAMSIZ + 1]; - int usage; - int clean; - struct dynamic_iface *next; -}; - - -/* Increment ref counter for ifname and add clean flag. - * If not in list, add it only if some flags are given. - */ -static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, - int clean) +static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan, + int existsok) { - struct dynamic_iface *next, **dynamic_ifaces; - struct hapd_interfaces *interfaces; + int ret, i; - interfaces = hapd->iface->interfaces; - dynamic_ifaces = &interfaces->vlan_priv; - - for (next = *dynamic_ifaces; next; next = next->next) { - if (os_strcmp(ifname, next->ifname) == 0) - break; - } - - if (next) { - next->usage++; - next->clean |= clean; - return; - } - - if (!clean) - return; - - next = os_zalloc(sizeof(*next)); - if (!next) - return; - os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); - next->usage = 1; - next->clean = clean; - next->next = *dynamic_ifaces; - *dynamic_ifaces = next; -} - - -/* Decrement reference counter for given ifname. - * Return clean flag iff reference counter was decreased to zero, else zero - */ -static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) -{ - struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; - struct hapd_interfaces *interfaces; - int clean; - - interfaces = hapd->iface->interfaces; - dynamic_ifaces = &interfaces->vlan_priv; - - for (next = *dynamic_ifaces; next; next = next->next) { - if (os_strcmp(ifname, next->ifname) == 0) - break; - prev = next; - } - - if (!next) - return 0; - - next->usage--; - if (next->usage) - return 0; - - if (prev) - prev->next = next->next; - else - *dynamic_ifaces = next->next; - clean = next->clean; - os_free(next); - - return clean; -} - - -static int ifconfig_helper(const char *if_name, int up) -{ - int fd; - struct ifreq ifr; - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (!hapd->conf->ssid.wep.key[i]) + continue; + wpa_printf(MSG_ERROR, + "VLAN: Refusing to set up VLAN iface %s with WEP", + vlan->ifname); return -1; } - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); - - if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " - "for interface %s: %s", - __func__, if_name, strerror(errno)); - close(fd); + if (!iface_exists(vlan->ifname)) + ret = hostapd_vlan_if_add(hapd, vlan->ifname); + else if (!existsok) return -1; - } - - if (up) - ifr.ifr_flags |= IFF_UP; else - ifr.ifr_flags &= ~IFF_UP; + ret = 0; - if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " - "for interface %s (up=%d): %s", - __func__, if_name, up, strerror(errno)); - close(fd); - return -1; - } + if (ret) + return ret; - close(fd); - return 0; -} + ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */ + if (hapd->wpa_auth) + ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id); -static int ifconfig_up(const char *if_name) -{ - wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); - return ifconfig_helper(if_name, 1); -} + if (ret == 0) + return ret; + wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)", + vlan->vlan_id, ret); + if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id)) + wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname); -static int ifconfig_down(const char *if_name) -{ - wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); - return ifconfig_helper(if_name, 0); -} + /* group state machine setup failed */ + if (hostapd_vlan_if_remove(hapd, vlan->ifname)) + wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname); - -/* - * These are only available in recent linux headers (without the leading - * underscore). - */ -#define _GET_VLAN_REALDEV_NAME_CMD 8 -#define _GET_VLAN_VID_CMD 9 - -/* This value should be 256 ONLY. If it is something else, then hostapd - * might crash!, as this value has been hard-coded in 2.4.x kernel - * bridging code. - */ -#define MAX_BR_PORTS 256 - -static int br_delif(const char *br_name, const char *if_name) -{ - int fd; - struct ifreq ifr; - unsigned long args[2]; - int if_index; - - wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - if_index = if_nametoindex(if_name); - - if (if_index == 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " - "interface index for '%s'", - __func__, if_name); - close(fd); - return -1; - } - - args[0] = BRCTL_DEL_IF; - args[1] = if_index; - - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) args; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { - /* No error if interface already removed. */ - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," - "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " - "%s", __func__, br_name, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; + return ret; } -/* - Add interface 'if_name' to the bridge 'br_name' - - returns -1 on error - returns 1 if the interface is already part of the bridge - returns 0 otherwise -*/ -static int br_addif(const char *br_name, const char *if_name) +int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan) { - int fd; - struct ifreq ifr; - unsigned long args[2]; - int if_index; + int ret; - wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } + ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id); + if (ret) + wpa_printf(MSG_ERROR, + "WPA deinitialization for VLAN %d failed (%d)", + vlan->vlan_id, ret); - if_index = if_nametoindex(if_name); - - if (if_index == 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " - "interface index for '%s'", - __func__, if_name); - close(fd); - return -1; - } - - args[0] = BRCTL_ADD_IF; - args[1] = if_index; - - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) args; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - if (errno == EBUSY) { - /* The interface is already added. */ - close(fd); - return 1; - } - - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," - "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " - "%s", __func__, br_name, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; + return hostapd_vlan_if_remove(hapd, vlan->ifname); } -static int br_delbr(const char *br_name) -{ - int fd; - unsigned long arg[2]; - - wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_DEL_BRIDGE; - arg[1] = (unsigned long) br_name; - - if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { - /* No error if bridge already removed. */ - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " - "%s: %s", __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -/* - Add a bridge with the name 'br_name'. - - returns -1 on error - returns 1 if the bridge already exists - returns 0 otherwise -*/ -static int br_addbr(const char *br_name) -{ - int fd; - unsigned long arg[4]; - struct ifreq ifr; - - wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_ADD_BRIDGE; - arg[1] = (unsigned long) br_name; - - if (ioctl(fd, SIOCGIFBR, arg) < 0) { - if (errno == EEXIST) { - /* The bridge is already added. */ - close(fd); - return 1; - } else { - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " - "failed for %s: %s", - __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - } - - /* Decrease forwarding delay to avoid EAPOL timeouts. */ - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); - arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; - arg[1] = 1; - arg[2] = 0; - arg[3] = 0; - ifr.ifr_data = (char *) &arg; - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: " - "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " - "%s: %s", __func__, br_name, strerror(errno)); - /* Continue anyway */ - } - - close(fd); - return 0; -} - - -static int br_getnumports(const char *br_name) -{ - int fd; - int i; - int port_cnt = 0; - unsigned long arg[4]; - int ifindices[MAX_BR_PORTS]; - struct ifreq ifr; - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_GET_PORT_LIST; - arg[1] = (unsigned long) ifindices; - arg[2] = MAX_BR_PORTS; - arg[3] = 0; - - os_memset(ifindices, 0, sizeof(ifindices)); - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) arg; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " - "failed for %s: %s", - __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - - for (i = 1; i < MAX_BR_PORTS; i++) { - if (ifindices[i] > 0) { - port_cnt++; - } - } - - close(fd); - return port_cnt; -} - - -#ifndef CONFIG_VLAN_NETLINK - -int vlan_rem(const char *if_name) -{ - int fd; - struct vlan_ioctl_args if_request; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); - if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { - wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", - if_name); - return -1; - } - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&if_request, 0, sizeof(if_request)); - - os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); - if_request.cmd = DEL_VLAN_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " - "%s", __func__, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -/* - Add a vlan interface with VLAN ID 'vid' and tagged interface - 'if_name'. - - returns -1 on error - returns 1 if the interface already exists - returns 0 otherwise -*/ -int vlan_add(const char *if_name, int vid, const char *vlan_if_name) -{ - int fd; - struct vlan_ioctl_args if_request; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", - if_name, vid); - ifconfig_up(if_name); - - if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { - wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", - if_name); - return -1; - } - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&if_request, 0, sizeof(if_request)); - - /* Determine if a suitable vlan device already exists. */ - - os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", - vid); - - if_request.cmd = _GET_VLAN_VID_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { - - if (if_request.u.VID == vid) { - if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && - os_strncmp(if_request.u.device2, if_name, - sizeof(if_request.u.device2)) == 0) { - close(fd); - wpa_printf(MSG_DEBUG, "VLAN: vlan_add: " - "if_name %s exists already", - if_request.device1); - return 1; - } - } - } - - /* A suitable vlan device does not already exist, add one. */ - - os_memset(&if_request, 0, sizeof(if_request)); - os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); - if_request.u.VID = vid; - if_request.cmd = ADD_VLAN_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: " - "%s", - __func__, if_request.device1, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -static int vlan_set_name_type(unsigned int name_type) -{ - int fd; - struct vlan_ioctl_args if_request; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", - name_type); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&if_request, 0, sizeof(if_request)); - - if_request.u.name_type = name_type; - if_request.cmd = SET_VLAN_NAME_TYPE_CMD; - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD " - "name_type=%u failed: %s", - __func__, name_type, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - -#endif /* CONFIG_VLAN_NETLINK */ - - -static void vlan_newlink(char *ifname, struct hostapd_data *hapd) -{ - char vlan_ifname[IFNAMSIZ]; - char br_name[IFNAMSIZ]; - struct hostapd_vlan *vlan = hapd->conf->vlan; - char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; - int vlan_naming = hapd->conf->ssid.vlan_naming; - int clean; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); - - while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) { - vlan->configured = 1; - - if (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, sizeof(br_name), "%s%d", - hapd->conf->vlan_bridge, - vlan->vlan_id); - } else if (tagged_interface) { - os_snprintf(br_name, sizeof(br_name), - "br%s.%d", tagged_interface, - vlan->vlan_id); - } else { - os_snprintf(br_name, sizeof(br_name), - "brvlan%d", vlan->vlan_id); - } - - dyn_iface_get(hapd, br_name, - br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); - - ifconfig_up(br_name); - - if (tagged_interface) { - if (vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "%s.%d", tagged_interface, - vlan->vlan_id); - else - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - - clean = 0; - ifconfig_up(tagged_interface); - if (!vlan_add(tagged_interface, vlan->vlan_id, - vlan_ifname)) - clean |= DVLAN_CLEAN_VLAN; - - if (!br_addif(br_name, vlan_ifname)) - clean |= DVLAN_CLEAN_VLAN_PORT; - - dyn_iface_get(hapd, vlan_ifname, clean); - - ifconfig_up(vlan_ifname); - } - - if (!br_addif(br_name, ifname)) - vlan->clean |= DVLAN_CLEAN_WLAN_PORT; - - ifconfig_up(ifname); - - break; - } - vlan = vlan->next; - } -} - - -static void vlan_dellink(char *ifname, struct hostapd_data *hapd) -{ - char vlan_ifname[IFNAMSIZ]; - char br_name[IFNAMSIZ]; - struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; - char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; - int vlan_naming = hapd->conf->ssid.vlan_naming; - int clean; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); - - first = prev = vlan; - - while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0 && - vlan->configured) { - if (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, sizeof(br_name), "%s%d", - hapd->conf->vlan_bridge, - vlan->vlan_id); - } else if (tagged_interface) { - os_snprintf(br_name, sizeof(br_name), - "br%s.%d", tagged_interface, - vlan->vlan_id); - } else { - os_snprintf(br_name, sizeof(br_name), - "brvlan%d", vlan->vlan_id); - } - - if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) - br_delif(br_name, vlan->ifname); - - if (tagged_interface) { - if (vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "%s.%d", tagged_interface, - vlan->vlan_id); - else - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - - clean = dyn_iface_put(hapd, vlan_ifname); - - if (clean & DVLAN_CLEAN_VLAN_PORT) - br_delif(br_name, vlan_ifname); - - if (clean & DVLAN_CLEAN_VLAN) { - ifconfig_down(vlan_ifname); - vlan_rem(vlan_ifname); - } - } - - clean = dyn_iface_put(hapd, br_name); - if ((clean & DVLAN_CLEAN_BR) && - br_getnumports(br_name) == 0) { - ifconfig_down(br_name); - br_delbr(br_name); - } - } - - if (os_strcmp(ifname, vlan->ifname) == 0) { - if (vlan == first) { - hapd->conf->vlan = vlan->next; - } else { - prev->next = vlan->next; - } - os_free(vlan); - - break; - } - prev = vlan; - vlan = vlan->next; - } -} - - -static void -vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, - struct hostapd_data *hapd) -{ - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr *attr; - char ifname[IFNAMSIZ + 1]; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - if (attrlen < 0) - return; - - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); - - os_memset(ifname, 0, sizeof(ifname)); - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - int n = attr->rta_len - rta_len; - if (n < 0) - break; - - if ((size_t) n >= sizeof(ifname)) - n = sizeof(ifname) - 1; - os_memcpy(ifname, ((char *) attr) + rta_len, n); - - } - - attr = RTA_NEXT(attr, attrlen); - } - - if (!ifname[0]) - return; - if (del && if_nametoindex(ifname)) { - /* interface still exists, race condition -> - * iface has just been recreated */ - return; - } - - wpa_printf(MSG_DEBUG, - "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", - del ? "DEL" : "NEW", - ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, - (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", - (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", - (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", - (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - - if (del) - vlan_dellink(ifname, hapd); - else - vlan_newlink(ifname, hapd); -} - - -static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) -{ - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - struct hostapd_data *hapd = eloop_ctx; - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", - __func__, strerror(errno)); - return; - } - - h = (struct nlmsghdr *) buf; - while (NLMSG_OK(h, left)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " - "message: len=%d left=%d plen=%d", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - vlan_read_ifnames(h, plen, 0, hapd); - break; - case RTM_DELLINK: - vlan_read_ifnames(h, plen, 1, hapd); - break; - } - - h = NLMSG_NEXT(h, left); - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " - "netlink message", __func__, left); - } -} - - -static struct full_dynamic_vlan * -full_dynamic_vlan_init(struct hostapd_data *hapd) -{ - struct sockaddr_nl local; - struct full_dynamic_vlan *priv; - - priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; - -#ifndef CONFIG_VLAN_NETLINK - vlan_set_name_type(hapd->conf->ssid.vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE ? - VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : - VLAN_NAME_TYPE_PLUS_VID_NO_PAD); -#endif /* CONFIG_VLAN_NETLINK */ - - priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (priv->s < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," - "NETLINK_ROUTE) failed: %s", - __func__, strerror(errno)); - os_free(priv); - return NULL; - } - - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", - __func__, strerror(errno)); - close(priv->s); - os_free(priv); - return NULL; - } - - if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) - { - close(priv->s); - os_free(priv); - return NULL; - } - - return priv; -} - - -static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) -{ - if (priv == NULL) - return; - eloop_unregister_read_sock(priv->s); - close(priv->s); - os_free(priv); -} -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ - - -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan) -{ - int i; - - if (dyn_vlan == NULL) - return 0; - - /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own - * functions for setting up dynamic broadcast keys. */ - for (i = 0; i < 4; i++) { - if (hapd->conf->ssid.wep.key[i] && - hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, - i == hapd->conf->ssid.wep.idx, NULL, 0, - hapd->conf->ssid.wep.key[i], - hapd->conf->ssid.wep.len[i])) - { - wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " - "encryption for dynamic VLAN"); - return -1; - } - } - - return 0; -} - - static int vlan_dynamic_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan) { while (vlan) { if (vlan->vlan_id != VLAN_ID_WILDCARD) { - if (hostapd_vlan_if_add(hapd, vlan->ifname)) { - if (errno != EEXIST) { - wpa_printf(MSG_ERROR, "VLAN: Could " - "not add VLAN %s: %s", - vlan->ifname, - strerror(errno)); - return -1; - } + if (vlan_if_add(hapd, vlan, 1)) { + wpa_printf(MSG_ERROR, + "VLAN: Could not add VLAN %s: %s", + vlan->ifname, strerror(errno)); + return -1; } #ifdef CONFIG_FULL_DYNAMIC_VLAN - ifconfig_up(vlan->ifname); + vlan_newlink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ } @@ -942,15 +109,17 @@ while (vlan) { next = vlan->next; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + /* vlan_dellink() takes care of cleanup and interface removal */ + if (vlan->vlan_id != VLAN_ID_WILDCARD) + vlan_dellink(vlan->ifname, hapd); +#else /* CONFIG_FULL_DYNAMIC_VLAN */ if (vlan->vlan_id != VLAN_ID_WILDCARD && - hostapd_vlan_if_remove(hapd, vlan->ifname)) { + vlan_if_remove(hapd, vlan)) { wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " "iface: %s: %s", vlan->ifname, strerror(errno)); } -#ifdef CONFIG_FULL_DYNAMIC_VLAN - if (vlan->clean) - vlan_dellink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ vlan = next; @@ -964,10 +133,13 @@ hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED || + hapd->conf->ssid.per_sta_vif) && !hapd->conf->vlan) { /* dynamic vlans enabled but no (or empty) vlan_file given */ struct hostapd_vlan *vlan; + int ret; + vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while assigning " @@ -976,8 +148,16 @@ } vlan->vlan_id = VLAN_ID_WILDCARD; - os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", - hapd->conf->iface); + ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", + hapd->conf->iface); + if (ret >= (int) sizeof(vlan->ifname)) { + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan->ifname); + } else if (ret < 0) { + os_free(vlan); + return ret; + } vlan->next = hapd->conf->vlan; hapd->conf->vlan = vlan; } @@ -1002,50 +182,51 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, - int vlan_id) + int vlan_id, + struct vlan_description *vlan_desc) { - struct hostapd_vlan *n = NULL; - char *ifname, *pos; + struct hostapd_vlan *n; + char ifname[IFNAMSIZ + 1], *pos; + int ret; - if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || - vlan->vlan_id != VLAN_ID_WILDCARD) + if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD) return NULL; wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)", __func__, vlan_id, vlan->ifname); - ifname = os_strdup(vlan->ifname); - if (ifname == NULL) - return NULL; + os_strlcpy(ifname, vlan->ifname, sizeof(ifname)); pos = os_strchr(ifname, '#'); if (pos == NULL) - goto free_ifname; + return NULL; *pos++ = '\0'; n = os_zalloc(sizeof(*n)); if (n == NULL) - goto free_ifname; + return NULL; n->vlan_id = vlan_id; + if (vlan_desc) + n->vlan_desc = *vlan_desc; n->dynamic_vlan = 1; - os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, - pos); - - if (hostapd_vlan_if_add(hapd, n->ifname)) { + ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", + ifname, vlan_id, pos); + if (os_snprintf_error(sizeof(n->ifname), ret)) { os_free(n); - n = NULL; - goto free_ifname; + return NULL; } + os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge)); n->next = hapd->conf->vlan; hapd->conf->vlan = n; -#ifdef CONFIG_FULL_DYNAMIC_VLAN - ifconfig_up(n->ifname); -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + /* hapd->conf->vlan needs this new VLAN here for WPA setup */ + if (vlan_if_add(hapd, n, 0)) { + hapd->conf->vlan = n->next; + os_free(n); + n = NULL; + } -free_ifname: - os_free(ifname); return n; } @@ -1054,7 +235,7 @@ { struct hostapd_vlan *vlan; - if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) + if (vlan_id <= 0) return 1; wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)", @@ -1073,7 +254,7 @@ return 1; if (vlan->dynamic_vlan == 0) { - hostapd_vlan_if_remove(hapd, vlan->ifname); + vlan_if_remove(hapd, vlan); #ifdef CONFIG_FULL_DYNAMIC_VLAN vlan_dellink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ --- contrib/wpa/src/ap/vlan_init.h.orig +++ contrib/wpa/src/ap/vlan_init.h @@ -15,10 +15,9 @@ void vlan_deinit(struct hostapd_data *hapd); struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, - int vlan_id); + int vlan_id, + struct vlan_description *vlan_desc); int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - const char *dyn_vlan); #else /* CONFIG_NO_VLAN */ static inline int vlan_init(struct hostapd_data *hapd) { @@ -29,9 +28,9 @@ { } -static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, - struct hostapd_vlan *vlan, - int vlan_id) +static inline struct hostapd_vlan * +vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, + int vlan_id, struct vlan_description *vlan_desc) { return NULL; } @@ -40,12 +39,6 @@ { return -1; } - -static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - const char *dyn_vlan) -{ - return -1; -} #endif /* CONFIG_NO_VLAN */ #endif /* VLAN_INIT_H */ --- contrib/wpa/src/ap/vlan_ioctl.c.orig +++ contrib/wpa/src/ap/vlan_ioctl.c @@ -0,0 +1,155 @@ +/* + * hostapd / VLAN ioctl API + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include + +#include "utils/common.h" +#include "common/linux_vlan.h" +#include "vlan_util.h" + + +int vlan_rem(const char *if_name) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.cmd = DEL_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " + "%s", __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a vlan interface with VLAN ID 'vid' and tagged interface + 'if_name'. + + returns -1 on error + returns 1 if the interface already exists + returns 0 otherwise +*/ +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", + if_name, vid); + ifconfig_up(if_name); + + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + /* Determine if a suitable vlan device already exists. */ + + os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", + vid); + + if_request.cmd = GET_VLAN_VID_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + if_request.u.VID == vid) { + if_request.cmd = GET_VLAN_REALDEV_NAME_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + os_strncmp(if_request.u.device2, if_name, + sizeof(if_request.u.device2)) == 0) { + close(fd); + wpa_printf(MSG_DEBUG, + "VLAN: vlan_add: if_name %s exists already", + if_request.device1); + return 1; + } + } + + /* A suitable vlan device does not already exist, add one. */ + + os_memset(&if_request, 0, sizeof(if_request)); + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.u.VID = vid; + if_request.cmd = ADD_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: ADD_VLAN_CMD failed for %s: %s", + __func__, if_request.device1, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +int vlan_set_name_type(unsigned int name_type) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", + name_type); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: socket(AF_INET,SOCK_STREAM) failed: %s", + __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + if_request.u.name_type = name_type; + if_request.cmd = SET_VLAN_NAME_TYPE_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s", + __func__, name_type, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} --- contrib/wpa/src/ap/vlan_util.c.orig +++ contrib/wpa/src/ap/vlan_util.c @@ -7,18 +7,10 @@ */ #include "utils/includes.h" -#include -#include -#include -#include -#include -#include #include #include #include "utils/common.h" -#include "utils/eloop.h" -#include "hostapd.h" #include "vlan_util.h" /* @@ -33,7 +25,6 @@ { int err, ret = -1; struct nl_sock *handle = NULL; - struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; int if_idx = 0; @@ -65,22 +56,19 @@ goto vlan_add_error; } - err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + err = rtnl_link_get_kernel(handle, 0, if_name, &rlink); if (err < 0) { - cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", - nl_geterror(err)); - goto vlan_add_error; - } - - if (!(if_idx = rtnl_link_name2i(cache, if_name))) { /* link does not exist */ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist", if_name); goto vlan_add_error; } + if_idx = rtnl_link_get_ifindex(rlink); + rtnl_link_put(rlink); + rlink = NULL; - if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { + err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink); + if (err >= 0) { /* link does exist */ rtnl_link_put(rlink); rlink = NULL; @@ -127,8 +115,6 @@ vlan_add_error: if (rlink) rtnl_link_put(rlink); - if (cache) - nl_cache_free(cache); if (handle) nl_socket_free(handle); return ret; @@ -139,7 +125,6 @@ { int err, ret = -1; struct nl_sock *handle = NULL; - struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name); @@ -157,15 +142,8 @@ goto vlan_rem_error; } - err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + err = rtnl_link_get_kernel(handle, 0, if_name, &rlink); if (err < 0) { - cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", - nl_geterror(err)); - goto vlan_rem_error; - } - - if (!(rlink = rtnl_link_get_by_name(cache, if_name))) { /* link does not exist */ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists", if_name); @@ -184,9 +162,13 @@ vlan_rem_error: if (rlink) rtnl_link_put(rlink); - if (cache) - nl_cache_free(cache); if (handle) nl_socket_free(handle); return ret; } + + +int vlan_set_name_type(unsigned int name_type) +{ + return 0; +} --- contrib/wpa/src/ap/vlan_util.h.orig +++ contrib/wpa/src/ap/vlan_util.h @@ -1,5 +1,5 @@ /* - * hostapd / VLAN netlink api + * hostapd / VLAN netlink/ioctl api * Copyright (c) 2012, Michael Braun * * This software may be distributed under the terms of the BSD license. @@ -9,7 +9,23 @@ #ifndef VLAN_UTIL_H #define VLAN_UTIL_H +struct hostapd_data; +struct hostapd_vlan; +struct full_dynamic_vlan; + int vlan_add(const char *if_name, int vid, const char *vlan_if_name); int vlan_rem(const char *if_name); +int vlan_set_name_type(unsigned int name_type); +int ifconfig_helper(const char *if_name, int up); +int ifconfig_up(const char *if_name); +int iface_exists(const char *ifname); +int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan); + +struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd); +void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv); +void vlan_newlink(const char *ifname, struct hostapd_data *hapd); +void vlan_dellink(const char *ifname, struct hostapd_data *hapd); + #endif /* VLAN_UTIL_H */ --- contrib/wpa/src/ap/wmm.c.orig +++ contrib/wpa/src/ap/wmm.c @@ -21,11 +21,6 @@ #include "wmm.h" -/* TODO: maintain separate sequence and fragment numbers for each AC - * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA - * if only WMM stations are receiving a certain group */ - - static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) { u8 ret; @@ -157,8 +152,9 @@ int wmm_process_tspec(struct wmm_tspec_element *tspec) { - int medium_time, pps, duration; - int up, psb, dir, tid; + u64 medium_time; + unsigned int pps, duration; + unsigned int up, psb, dir, tid; u16 val, surplus; up = (tspec->ts_info[1] >> 3) & 0x07; @@ -206,8 +202,9 @@ return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } - medium_time = surplus * pps * duration / 0x2000; - wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); + medium_time = (u64) surplus * pps * duration / 0x2000; + wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu", + (unsigned long) medium_time); /* * TODO: store list of granted (and still active) TSPECs and check --- contrib/wpa/src/ap/wnm_ap.c.orig +++ contrib/wpa/src/ap/wnm_ap.c @@ -12,11 +12,13 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/ocv.h" #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" #include "ap/wpa_auth.h" +#include "mbo_ap.h" #include "wnm_ap.h" #define MAX_TFS_IE_LEN 1024 @@ -53,8 +55,8 @@ size_t gtk_elem_len = 0; size_t igtk_elem_len = 0; struct wnm_sleep_element wnmsleep_ie; - u8 *wnmtfs_ie; - u8 wnmsleep_ie_len; + u8 *wnmtfs_ie, *oci_ie; + u8 wnmsleep_ie_len, oci_ie_len; u16 wnmtfs_ie_len; u8 *pos; struct sta_info *sta; @@ -87,14 +89,47 @@ wnmtfs_ie = NULL; } + oci_ie = NULL; + oci_ie_len = 0; +#ifdef CONFIG_OCV + if (action_type == WNM_SLEEP_MODE_EXIT && + wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in WNM-Sleep Mode frame"); + os_free(wnmtfs_ie); + return -1; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame"); + os_free(wnmtfs_ie); + return -1; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(wnmtfs_ie); + os_free(oci_ie); + return -1; + } + } +#endif /* CONFIG_OCV */ + #define MAX_GTK_SUBELEM_LEN 45 #define MAX_IGTK_SUBELEM_LEN 26 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + - MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN + + oci_ie_len); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); - return -1; + res = -1; + goto fail; } os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); @@ -107,6 +142,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 { @@ -116,11 +152,8 @@ (int) gtk_elem_len); #ifdef CONFIG_IEEE80211W res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); - if (res < 0) { - os_free(wnmtfs_ie); - os_free(mgmt); - return -1; - } + if (res < 0) + goto fail; igtk_elem_len = res; pos += igtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", @@ -134,11 +167,18 @@ os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ pos += wnmsleep_ie_len; - if (wnmtfs_ie) + if (wnmtfs_ie) { os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); + pos += wnmtfs_ie_len; + } +#ifdef CONFIG_OCV + /* copy OCV OCI here */ + if (oci_ie_len > 0) + os_memcpy(pos, oci_ie, oci_ie_len); +#endif /* CONFIG_OCV */ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + - igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len; /* In driver, response frame should be forced to sent when STA is in * PS mode */ @@ -174,7 +214,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 @@ -182,7 +223,9 @@ #undef MAX_GTK_SUBELEM_LEN #undef MAX_IGTK_SUBELEM_LEN +fail: os_free(wnmtfs_ie); + os_free(oci_ie); os_free(mgmt); return res; } @@ -199,18 +242,44 @@ u8 *tfsreq_ie_start = NULL; u8 *tfsreq_ie_end = NULL; u16 tfsreq_ie_len = 0; +#ifdef CONFIG_OCV + struct sta_info *sta; + const u8 *oci_ie = NULL; + u8 oci_ie_len = 0; +#endif /* CONFIG_OCV */ + 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; + } + + if (len < 1) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore too short WNM-Sleep Mode Request from " + MACSTR, MAC2STR(addr)); + return; + } + dialog_token = *pos++; while (pos + 1 < frm + len) { u8 ie_len = pos[1]; if (pos + 2 + ie_len > frm + len) break; - if (*pos == WLAN_EID_WNMSLEEP) + if (*pos == WLAN_EID_WNMSLEEP && + ie_len >= (int) sizeof(*wnmsleep_ie) - 2) wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_REQ) { if (!tfsreq_ie_start) tfsreq_ie_start = (u8 *) pos; tfsreq_ie_end = (u8 *) pos; +#ifdef CONFIG_OCV + } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 && + pos[2] == WLAN_EID_EXT_OCV_OCI) { + oci_ie = pos + 3; + oci_ie_len = ie_len - 1; +#endif /* CONFIG_OCV */ } else wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", *pos); @@ -222,6 +291,27 @@ return; } +#ifdef CONFIG_OCV + sta = ap_get_sta(hapd, addr); + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT && + sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame"); + return; + } + + if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && tfsreq_ie_start && tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) { @@ -249,20 +339,14 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, const u8 *addr, - u8 dialog_token, - const char *url) + u8 dialog_token) { struct ieee80211_mgmt *mgmt; - size_t url_len, len; + size_t len; u8 *pos; int res; - if (url) - url_len = os_strlen(url); - else - url_len = 0; - - mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); + mgmt = os_zalloc(sizeof(*mgmt)); if (mgmt == NULL) return -1; os_memcpy(mgmt->da, addr, ETH_ALEN); @@ -277,11 +361,6 @@ mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); mgmt->u.action.u.bss_tm_req.validity_interval = 1; pos = mgmt->u.action.u.bss_tm_req.variable; - if (url) { - *pos++ += url_len; - os_memcpy(pos, url, url_len); - pos += url_len; - } wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " @@ -305,7 +384,21 @@ { 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 " MACSTR, MAC2STR(addr)); @@ -324,10 +417,23 @@ wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", pos, end - pos); - ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); + ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token); } +void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (sta->agreed_to_steer) { + wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->agreed_to_steer = 0; + } +} + + static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, const u8 *addr, const u8 *frm, size_t len) @@ -334,7 +440,22 @@ { 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 " MACSTR, MAC2STR(addr)); @@ -352,11 +473,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 @@ -366,6 +499,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); @@ -376,6 +510,71 @@ } +static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *buf, + size_t len) +{ + u8 dialog_token, type; + + if (len < 2) + return; + dialog_token = *buf++; + type = *buf++; + len -= 2; + + wpa_printf(MSG_DEBUG, + "WNM: Received WNM Notification Request frame from " + MACSTR " (dialog_token=%u type=%u)", + MAC2STR(addr), dialog_token, type); + wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements", + buf, len); + if (type == WLAN_EID_VENDOR_SPECIFIC) + mbo_ap_wnm_notification_req(hapd, addr, buf, len); +} + + +static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd, + const u8 *addr, const u8 *buf, + size_t len) +{ + u8 dialog_token; + char *hex; + size_t hex_len; + + if (!hapd->conf->coloc_intf_reporting) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore unexpected Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + + if (len < 1) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore too short Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + dialog_token = *buf++; + len--; + + wpa_printf(MSG_DEBUG, + "WNM: Received Collocated Interference Report frame from " + MACSTR " (dialog_token=%u)", + MAC2STR(addr), dialog_token); + wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements", + buf, len); + + hex_len = 2 * len + 1; + hex = os_malloc(hex_len); + if (!hex) + return; + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s", + MAC2STR(addr), dialog_token, hex); + os_free(hex); +} + + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -402,6 +601,14 @@ case WNM_SLEEP_MODE_REQ: ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); return 0; + case WNM_NOTIFICATION_REQ: + ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, + plen); + return 0; + case WNM_COLLOCATED_INTERFERENCE_REPORT: + ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload, + plen); + return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, @@ -527,7 +734,8 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, u8 req_mode, int disassoc_timer, u8 valid_int, const u8 *bss_term_dur, const char *url, - const u8 *nei_rep, size_t nei_rep_len) + const u8 *nei_rep, size_t nei_rep_len, + const u8 *mbo_attrs, size_t mbo_len) { u8 *buf, *pos; struct ieee80211_mgmt *mgmt; @@ -536,7 +744,7 @@ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x", MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int); - buf = os_zalloc(1000 + nei_rep_len); + buf = os_zalloc(1000 + nei_rep_len + mbo_len); if (buf == NULL) return -1; mgmt = (struct ieee80211_mgmt *) buf; @@ -579,6 +787,11 @@ pos += nei_rep_len; } + if (mbo_len > 0) { + pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs, + mbo_len); + } + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { wpa_printf(MSG_DEBUG, "Failed to send BSS Transition Management Request frame"); @@ -594,3 +807,40 @@ return 0; } + + +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout) +{ + u8 buf[100], *pos; + struct ieee80211_mgmt *mgmt; + u8 dialog_token = 1; + + if (auto_report > 3 || timeout > 63) + return -1; + os_memset(buf, 0, sizeof(buf)); + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.coloc_intf_req.action = + WNM_COLLOCATED_INTERFERENCE_REQ; + mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token; + mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2); + pos = &mgmt->u.action.u.coloc_intf_req.req_info; + pos++; + + wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to " + MACSTR " (dialog_token=%u auto_report=%u timeout=%u)", + MAC2STR(sta->addr), dialog_token, auto_report, timeout); + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, + "WNM: Failed to send Collocated Interference Request frame"); + return -1; + } + + return 0; +} --- contrib/wpa/src/ap/wnm_ap.h.orig +++ contrib/wpa/src/ap/wnm_ap.h @@ -21,6 +21,10 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, u8 req_mode, int disassoc_timer, u8 valid_int, const u8 *bss_term_dur, const char *url, - const u8 *nei_rep, size_t nei_rep_len); + const u8 *nei_rep, size_t nei_rep_len, + const u8 *mbo_attrs, size_t mbo_len); +void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx); +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout); #endif /* WNM_AP_H */ --- contrib/wpa/src/ap/wpa_auth.c.orig +++ contrib/wpa/src/ap/wpa_auth.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 RSN / WPA Authenticator - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,12 +13,17 @@ #include "utils/state_machine.h" #include "utils/bitfield.h" #include "common/ieee802_11_defs.h" +#include "common/ocv.h" +#include "crypto/aes.h" #include "crypto/aes_wrap.h" +#include "crypto/aes_siv.h" #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" #include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" +#include "drivers/driver.h" #include "ap_config.h" #include "ieee802_11.h" #include "wpa_auth.h" @@ -33,8 +38,14 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_sm_step(struct wpa_state_machine *sm); -static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, - size_t data_len); +static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, + u8 *data, size_t data_len); +#ifdef CONFIG_FILS +static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk, + u8 *buf, size_t buf_len, u16 *_key_data_len); +static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, + const struct wpabuf *hlp); +#endif /* CONFIG_FILS */ static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); @@ -44,7 +55,8 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, - const u8 *pmk, struct wpa_ptk *ptk); + const u8 *pmk, unsigned int pmk_len, + struct wpa_ptk *ptk); static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_get(struct wpa_authenticator *wpa_auth, @@ -51,12 +63,12 @@ struct wpa_group *group); static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); -static const u32 dot11RSNAConfigGroupUpdateCount = 4; -static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ static const u32 eapol_key_timeout_first_group = 500; /* ms */ +static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; @@ -67,8 +79,8 @@ static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { - if (wpa_auth->cb.mic_failure_report) - return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + if (wpa_auth->cb->mic_failure_report) + return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr); return 0; } @@ -76,8 +88,8 @@ static inline void wpa_auth_psk_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { - if (wpa_auth->cb.psk_failure_report) - wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr); + if (wpa_auth->cb->psk_failure_report) + wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr); } @@ -85,8 +97,8 @@ const u8 *addr, wpa_eapol_variable var, int value) { - if (wpa_auth->cb.set_eapol) - wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); + if (wpa_auth->cb->set_eapol) + wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value); } @@ -93,9 +105,9 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var) { - if (wpa_auth->cb.get_eapol == NULL) + if (wpa_auth->cb->get_eapol == NULL) return -1; - return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); + return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var); } @@ -102,12 +114,13 @@ static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk) + const u8 *prev_psk, size_t *psk_len, + int *vlan_id) { - if (wpa_auth->cb.get_psk == NULL) + if (wpa_auth->cb->get_psk == NULL) return NULL; - return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, - prev_psk); + return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, + prev_psk, psk_len, vlan_id); } @@ -114,9 +127,9 @@ static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, const u8 *addr, u8 *msk, size_t *len) { - if (wpa_auth->cb.get_msk == NULL) + if (wpa_auth->cb->get_msk == NULL) return -1; - return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); + return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len); } @@ -125,10 +138,10 @@ enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { - if (wpa_auth->cb.set_key == NULL) + if (wpa_auth->cb->set_key == NULL) return -1; - return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, - key, key_len); + return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx, + key, key_len); } @@ -135,9 +148,9 @@ static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { - if (wpa_auth->cb.get_seqnum == NULL) + if (wpa_auth->cb->get_seqnum == NULL) return -1; - return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); + return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq); } @@ -145,10 +158,10 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { - if (wpa_auth->cb.send_eapol == NULL) + if (wpa_auth->cb->send_eapol == NULL) return -1; - return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, - encrypt); + return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len, + encrypt); } @@ -156,9 +169,9 @@ static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, const u8 *addr) { - if (wpa_auth->cb.start_ampe == NULL) + if (wpa_auth->cb->start_ampe == NULL) return -1; - return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr); + return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr); } #endif /* CONFIG_MESH */ @@ -167,9 +180,9 @@ int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) { - if (wpa_auth->cb.for_each_sta == NULL) + if (wpa_auth->cb->for_each_sta == NULL) return 0; - return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); + return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx); } @@ -177,9 +190,9 @@ int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx) { - if (wpa_auth->cb.for_each_auth == NULL) + if (wpa_auth->cb->for_each_auth == NULL) return 0; - return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); + return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx); } @@ -186,9 +199,9 @@ void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *txt) { - if (wpa_auth->cb.logger == NULL) + if (wpa_auth->cb->logger == NULL) return; - wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); + wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt); } @@ -199,7 +212,7 @@ int maxlen; va_list ap; - if (wpa_auth->cb.logger == NULL) + if (wpa_auth->cb->logger == NULL) return; maxlen = os_strlen(fmt) + 100; @@ -218,33 +231,36 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, - const u8 *addr) + const u8 *addr, u16 reason) { - if (wpa_auth->cb.disconnect == NULL) + if (wpa_auth->cb->disconnect == NULL) return; - wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); - wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); + wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)", + MAC2STR(addr), reason); + wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason); } -static int wpa_use_aes_cmac(struct wpa_state_machine *sm) +#ifdef CONFIG_OCV +static int wpa_channel_info(struct wpa_authenticator *wpa_auth, + struct wpa_channel_info *ci) { - int ret = 0; -#ifdef CONFIG_IEEE80211R - if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - ret = 1; -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) - ret = 1; -#endif /* CONFIG_IEEE80211W */ - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) - ret = 1; - return ret; + if (!wpa_auth->cb->channel_info) + return -1; + return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci); } +#endif /* CONFIG_OCV */ +static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth, + const u8 *addr, int vlan_id) +{ + if (!wpa_auth->cb->update_vlan) + return -1; + return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id); +} + + static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; @@ -304,6 +320,19 @@ } +void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm) +{ + if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) { + wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for " + MACSTR " (%d seconds)", MAC2STR(sm->addr), + sm->wpa_auth->conf.wpa_ptk_rekey); + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0, + wpa_rekey_ptk, sm->wpa_auth, sm); + } +} + + static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->pmksa == ctx) @@ -339,6 +368,10 @@ wpa_get_ntp_timestamp(buf + ETH_ALEN); ptr = (unsigned long) group; os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); +#ifdef TEST_FUZZ + os_memset(buf + ETH_ALEN, 0xab, 8); + os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr)); +#endif /* TEST_FUZZ */ if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; @@ -408,7 +441,8 @@ */ struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, - struct wpa_auth_callbacks *cb) + const struct wpa_auth_callbacks *cb, + void *cb_ctx) { struct wpa_authenticator *wpa_auth; @@ -417,7 +451,8 @@ return NULL; os_memcpy(wpa_auth->addr, addr, ETH_ALEN); os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); - os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + wpa_auth->cb = cb; + wpa_auth->cb_ctx = cb_ctx; if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); @@ -442,7 +477,7 @@ return NULL; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); if (wpa_auth->ft_pmk_cache == NULL) { wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); @@ -452,7 +487,7 @@ os_free(wpa_auth); return NULL; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, @@ -505,17 +540,13 @@ eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); -#ifdef CONFIG_PEERKEY - while (wpa_auth->stsl_negotiations) - wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); -#endif /* CONFIG_PEERKEY */ - pmksa_cache_auth_deinit(wpa_auth->pmksa); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; -#endif /* CONFIG_IEEE80211R */ + wpa_ft_deinit(wpa_auth); +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P bitfield_free(wpa_auth->ip_pool); @@ -598,7 +629,7 @@ if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return -1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sm->ft_completed) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "FT authentication already completed - do not " @@ -605,10 +636,22 @@ "start 4-way handshake"); /* Go to PTKINITDONE state to allow GTK rekeying */ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; + sm->Pair = TRUE; return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_FILS + if (sm->fils_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FILS authentication already completed - do not start 4-way handshake"); + /* Go to PTKINITDONE state to allow GTK rekeying */ + sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; + sm->Pair = TRUE; + return 0; + } +#endif /* CONFIG_FILS */ + if (sm->started) { os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); sm->ReAuthenticationRequest = TRUE; @@ -659,14 +702,17 @@ sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP os_free(sm->assoc_resp_ftie); wpabuf_free(sm->ft_pending_req_ies); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); wpa_group_put(sm->wpa_auth, sm->group); - os_free(sm); +#ifdef CONFIG_DPP2 + wpabuf_clear_free(sm->dpp_z); +#endif /* CONFIG_DPP2 */ + bin_clear_free(sm, sizeof(*sm)); } @@ -679,9 +725,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); @@ -688,6 +735,9 @@ sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +#ifdef CONFIG_IEEE80211R_AP + wpa_ft_sta_deinit(sm); +#endif /* CONFIG_IEEE80211R_AP */ if (sm->in_step_loop) { /* Must not free state machine while wpa_sm_step() is running. * Freeing will be completed in the end of wpa_sm_step(). */ @@ -738,7 +788,7 @@ } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_ie_parse *kde) @@ -785,7 +835,7 @@ return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, @@ -827,25 +877,44 @@ struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; + size_t pmk_len; + int vlan_id = 0; + os_memset(&PTK, 0, sizeof(PTK)); for (;;) { - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk); + sm->p2p_dev_addr, pmk, &pmk_len, + &vlan_id); if (pmk == NULL) break; - } else +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, pmk, pmk_len); + sm->xxkey_len = pmk_len; + } +#endif /* CONFIG_IEEE80211R_AP */ + } else { pmk = sm->PMK; + pmk_len = sm->pmk_len; + } - wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK); + if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0) + break; - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) - == 0) { + if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, + data, data_len) == 0) { + if (sm->PMK != pmk) { + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + } ok = 1; break; } - if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) break; } @@ -858,6 +927,11 @@ wpa_printf(MSG_DEBUG, "WPA: Earlier SNonce resulted in matching MIC"); sm->alt_snonce_valid = 0; + + if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0) + return -1; + os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); sm->PTK_valid = TRUE; @@ -872,39 +946,41 @@ { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - struct wpa_eapol_key_192 *key192; u16 key_info, key_data_length; - enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, - SMK_M1, SMK_M3, SMK_ERROR } msg; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; char *msgtxt; struct wpa_eapol_ie_parse kde; - int ft; - const u8 *eapol_key_ie, *key_data; - size_t eapol_key_ie_len, keyhdrlen, mic_len; + const u8 *key_data; + size_t keyhdrlen, mic_len; + u8 *mic; if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len); - mic_len = wpa_mic_len(sm->wpa_key_mgmt); - keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + keyhdrlen = sizeof(*key) + mic_len + 2; - if (data_len < sizeof(*hdr) + keyhdrlen) + if (data_len < sizeof(*hdr) + keyhdrlen) { + wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame"); return; + } hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); - key192 = (struct wpa_eapol_key_192 *) (hdr + 1); + mic = (u8 *) (key + 1); key_info = WPA_GET_BE16(key->key_info); - if (mic_len == 24) { - key_data = (const u8 *) (key192 + 1); - key_data_length = WPA_GET_BE16(key192->key_data_length); - } else { - key_data = (const u8 *) (key + 1); - key_data_length = WPA_GET_BE16(key->key_data_length); - } + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR - " key_info=0x%x type=%u key_data_length=%u", - MAC2STR(sm->addr), key_info, key->type, key_data_length); + " key_info=0x%x type=%u mic_len=%u key_data_length=%u", + MAC2STR(sm->addr), key_info, key->type, + (unsigned int) mic_len, key_data_length); + wpa_hexdump(MSG_MSGDUMP, + "WPA: EAPOL-Key header (ending before Key MIC)", + key, sizeof(*key)); + wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC", + mic, mic_len); if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", @@ -945,25 +1021,20 @@ /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ - if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == - (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { - if (key_info & WPA_KEY_INFO_ERROR) { - msg = SMK_ERROR; - msgtxt = "SMK Error"; - } else { - msg = SMK_M1; - msgtxt = "SMK M1"; - } - } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { - msg = SMK_M3; - msgtxt = "SMK M3"; - } else if (key_info & WPA_KEY_INFO_REQUEST) { + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message"); + return; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { msg = REQUEST; msgtxt = "Request"; } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { msg = GROUP_2; msgtxt = "2/2 Group"; - } else if (key_data_length == 0) { + } else if (key_data_length == 0 || + (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && + key_data_length == AES_BLOCK_SIZE)) { msg = PAIRWISE_4; msgtxt = "4/4 Pairwise"; } else { @@ -971,15 +1042,13 @@ msgtxt = "2/4 Pairwise"; } - /* TODO: key_info type validation for PeerKey */ if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || msg == GROUP_2) { u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (sm->pairwise == WPA_CIPHER_CCMP || sm->pairwise == WPA_CIPHER_GCMP) { - if (wpa_use_aes_cmac(sm) && - sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN && - !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && + if (wpa_use_cmac(sm->wpa_key_mgmt) && + !wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -989,7 +1058,8 @@ return; } - if (!wpa_use_aes_cmac(sm) && + if (!wpa_use_cmac(sm->wpa_key_mgmt) && + !wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -999,7 +1069,7 @@ } } - if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && + if (wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases"); @@ -1087,6 +1157,15 @@ } continue_processing: +#ifdef CONFIG_FILS + if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 && + !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame"); + return; + } +#endif /* CONFIG_FILS */ + switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && @@ -1114,70 +1193,10 @@ "collect more entropy for random number " "generation"); random_mark_pool_ready(); - wpa_sta_disconnect(wpa_auth, sm->addr); + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); return; } - if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key msg 2/4 with " - "invalid Key Data contents"); - return; - } - if (kde.rsn_ie) { - eapol_key_ie = kde.rsn_ie; - eapol_key_ie_len = kde.rsn_ie_len; - } else if (kde.osen) { - eapol_key_ie = kde.osen; - eapol_key_ie_len = kde.osen_len; - } else { - eapol_key_ie = kde.wpa_ie; - eapol_key_ie_len = kde.wpa_ie_len; - } - ft = sm->wpa == WPA_VERSION_WPA2 && - wpa_key_mgmt_ft(sm->wpa_key_mgmt); - if (sm->wpa_ie == NULL || - wpa_compare_rsn_ie(ft, - sm->wpa_ie, sm->wpa_ie_len, - eapol_key_ie, eapol_key_ie_len)) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "WPA IE from (Re)AssocReq did not " - "match with msg 2/4"); - if (sm->wpa_ie) { - wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", - sm->wpa_ie, sm->wpa_ie_len); - } - wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", - eapol_key_ie, eapol_key_ie_len); - /* MLME-DEAUTHENTICATE.request */ - wpa_sta_disconnect(wpa_auth, sm->addr); - return; - } -#ifdef CONFIG_IEEE80211R - if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { - wpa_sta_disconnect(wpa_auth, sm->addr); - return; - } -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_P2P - if (kde.ip_addr_req && kde.ip_addr_req[0] && - wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { - int idx; - wpa_printf(MSG_DEBUG, "P2P: IP address requested in " - "EAPOL-Key exchange"); - idx = bitfield_get_first_zero(wpa_auth->ip_pool); - if (idx >= 0) { - u32 start = WPA_GET_BE32(wpa_auth->conf. - ip_addr_start); - bitfield_set(wpa_auth->ip_pool, idx); - WPA_PUT_BE32(sm->ip_addr, start + idx); - wpa_printf(MSG_DEBUG, "P2P: Assigned IP " - "address %u.%u.%u.%u to " MACSTR, - sm->ip_addr[0], sm->ip_addr[1], - sm->ip_addr[2], sm->ip_addr[3], - MAC2STR(sm->addr)); - } - } -#endif /* CONFIG_P2P */ break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || @@ -1199,28 +1218,6 @@ return; } break; -#ifdef CONFIG_PEERKEY - case SMK_M1: - case SMK_M3: - case SMK_ERROR: - if (!wpa_auth->conf.peerkey) { - wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " - "PeerKey use disabled - ignoring message"); - return; - } - if (!sm->PTK_valid) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key msg SMK in " - "invalid state - dropped"); - return; - } - break; -#else /* CONFIG_PEERKEY */ - case SMK_M1: - case SMK_M3: - case SMK_ERROR: - return; /* STSL disabled - ignore SMK messages */ -#endif /* CONFIG_PEERKEY */ case REQUEST: break; } @@ -1234,22 +1231,55 @@ return; } - if (!(key_info & WPA_KEY_INFO_MIC)) { + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + !(key_info & WPA_KEY_INFO_MIC)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key MIC not set"); return; } +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + (key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC set"); + return; + } +#endif /* CONFIG_FILS */ + sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, - data_len) && + if (mic_len && + wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK, + data, data_len) && (msg != PAIRWISE_4 || !sm->alt_snonce_valid || wpa_try_alt_snonce(sm, data, data_len))) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ return; } +#ifdef CONFIG_FILS + if (!mic_len && + wpa_aead_decrypt(sm, &sm->PTK, data, data_len, + &key_data_length) < 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ + return; + } +#endif /* CONFIG_FILS */ +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); sm->pending_1_of_4_timeout = 0; @@ -1272,12 +1302,7 @@ * even though MAC address KDE is not normally encrypted, * supplicant is allowed to encrypt it. */ - if (msg == SMK_ERROR) { -#ifdef CONFIG_PEERKEY - wpa_smk_error(wpa_auth, sm, key_data, key_data_length); -#endif /* CONFIG_PEERKEY */ - return; - } else if (key_info & WPA_KEY_INFO_ERROR) { + if (key_info & WPA_KEY_INFO_ERROR) { if (wpa_receive_error_report( wpa_auth, sm, !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) @@ -1287,11 +1312,6 @@ "received EAPOL-Key Request for new " "4-Way Handshake"); wpa_request_new_ptk(sm); -#ifdef CONFIG_PEERKEY - } else if (msg == SMK_M1) { - wpa_smk_m1(wpa_auth, sm, key, key_data, - key_data_length); -#endif /* CONFIG_PEERKEY */ } else if (key_data_length > 0 && wpa_parse_kde_ies(key_data, key_data_length, &kde) == 0 && @@ -1330,18 +1350,10 @@ wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } -#ifdef CONFIG_PEERKEY - if (msg == SMK_M3) { - wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length); - return; - } -#endif /* CONFIG_PEERKEY */ - os_free(sm->last_rx_eapol_key); - sm->last_rx_eapol_key = os_malloc(data_len); + sm->last_rx_eapol_key = os_memdup(data, data_len); if (sm->last_rx_eapol_key == NULL) return; - os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); @@ -1356,7 +1368,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; @@ -1367,21 +1379,33 @@ * 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); +#ifdef TEST_FUZZ + os_memset(pos, 0xef, 8); +#endif /* TEST_FUZZ */ 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; } @@ -1407,7 +1431,6 @@ { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - struct wpa_eapol_key_192 *key192; size_t len, mic_len, keyhdrlen; int alg; int key_data_len, pad_len = 0; @@ -1414,19 +1437,18 @@ u8 *buf, *pos; int version, pairwise; int i; - u8 *key_data; + u8 *key_mic, *key_data; - mic_len = wpa_mic_len(sm->wpa_key_mgmt); - keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + keyhdrlen = sizeof(*key) + mic_len + 2; len = sizeof(struct ieee802_1x_hdr) + keyhdrlen; if (force_version) version = force_version; - else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) + else if (wpa_use_akm_defined(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AKM_DEFINED; - else if (wpa_use_aes_cmac(sm)) + else if (wpa_use_cmac(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; @@ -1448,8 +1470,7 @@ key_data_len = kde_len; if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || + wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) @@ -1458,6 +1479,8 @@ } len += key_data_len; + if (!mic_len && encr) + len += AES_BLOCK_SIZE; hdr = os_zalloc(len); if (hdr == NULL) @@ -1466,7 +1489,7 @@ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = host_to_be16(len - sizeof(*hdr)); key = (struct wpa_eapol_key *) (hdr + 1); - key192 = (struct wpa_eapol_key_192 *) (hdr + 1); + key_mic = (u8 *) (key + 1); key_data = ((u8 *) (hdr + 1)) + keyhdrlen; key->type = sm->wpa == WPA_VERSION_WPA2 ? @@ -1479,11 +1502,11 @@ WPA_PUT_BE16(key->key_info, key_info); alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; - WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); - if (key_info & WPA_KEY_INFO_SMK_MESSAGE) + if (sm->wpa == WPA_VERSION_WPA2 && !pairwise) WPA_PUT_BE16(key->key_length, 0); + else + WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); - /* FIX: STSL: what to use as key_replay_counter? */ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { sm->key_replay[i].valid = sm->key_replay[i - 1].valid; os_memcpy(sm->key_replay[i].counter, @@ -1505,10 +1528,31 @@ if (kde && !encr) { os_memcpy(key_data, kde, kde_len); - if (mic_len == 24) - WPA_PUT_BE16(key192->key_data_length, kde_len); - else - WPA_PUT_BE16(key->key_data_length, kde_len); + WPA_PUT_BE16(key_mic + mic_len, kde_len); +#ifdef CONFIG_FILS + } else if (!mic_len && kde) { + const u8 *aad[1]; + size_t aad_len[1]; + + WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len); + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + kde, kde_len); + + wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", + sm->PTK.kek, sm->PTK.kek_len); + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = (u8 *) hdr; + aad_len[0] = key_mic + 2 - (u8 *) hdr; + if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len, + 1, aad, aad_len, key_mic + 2) < 0) { + wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed"); + return; + } + + wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV", + key_mic + 2, AES_BLOCK_SIZE + kde_len); +#endif /* CONFIG_FILS */ } else if (encr && kde) { buf = os_zalloc(key_data_len); if (buf == NULL) { @@ -1525,9 +1569,11 @@ wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || + wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, + "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)", + (unsigned int) sm->PTK.kek_len); if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, (key_data_len - 8) / 8, buf, key_data)) { os_free(hdr); @@ -1534,15 +1580,13 @@ os_free(buf); return; } - if (mic_len == 24) - WPA_PUT_BE16(key192->key_data_length, - key_data_len); - else - WPA_PUT_BE16(key->key_data_length, - key_data_len); + WPA_PUT_BE16(key_mic + mic_len, key_data_len); #ifndef CONFIG_NO_RC4 } else if (sm->PTK.kek_len == 16) { u8 ek[32]; + + wpa_printf(MSG_DEBUG, + "WPA: Encrypt Key Data using RC4"); os_memcpy(key->key_iv, sm->group->Counter + WPA_NONCE_LEN - 16, 16); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); @@ -1550,12 +1594,7 @@ os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len); os_memcpy(key_data, buf, key_data_len); rc4_skip(ek, 32, 256, key_data, key_data_len); - if (mic_len == 24) - WPA_PUT_BE16(key192->key_data_length, - key_data_len); - else - WPA_PUT_BE16(key->key_data_length, - key_data_len); + WPA_PUT_BE16(key_mic + mic_len, key_data_len); #endif /* CONFIG_NO_RC4 */ } else { os_free(hdr); @@ -1566,9 +1605,7 @@ } if (key_info & WPA_KEY_INFO_MIC) { - u8 *key_mic; - - if (!sm->PTK_valid) { + if (!sm->PTK_valid || !mic_len) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "PTK not valid when sending EAPOL-Key " "frame"); @@ -1576,10 +1613,12 @@ return; } - key_mic = key192->key_mic; /* same offset for key and key192 */ - wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, - sm->wpa_key_mgmt, version, - (u8 *) hdr, len, key_mic); + if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, + sm->wpa_key_mgmt, version, + (u8 *) hdr, len, key_mic) < 0) { + os_free(hdr); + return; + } #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && @@ -1608,7 +1647,7 @@ { int timeout_ms; int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; - int ctr; + u32 ctr; if (sm == NULL) return; @@ -1622,25 +1661,30 @@ eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; + if (wpa_auth->conf.wpa_disable_eapol_key_retries && + (!pairwise || (key_info & WPA_KEY_INFO_MIC))) + timeout_ms = eapol_key_timeout_no_retrans; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; +#ifdef TEST_FUZZ + timeout_ms = 1; +#endif /* TEST_FUZZ */ wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " - "counter %d)", timeout_ms, ctr); + "counter %u)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } -static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, - size_t data_len) +static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, + u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - struct wpa_eapol_key_192 *key192; u16 key_info; int ret = 0; - u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; - size_t mic_len = wpa_mic_len(akmp); + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos; + size_t mic_len = wpa_mic_len(akmp, pmk_len); if (data_len < sizeof(*hdr) + sizeof(*key)) return -1; @@ -1647,16 +1691,16 @@ hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); - key192 = (struct wpa_eapol_key_192 *) (hdr + 1); + mic_pos = (u8 *) (key + 1); key_info = WPA_GET_BE16(key->key_info); - os_memcpy(mic, key192->key_mic, mic_len); - os_memset(key192->key_mic, 0, mic_len); + os_memcpy(mic, mic_pos, mic_len); + os_memset(mic_pos, 0, mic_len); if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, key_info & WPA_KEY_INFO_TYPE_MASK, - data, data_len, key192->key_mic) || - os_memcmp_const(mic, key192->key_mic, mic_len) != 0) + data, data_len, mic_pos) || + os_memcmp_const(mic, mic_pos, mic_len) != 0) ret = -1; - os_memcpy(key192->key_mic, mic, mic_len); + os_memcpy(mic_pos, mic, mic_len); return ret; } @@ -1665,7 +1709,10 @@ { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); - wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); + if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, + 0)) + wpa_printf(MSG_DEBUG, + "RSN: PTK removal from the driver failed"); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } @@ -1696,6 +1743,14 @@ case WPA_DEAUTH: case WPA_DISASSOC: sm->DeauthenticationRequest = TRUE; +#ifdef CONFIG_IEEE80211R_AP + os_memset(sm->PMK, 0, sizeof(sm->PMK)); + sm->pmk_len = 0; + os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); + sm->xxkey_len = 0; + os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); + sm->pmk_r1_len = 0; +#endif /* CONFIG_IEEE80211R_AP */ break; case WPA_REAUTH: case WPA_REAUTH_EAPOL: @@ -1729,7 +1784,7 @@ sm->ReAuthenticationRequest = TRUE; break; case WPA_ASSOC_FT: -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " "after association"); wpa_ft_install_ptk(sm); @@ -1736,20 +1791,39 @@ /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; + wpa_auth_set_ptk_rekey_timer(sm); return 0; -#else /* CONFIG_IEEE80211R */ +#else /* CONFIG_IEEE80211R_AP */ break; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + case WPA_ASSOC_FILS: +#ifdef CONFIG_FILS + wpa_printf(MSG_DEBUG, + "FILS: TK configuration after association"); + fils_set_tk(sm); + sm->fils_completed = 1; + return 0; +#else /* CONFIG_FILS */ + break; +#endif /* CONFIG_FILS */ + case WPA_DRV_STA_REMOVED: + sm->tk_already_set = FALSE; + return 0; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP sm->ft_completed = 0; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (sm->mgmt_frame_prot && event == WPA_AUTH) remove_ptk = 0; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + (event == WPA_AUTH || event == WPA_ASSOC)) + remove_ptk = 0; +#endif /* CONFIG_FILS */ if (remove_ptk) { sm->PTK_valid = FALSE; @@ -1794,7 +1868,9 @@ wpa_remove_ptk(sm); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); sm->TimeoutCtr = 0; - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 0); } @@ -1803,9 +1879,14 @@ SM_STATE(WPA_PTK, DISCONNECT) { + u16 reason = sm->disconnect_reason; + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; - wpa_sta_disconnect(sm->wpa_auth, sm->addr); + sm->disconnect_reason = 0; + if (!reason) + reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason); } @@ -1914,25 +1995,54 @@ size_t len = 2 * PMK_LEN; SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP sm->xxkey_len = 0; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); - os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); + os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->pmk_len = sm->pmksa->pmk_len; +#ifdef CONFIG_DPP + } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) { + wpa_printf(MSG_DEBUG, + "DPP: No PMKSA cache entry for STA - reject connection"); + sm->Disconnect = TRUE; + sm->disconnect_reason = WLAN_REASON_INVALID_PMKID; + return; +#endif /* CONFIG_DPP */ } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { + unsigned int pmk_len; + + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) + pmk_len = PMK_LEN_SUITE_B_192; + else + pmk_len = PMK_LEN; wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " - "(len=%lu)", (unsigned long) len); - os_memcpy(sm->PMK, msk, PMK_LEN); -#ifdef CONFIG_IEEE80211R + "(MSK len=%lu PMK len=%u)", (unsigned long) len, + pmk_len); + if (len < pmk_len) { + wpa_printf(MSG_DEBUG, + "WPA: MSK not long enough (%u) to create PMK (%u)", + (unsigned int) len, (unsigned int) pmk_len); + sm->Disconnect = TRUE; + return; + } + os_memcpy(sm->PMK, msk, pmk_len); + 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 */ +#endif /* CONFIG_IEEE80211R_AP */ } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", - sm->wpa_auth->cb.get_msk); + sm->wpa_auth->cb->get_msk); sm->Disconnect = TRUE; return; } @@ -1954,15 +2064,30 @@ SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; + size_t psk_len; + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); - psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL, + &psk_len, NULL); if (psk) { - os_memcpy(sm->PMK, psk, PMK_LEN); -#ifdef CONFIG_IEEE80211R + os_memcpy(sm->PMK, psk, psk_len); + sm->pmk_len = psk_len; +#ifdef CONFIG_IEEE80211R_AP os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } +#ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sm) && sm->pmksa) { + wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->pmk_len = sm->pmksa->pmk_len; +#ifdef CONFIG_IEEE80211R_AP + os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->xxkey_len = sm->pmksa->pmk_len; +#endif /* CONFIG_IEEE80211R_AP */ + } +#endif /* CONFIG_SAE */ sm->req_replay_counter_used = 0; } @@ -1978,7 +2103,7 @@ sm->alt_snonce_valid = FALSE; sm->TimeoutCtr++; - if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -1987,11 +2112,23 @@ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/4 msg of 4-Way Handshake"); /* - * TODO: Could add PMKID even with WPA2-PSK, but only if there is only - * one possible PSK for this STA. + * For infrastructure BSS cases, it is better for the AP not to include + * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate + * offline search for the passphrase/PSK without having to be able to + * capture a 4-way handshake from a STA that has access to the network. + * + * For IBSS cases, addition of PMKID KDE could be considered even with + * WPA2-PSK cases that use multiple PSKs, but only if there is a single + * possible PSK for this STA. However, this should not be done unless + * there is support for using that information on the supplicant side. + * The concern about exposing PMKID unnecessarily in infrastructure BSS + * cases would also apply here, but at least in the IBSS case, this + * would cover a potential real use case. */ if (sm->wpa == WPA_VERSION_WPA2 && - wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) || + (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) { pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; @@ -1999,19 +2136,65 @@ 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_FILS + } else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + if (sm->pmkid_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID from FILS/ERP", + 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 FILS/ERP PMKID available for message 1/4"); + pmkid = NULL; + } +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP + } else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && + sm->ft_completed) { + wpa_printf(MSG_DEBUG, + "FT: No PMKID in message 1/4 when using FT protocol"); + pmkid = NULL; + pmkid_len = 0; +#endif /* CONFIG_IEEE80211R_AP */ +#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 * available with pre-calculated PMKID. */ - rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, + rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr, sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], - wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + sm->wpa_key_mgmt); + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID derived from PMK", + &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN); } } wpa_send_eapol(sm->wpa_auth, sm, @@ -2021,53 +2204,682 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, - const u8 *pmk, struct wpa_ptk *ptk) + const u8 *pmk, unsigned int pmk_len, + struct wpa_ptk *ptk) { -#ifdef CONFIG_IEEE80211R - if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - return wpa_auth_derive_ptk_ft(sm, pmk, ptk); -#endif /* CONFIG_IEEE80211R */ + const u8 *z = NULL; + size_t z_len = 0; - return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (sm->ft_completed) { + u8 ptk_name[WPA_PMK_NAME_LEN]; + + return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->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); + } + return wpa_auth_derive_ptk_ft(sm, ptk); + } +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_DPP2 + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) { + z = wpabuf_head(sm->dpp_z); + z_len = wpabuf_len(sm->dpp_z); + } +#endif /* CONFIG_DPP2 */ + + return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, - ptk, sm->wpa_key_mgmt, sm->pairwise); + ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len); } +#ifdef CONFIG_FILS + +int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *snonce, const u8 *anonce, + const u8 *dhss, size_t dhss_len, + struct wpabuf *g_sta, struct wpabuf *g_ap) +{ + u8 ick[FILS_ICK_MAX_LEN]; + size_t ick_len; + int res; + u8 fils_ft[FILS_FT_MAX_LEN]; + size_t fils_ft_len = 0; + + res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr, + snonce, anonce, dhss, dhss_len, + &sm->PTK, ick, &ick_len, + sm->wpa_key_mgmt, sm->pairwise, + fils_ft, &fils_ft_len); + if (res < 0) + return res; + sm->PTK_valid = TRUE; + sm->tk_already_set = FALSE; + +#ifdef CONFIG_IEEE80211R_AP + if (fils_ft_len) { + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + struct wpa_auth_config *conf = &wpa_auth->conf; + u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; + + if (wpa_derive_pmk_r0(fils_ft, fils_ft_len, + conf->ssid, conf->ssid_len, + conf->mobility_domain, + conf->r0_key_holder, + conf->r0_key_holder_len, + sm->addr, pmk_r0, pmk_r0_name, + use_sha384) < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", + pmk_r0, pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name", + pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name); + os_memset(fils_ft, 0, sizeof(fils_ft)); + + res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder, + sm->addr, sm->pmk_r1_name, + use_sha384); + os_memset(pmk_r0, 0, PMK_LEN_MAX); + if (res < 0) + return -1; + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + sm->pmk_r1_name_valid = 1; + } +#endif /* CONFIG_IEEE80211R_AP */ + + res = fils_key_auth_sk(ick, ick_len, snonce, anonce, + sm->addr, sm->wpa_auth->addr, + g_sta ? wpabuf_head(g_sta) : NULL, + g_sta ? wpabuf_len(g_sta) : 0, + g_ap ? wpabuf_head(g_ap) : NULL, + g_ap ? wpabuf_len(g_ap) : 0, + sm->wpa_key_mgmt, sm->fils_key_auth_sta, + sm->fils_key_auth_ap, + &sm->fils_key_auth_len); + os_memset(ick, 0, sizeof(ick)); + + /* Store nonces for (Re)Association Request/Response frame processing */ + os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN); + os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN); + + return res; +} + + +static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk, + u8 *buf, size_t buf_len, u16 *_key_data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u8 *pos; + u16 key_data_len; + u8 *tmp; + const u8 *aad[1]; + size_t aad_len[1]; + + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct wpa_eapol_key *) (hdr + 1); + pos = (u8 *) (key + 1); + key_data_len = WPA_GET_BE16(pos); + if (key_data_len < AES_BLOCK_SIZE || + key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "No room for AES-SIV data in the frame"); + return -1; + } + pos += 2; /* Pointing at the Encrypted Key Data field */ + + tmp = os_malloc(key_data_len); + if (!tmp) + return -1; + + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = buf; + aad_len[0] = pos - buf; + if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len, + 1, aad, aad_len, tmp) < 0) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "Invalid AES-SIV data in the frame"); + bin_clear_free(tmp, key_data_len); + return -1; + } + + /* AEAD decryption and validation completed successfully */ + key_data_len -= AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data", + tmp, key_data_len); + + /* Replace Key Data field with the decrypted version */ + os_memcpy(pos, tmp, key_data_len); + pos -= 2; /* Key Data Length field */ + WPA_PUT_BE16(pos, key_data_len); + bin_clear_free(tmp, key_data_len); + if (_key_data_len) + *_key_data_len = key_data_len; + return 0; +} + + +const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *fils_session) +{ + const u8 *ie, *end; + const u8 *session = NULL; + + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + wpa_printf(MSG_DEBUG, + "FILS: Not a FILS AKM - reject association"); + return NULL; + } + + /* Verify Session element */ + ie = ies; + end = ((const u8 *) ie) + ies_len; + while (ie + 1 < end) { + if (ie + 2 + ie[1] > end) + break; + if (ie[0] == WLAN_EID_EXTENSION && + ie[1] >= 1 + FILS_SESSION_LEN && + ie[2] == WLAN_EID_EXT_FILS_SESSION) { + session = ie; + break; + } + ie += 2 + ie[1]; + } + + if (!session) { + wpa_printf(MSG_DEBUG, + "FILS: %s: Could not find FILS Session element in Assoc Req - reject", + __func__); + return NULL; + } + + if (!fils_session) { + wpa_printf(MSG_DEBUG, + "FILS: %s: Could not find FILS Session element in STA entry - reject", + __func__); + return NULL; + } + + if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + fils_session, FILS_SESSION_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session", + session + 3, FILS_SESSION_LEN); + return NULL; + } + return session; +} + + +int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len) +{ + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to parse decrypted elements"); + return -1; + } + + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + return -1; + } + + if (!elems.fils_key_confirm) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element"); + return -1; + } + + if (elems.fils_key_confirm_len != sm->fils_key_auth_len) { + wpa_printf(MSG_DEBUG, + "FILS: Unexpected Key-Auth length %d (expected %d)", + elems.fils_key_confirm_len, + (int) sm->fils_key_auth_len); + return -1; + } + + if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta, + sm->fils_key_auth_len) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth", + elems.fils_key_confirm, elems.fils_key_confirm_len); + wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth", + sm->fils_key_auth_sta, sm->fils_key_auth_len); + return -1; + } + + return 0; +} + + +int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, + const struct ieee80211_mgmt *mgmt, size_t frame_len, + u8 *pos, size_t left) +{ + u16 fc, stype; + const u8 *end, *ie_start, *ie, *session, *crypt; + const u8 *aad[5]; + size_t aad_len[5]; + + if (!sm || !sm->PTK_valid) { + wpa_printf(MSG_DEBUG, + "FILS: No KEK to decrypt Assocication Request frame"); + return -1; + } + + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + wpa_printf(MSG_DEBUG, + "FILS: Not a FILS AKM - reject association"); + return -1; + } + + end = ((const u8 *) mgmt) + frame_len; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + if (stype == WLAN_FC_STYPE_REASSOC_REQ) + ie_start = mgmt->u.reassoc_req.variable; + else + ie_start = mgmt->u.assoc_req.variable; + ie = ie_start; + + /* + * Find FILS Session element which is the last unencrypted element in + * the frame. + */ + session = wpa_fils_validate_fils_session(sm, ie, end - ie, + fils_session); + if (!session) { + wpa_printf(MSG_DEBUG, "FILS: Session validation failed"); + return -1; + } + + crypt = session + 2 + session[1]; + + if (end - crypt < AES_BLOCK_SIZE) { + wpa_printf(MSG_DEBUG, + "FILS: Too short frame to include AES-SIV data"); + return -1; + } + + /* AES-SIV AAD vectors */ + + /* The STA's MAC address */ + aad[0] = mgmt->sa; + aad_len[0] = ETH_ALEN; + /* The AP's BSSID */ + aad[1] = mgmt->da; + aad_len[1] = ETH_ALEN; + /* The STA's nonce */ + aad[2] = sm->SNonce; + aad_len[2] = FILS_NONCE_LEN; + /* The AP's nonce */ + aad[3] = sm->ANonce; + aad_len[3] = FILS_NONCE_LEN; + /* + * The (Re)Association Request frame from the Capability Information + * field to the FILS Session element (both inclusive). + */ + aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info; + aad_len[4] = crypt - aad[4]; + + if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt, + 5, aad, aad_len, pos + (crypt - ie_start)) < 0) { + wpa_printf(MSG_DEBUG, + "FILS: Invalid AES-SIV data in the frame"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements", + pos, left - AES_BLOCK_SIZE); + + if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed"); + return -1; + } + + return left - AES_BLOCK_SIZE; +} + + +int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, + size_t current_len, size_t max_len, + const struct wpabuf *hlp) +{ + u8 *end = buf + max_len; + u8 *pos = buf + current_len; + struct ieee80211_mgmt *mgmt; + struct wpabuf *plain; + const u8 *aad[5]; + size_t aad_len[5]; + + if (!sm || !sm->PTK_valid) + return -1; + + wpa_hexdump(MSG_DEBUG, + "FILS: Association Response frame before FILS processing", + buf, current_len); + + mgmt = (struct ieee80211_mgmt *) buf; + + /* AES-SIV AAD vectors */ + + /* The AP's BSSID */ + aad[0] = mgmt->sa; + aad_len[0] = ETH_ALEN; + /* The STA's MAC address */ + aad[1] = mgmt->da; + aad_len[1] = ETH_ALEN; + /* The AP's nonce */ + aad[2] = sm->ANonce; + aad_len[2] = FILS_NONCE_LEN; + /* The STA's nonce */ + aad[3] = sm->SNonce; + aad_len[3] = FILS_NONCE_LEN; + /* + * The (Re)Association Response frame from the Capability Information + * field (the same offset in both Association and Reassociation + * Response frames) to the FILS Session element (both inclusive). + */ + aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info; + aad_len[4] = pos - aad[4]; + + /* The following elements will be encrypted with AES-SIV */ + plain = fils_prepare_plainbuf(sm, hlp); + if (!plain) { + wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed"); + return -1; + } + + if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) { + wpa_printf(MSG_DEBUG, + "FILS: Not enough room for FILS elements"); + wpabuf_free(plain); + return -1; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext", + plain); + + if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, + wpabuf_head(plain), wpabuf_len(plain), + 5, aad, aad_len, pos) < 0) { + wpabuf_free(plain); + return -1; + } + + wpa_hexdump(MSG_DEBUG, + "FILS: Encrypted Association Response elements", + pos, AES_BLOCK_SIZE + wpabuf_len(plain)); + current_len += wpabuf_len(plain) + AES_BLOCK_SIZE; + wpabuf_free(plain); + + sm->fils_completed = 1; + + return current_len; +} + + +static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, + const struct wpabuf *hlp) +{ + struct wpabuf *plain; + u8 *len, *tmp, *tmp2; + u8 hdr[2]; + u8 *gtk, dummy_gtk[32]; + size_t gtk_len; + struct wpa_group *gsm; + + plain = wpabuf_alloc(1000); + if (!plain) + return NULL; + + /* TODO: FILS Public Key */ + + /* FILS Key Confirmation */ + wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM); + wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len); + + /* FILS HLP Container */ + if (hlp) + wpabuf_put_buf(plain, hlp); + + /* TODO: FILS IP Address Assignment */ + + /* Key Delivery */ + gsm = sm->group; + wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */ + len = wpabuf_put(plain, 1); + wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, + wpabuf_put(plain, WPA_KEY_RSC_LEN)); + /* GTK KDE */ + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) { + wpabuf_free(plain); + return NULL; + } + gtk = dummy_gtk; + } + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + tmp = wpabuf_put(plain, 0); + tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + wpabuf_put(plain, tmp2 - tmp); + + /* IGTK KDE */ + tmp = wpabuf_put(plain, 0); + tmp2 = ieee80211w_kde_add(sm, tmp); + wpabuf_put(plain, tmp2 - tmp); + + *len = (u8 *) wpabuf_put(plain, 0) - len - 1; + +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "FILS: Failed to get channel info for OCI element"); + wpabuf_free(plain); + return NULL; + } + + pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN); + if (ocv_insert_extended_oci(&ci, pos) < 0) { + wpabuf_free(plain); + return NULL; + } + } +#endif /* CONFIG_OCV */ + + return plain; +} + + +int fils_set_tk(struct wpa_state_machine *sm) +{ + enum wpa_alg alg; + int klen; + + if (!sm || !sm->PTK_valid) { + wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK"); + return -1; + } + if (sm->tk_already_set) { + wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver"); + return -1; + } + + alg = wpa_cipher_to_alg(sm->pairwise); + klen = wpa_cipher_key_len(sm->pairwise); + + wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver"); + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk, klen)) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver"); + return -1; + } + sm->tk_already_set = TRUE; + + return 0; +} + + +u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf, + const u8 *fils_session, struct wpabuf *hlp) +{ + struct wpabuf *plain; + u8 *pos = buf; + + /* FILS Session */ + *pos++ = WLAN_EID_EXTENSION; /* Element ID */ + *pos++ = 1 + FILS_SESSION_LEN; /* Length */ + *pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ + os_memcpy(pos, fils_session, FILS_SESSION_LEN); + pos += FILS_SESSION_LEN; + + plain = fils_prepare_plainbuf(sm, hlp); + if (!plain) { + wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed"); + return NULL; + } + + os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain)); + pos += wpabuf_len(plain); + + wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__, + (unsigned int) wpabuf_len(plain)); + wpabuf_free(plain); + sm->fils_completed = 1; + return pos; +} + +#endif /* CONFIG_FILS */ + + +#ifdef CONFIG_OCV +int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx) +{ + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + + if (!wpa_auth->cb->get_sta_tx_params) + return -1; + return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr, + ap_max_chanwidth, ap_seg1_idx, + bandwidth, seg1_idx); +} +#endif /* CONFIG_OCV */ + + SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { + struct wpa_authenticator *wpa_auth = sm->wpa_auth; struct wpa_ptk PTK; int ok = 0, psk_found = 0; const u8 *pmk = NULL; + size_t pmk_len; + int ft; + const u8 *eapol_key_ie, *key_data, *mic; + u16 key_data_length; + size_t mic_len, eapol_key_ie_len; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_ie_parse kde; + int vlan_id = 0; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; sm->update_snonce = FALSE; + os_memset(&PTK, 0, sizeof(PTK)); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching * the packet */ for (;;) { - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk); + sm->p2p_dev_addr, pmk, &pmk_len, + &vlan_id); if (pmk == NULL) break; psk_found = 1; - } else +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, pmk, pmk_len); + sm->xxkey_len = pmk_len; + } +#endif /* CONFIG_IEEE80211R_AP */ + } else { pmk = sm->PMK; + pmk_len = sm->pmk_len; + } - wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK); + if ((!pmk || !pmk_len) && sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache"); + pmk = sm->pmksa->pmk; + pmk_len = sm->pmksa->pmk_len; + } - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, + if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0) + break; + + if (mic_len && + wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { + if (sm->PMK != pmk) { + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + } ok = 1; break; } - if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) +#ifdef CONFIG_FILS + if (!mic_len && + wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len, NULL) == 0) { + ok = 1; break; + } +#endif /* CONFIG_FILS */ + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) + break; } if (!ok) { @@ -2078,7 +2890,105 @@ return; } -#ifdef CONFIG_IEEE80211R + /* + * Note: last_rx_eapol_key length fields have already been validated in + * wpa_receive(). + */ + hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key; + key = (struct wpa_eapol_key *) (hdr + 1); + mic = (u8 *) (key + 1); + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); + if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - + sizeof(*key) - mic_len - 2) + return; + + if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 with invalid Key Data contents"); + return; + } + if (kde.rsn_ie) { + eapol_key_ie = kde.rsn_ie; + eapol_key_ie_len = kde.rsn_ie_len; + } else if (kde.osen) { + eapol_key_ie = kde.osen; + eapol_key_ie_len = kde.osen_len; + } else { + eapol_key_ie = kde.wpa_ie; + eapol_key_ie_len = kde.wpa_ie_len; + } + ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt); + if (sm->wpa_ie == NULL || + wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len, + eapol_key_ie, eapol_key_ie_len)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not match with msg 2/4"); + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); + } + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + eapol_key_ie, eapol_key_ie_len); + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(wpa_auth, &ci) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Failed to get channel info to validate received OCI in EAPOL-Key 2/4"); + return; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ +#ifdef CONFIG_IEEE80211R_AP + if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_P2P + if (kde.ip_addr_req && kde.ip_addr_req[0] && + wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { + int idx; + wpa_printf(MSG_DEBUG, + "P2P: IP address requested in EAPOL-Key exchange"); + idx = bitfield_get_first_zero(wpa_auth->ip_pool); + if (idx >= 0) { + u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start); + bitfield_set(wpa_auth->ip_pool, idx); + WPA_PUT_BE32(sm->ip_addr, start + idx); + wpa_printf(MSG_DEBUG, + "P2P: Assigned IP address %u.%u.%u.%u to " + MACSTR, sm->ip_addr[0], sm->ip_addr[1], + sm->ip_addr[2], sm->ip_addr[3], + MAC2STR(sm->addr)); + } + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_IEEE80211R_AP if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { /* * Verify that PMKR1Name from EAPOL-Key message 2/4 matches @@ -2097,8 +3007,15 @@ return; } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); @@ -2107,6 +3024,7 @@ * state machine data based on whatever PSK was selected here. */ os_memcpy(sm->PMK, pmk, PMK_LEN); + sm->pmk_len = PMK_LEN; } sm->MICVerified = TRUE; @@ -2155,7 +3073,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. @@ -2186,6 +3105,36 @@ #endif /* CONFIG_IEEE80211W */ +static int ocv_oci_len(struct wpa_state_machine *sm) +{ +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) + return OCV_OCI_KDE_LEN; +#endif /* CONFIG_OCV */ + return 0; +} + +static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos) +{ +#ifdef CONFIG_OCV + struct wpa_channel_info ci; + + if (!wpa_auth_uses_ocv(sm)) + return 0; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element"); + return -1; + } + + return ocv_insert_oci_kde(&ci, argpos); +#else /* CONFIG_OCV */ + return 0; +#endif /* CONFIG_OCV */ +} + + SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; @@ -2198,7 +3147,12 @@ sm->TimeoutEvt = FALSE; sm->TimeoutCtr++; - if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->TimeoutCtr > 1) { + /* Do not allow retransmission of EAPOL-Key msg 3/4 */ + return; + } + if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -2228,7 +3182,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. @@ -2263,15 +3218,15 @@ } } - kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ kde_len += 300; /* FTIE + 2 * TIE */ } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; @@ -2283,9 +3238,13 @@ pos = kde; os_memcpy(pos, wpa_ie, wpa_ie_len); pos += wpa_ie_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { - int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); + int res; + size_t elen; + + elen = pos - kde; + res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name); if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert " "PMKR1Name into RSN IE in EAPOL-Key data"); @@ -2292,9 +3251,10 @@ os_free(kde); return; } - pos += res; + pos -= wpa_ie_len; + pos += elen; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (gtk) { u8 hdr[2]; hdr[0] = keyidx & 0x03; @@ -2303,17 +3263,32 @@ gtk, gtk_len); } pos = ieee80211w_kde_add(sm, pos); + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde); + return; + } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; struct wpa_auth_config *conf; conf = &sm->wpa_auth->conf; - res = wpa_write_ftie(conf, conf->r0_key_holder, - conf->r0_key_holder_len, - NULL, NULL, pos, kde + kde_len - pos, - NULL, 0); + if (sm->assoc_resp_ftie && + kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { + os_memcpy(pos, sm->assoc_resp_ftie, + 2 + sm->assoc_resp_ftie[1]); + res = 2 + sm->assoc_resp_ftie[1]; + } else { + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + + res = wpa_write_ftie(conf, use_sha384, + conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, + kde + kde_len - pos, + NULL, 0); + } if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " "into EAPOL-Key Key Data"); @@ -2333,10 +3308,10 @@ *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; - WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); + WPA_PUT_LE32(pos, conf->r0_key_lifetime); pos += 4; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) { u8 addr[3 * 4]; @@ -2349,7 +3324,9 @@ #endif /* CONFIG_P2P */ wpa_send_eapol(sm->wpa_auth, sm, - (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + (secure ? WPA_KEY_INFO_SECURE : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_KEY_TYPE, _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); @@ -2366,20 +3343,18 @@ int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk, klen)) { - wpa_sta_disconnect(sm->wpa_auth, sm->addr); + wpa_sta_disconnect(sm->wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); return; } /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; - if (sm->wpa_auth->conf.wpa_ptk_rekey) { - eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); - eloop_register_timeout(sm->wpa_auth->conf. - wpa_ptk_rekey, 0, wpa_rekey_ptk, - sm->wpa_auth, sm); - } + wpa_auth_set_ptk_rekey_timer(sm); - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 1); } @@ -2405,9 +3380,9 @@ "pairwise key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } @@ -2451,15 +3426,22 @@ wpa_auth_get_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun) > 0) SM_ENTER(WPA_PTK, INITPMK); - else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) + else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE /* FIX: && 802.1X::keyRun */) SM_ENTER(WPA_PTK, INITPSK); + else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) + SM_ENTER(WPA_PTK, INITPMK); break; case WPA_PTK_INITPMK: if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, - WPA_EAPOL_keyAvailable) > 0) + WPA_EAPOL_keyAvailable) > 0) { SM_ENTER(WPA_PTK, PTKSTART); - else { +#ifdef CONFIG_DPP + } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) { + SM_ENTER(WPA_PTK, PTKSTART); +#endif /* CONFIG_DPP */ + } else { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "INITPMK - keyAvailable = false"); @@ -2468,9 +3450,13 @@ break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, - NULL)) + NULL, NULL, NULL)) { SM_ENTER(WPA_PTK, PTKSTART); - else { +#ifdef CONFIG_SAE + } else if (wpa_auth_uses_sae(sm) && sm->pmksa) { + SM_ENTER(WPA_PTK, PTKSTART); +#endif /* CONFIG_SAE */ + } else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; @@ -2482,11 +3468,12 @@ sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutCtr > - (int) dot11RSNAConfigPairwiseUpdateCount) { + sm->wpa_auth->conf.wpa_pairwise_update_count) { wpa_auth->dot11RSNA4WayHandshakeFailures++; - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "PTKSTART: Retry limit %d reached", - dot11RSNAConfigPairwiseUpdateCount); + wpa_auth_vlogger( + sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKSTART: Retry limit %u reached", + sm->wpa_auth->conf.wpa_pairwise_update_count); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); @@ -2510,12 +3497,14 @@ sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > - (int) dot11RSNAConfigPairwiseUpdateCount) { + sm->wpa_auth->conf.wpa_pairwise_update_count || + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->TimeoutCtr > 1)) { wpa_auth->dot11RSNA4WayHandshakeFailures++; - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "PTKINITNEGOTIATING: Retry limit %d " - "reached", - dot11RSNAConfigPairwiseUpdateCount); + wpa_auth_vlogger( + sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKINITNEGOTIATING: Retry limit %u reached", + sm->wpa_auth->conf.wpa_pairwise_update_count); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); @@ -2550,7 +3539,12 @@ SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); sm->GTimeoutCtr++; - if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->GTimeoutCtr > 1) { + /* Do not allow retransmission of EAPOL-Key group msg 1/2 */ + return; + } + if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -2567,7 +3561,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. @@ -2578,7 +3573,7 @@ } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + - ieee80211w_kde_len(sm); + ieee80211w_kde_len(sm) + ocv_oci_len(sm); kde_buf = os_malloc(kde_len); if (kde_buf == NULL) return; @@ -2589,6 +3584,10 @@ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde_buf); + return; + } kde_len = pos - kde; } else { kde = gtk; @@ -2596,10 +3595,12 @@ } wpa_send_eapol(sm->wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), - rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1); + rsc, NULL, kde, kde_len, gsm->GN, 1); os_free(kde_buf); } @@ -2607,8 +3608,67 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) { +#ifdef CONFIG_OCV + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + const u8 *key_data, *mic; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_ie_parse kde; + size_t mic_len; + u16 key_data_length; +#endif /* CONFIG_OCV */ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; + +#ifdef CONFIG_OCV + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + + /* + * Note: last_rx_eapol_key length fields have already been validated in + * wpa_receive(). + */ + hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key; + key = (struct wpa_eapol_key *) (hdr + 1); + mic = (u8 *) (key + 1); + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); + if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - + sizeof(*key) - mic_len - 2) + return; + + if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key group msg 2/2 with invalid Key Data contents"); + return; + } + + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(wpa_auth, &ci) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Failed to get channel info to validate received OCI in EAPOL-Key group 1/2"); + return; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; @@ -2628,6 +3688,10 @@ sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake failed (%s) after %u tries", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN", + sm->wpa_auth->conf.wpa_group_update_count); } @@ -2647,7 +3711,9 @@ !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); else if (sm->GTimeoutCtr > - (int) dot11RSNAConfigGroupUpdateCount) + sm->wpa_auth->conf.wpa_group_update_count || + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->GTimeoutCtr > 1)) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); @@ -2750,7 +3816,7 @@ } -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP /* update GTK when exiting WNM-Sleep Mode */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) { @@ -2829,7 +3895,7 @@ return pos - start; } #endif /* CONFIG_IEEE80211W */ -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, @@ -3107,8 +4173,8 @@ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", RSN_VERSION, !!wpa_auth->conf.wpa_strict_rekey, - dot11RSNAConfigGroupUpdateCount, - dot11RSNAConfigPairwiseUpdateCount, + wpa_auth->conf.wpa_group_update_count, + wpa_auth->conf.wpa_pairwise_update_count, wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, @@ -3211,6 +4277,15 @@ } +const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len) +{ + if (!sm) + return NULL; + *len = sm->pmk_len; + return sm->PMK; +} + + int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) { if (sm == NULL) @@ -3235,6 +4310,14 @@ } +int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm) +{ + if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt)) + return 0; + return sm->tk_already_set; +} + + int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry) { @@ -3269,6 +4352,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + unsigned int pmk_len, int session_timeout, struct eapol_state_machine *eapol) { if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || @@ -3275,7 +4359,14 @@ sm->wpa_auth->conf.disable_pmksa_caching) return -1; - if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { + if (pmk_len > PMK_LEN_SUITE_B_192) + pmk_len = PMK_LEN_SUITE_B_192; + } else if (pmk_len > PMK_LEN) { + pmk_len = PMK_LEN; + } + + if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL, sm->PTK.kck, sm->PTK.kck_len, sm->wpa_auth->addr, sm->addr, session_timeout, eapol, sm->wpa_key_mgmt)) @@ -3293,7 +4384,7 @@ if (wpa_auth == NULL) return -1; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL, NULL, 0, wpa_auth->addr, sta_addr, session_timeout, eapol, @@ -3305,12 +4396,12 @@ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, - const u8 *pmk) + const u8 *pmk, const u8 *pmkid) { if (wpa_auth->conf.disable_pmksa_caching) return -1; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid, NULL, 0, wpa_auth->addr, addr, 0, NULL, WPA_KEY_MGMT_SAE)) @@ -3320,6 +4411,29 @@ } +void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid) +{ + os_memcpy(sm->pmkid, pmkid, PMKID_LEN); + sm->pmkid_set = 1; +} + + +int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, + int session_timeout, int akmp) +{ + if (wpa_auth->conf.disable_pmksa_caching) + return -1; + + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid, + NULL, 0, wpa_auth->addr, addr, session_timeout, + NULL, akmp)) + return 0; + + return -1; +} + + void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { @@ -3336,6 +4450,99 @@ } +int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, + size_t len) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return 0; + return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len); +} + + +void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth && wpa_auth->pmksa) + pmksa_cache_auth_flush(wpa_auth->pmksa); +} + + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, + char *buf, size_t len) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return 0; + + return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len); +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, + const u8 *pmkid, int expiration) +{ + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa, + spa, 0, NULL, WPA_KEY_MGMT_SAE); + if (!entry) + return NULL; + + os_get_reltime(&now); + entry->expiration = now.sec + expiration; + return entry; +} + + +int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, + struct rsn_pmksa_cache_entry *entry) +{ + int ret; + + if (!wpa_auth || !wpa_auth->pmksa) + return -1; + + ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry); + if (ret < 0) + wpa_printf(MSG_DEBUG, + "RSN: Failed to store external PMKSA cache for " + MACSTR, MAC2STR(entry->spa)); + + return ret; +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *pmkid) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return NULL; + return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid); +} + + +void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, + struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, + u8 *pmkid, u8 *pmk) +{ + if (!sm) + return; + + sm->pmksa = pmksa; + os_memcpy(pmk, pmksa->pmk, PMK_LEN); + os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN); +} + + /* * Remove and free the group from wpa_authenticator. This is triggered by a * callback to make sure nobody is currently iterating the group list while it @@ -3414,6 +4621,98 @@ } +/* + * Enforce that the group state machine for the VLAN is running, increase + * reference counter as interface is up. References might have been increased + * even if a negative value is returned. + * Returns: -1 on error (group missing, group already failed); otherwise, 0 + */ +int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + + if (wpa_auth == NULL) + return 0; + + group = wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + wpa_printf(MSG_DEBUG, + "WPA: Ensure group state machine running for VLAN ID %d", + vlan_id); + + wpa_group_get(wpa_auth, group); + group->num_setup_iface++; + + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; + + return 0; +} + + +/* + * Decrease reference counter, expected to be zero afterwards. + * returns: -1 on error (group not found, group in fail state) + * -2 if wpa_group is still referenced + * 0 else + */ +int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + int ret = 0; + + if (wpa_auth == NULL) + return 0; + + group = wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) + return -1; + + wpa_printf(MSG_DEBUG, + "WPA: Try stopping group state machine for VLAN ID %d", + vlan_id); + + if (group->num_setup_iface <= 0) { + wpa_printf(MSG_ERROR, + "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.", + vlan_id); + return -1; + } + group->num_setup_iface--; + + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + ret = -1; + + if (group->references > 1) { + wpa_printf(MSG_DEBUG, + "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold", + vlan_id); + ret = -2; + } + + wpa_group_put(wpa_auth, group); + + return ret; +} + + int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) { struct wpa_group *group; @@ -3478,6 +4777,14 @@ (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } + +#ifdef CONFIG_TESTING_OPTIONS + if (sm->eapol_status_cb) { + sm->eapol_status_cb(sm->eapol_status_cb_ctx1, + sm->eapol_status_cb_ctx2); + sm->eapol_status_cb = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ } @@ -3524,3 +4831,379 @@ for (group = wpa_auth->group; group; group = group->next) wpa_group_config_group_keys(wpa_auth, group); } + + +#ifdef CONFIG_FILS + +struct wpa_auth_fils_iter_data { + struct wpa_authenticator *auth; + const u8 *cache_id; + struct rsn_pmksa_cache_entry *pmksa; + const u8 *spa; + const u8 *pmkid; +}; + + +static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx) +{ + struct wpa_auth_fils_iter_data *data = ctx; + + if (a == data->auth || !a->conf.fils_cache_id_set || + os_memcmp(a->conf.fils_cache_id, data->cache_id, + FILS_CACHE_ID_LEN) != 0) + return 0; + data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid); + return data->pmksa != NULL; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, const u8 *pmkid) +{ + struct wpa_auth_fils_iter_data idata; + + if (!wpa_auth->conf.fils_cache_id_set) + return NULL; + idata.auth = wpa_auth; + idata.cache_id = wpa_auth->conf.fils_cache_id; + idata.pmksa = NULL; + idata.spa = sta_addr; + idata.pmkid = pmkid; + wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata); + return idata.pmksa; +} + + +#ifdef CONFIG_IEEE80211R_AP +int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, + u8 *buf, size_t len) +{ + struct wpa_auth_config *conf = &wpa_auth->conf; + + return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, buf, len, NULL, 0); +} +#endif /* CONFIG_IEEE80211R_AP */ + + +void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, + u8 *fils_anonce, u8 *fils_snonce, + u8 *fils_kek, size_t *fils_kek_len) +{ + os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN); + os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN); + os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN); + *fils_kek_len = sm->PTK.kek_len; +} + + +void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *pmkid) +{ + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + os_memcpy(sm->pmkid, pmkid, PMKID_LEN); + sm->pmkid_set = 1; +} + +#endif /* CONFIG_FILS */ + + +void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg) +{ + if (sm) + sm->auth_alg = auth_alg; +} + + +#ifdef CONFIG_DPP2 +void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z) +{ + if (sm) { + wpabuf_clear_free(sm->dpp_z); + sm->dpp_z = z ? wpabuf_dup(z) : NULL; + } +} +#endif /* CONFIG_DPP2 */ + + +#ifdef CONFIG_TESTING_OPTIONS + +int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + const u8 *anonce = sm->ANonce; + u8 anonce_buf[WPA_NONCE_LEN]; + + if (change_anonce) { + if (random_get_bytes(anonce_buf, WPA_NONCE_LEN)) + return -1; + anonce = anonce_buf; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake (TESTING)"); + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + anonce, NULL, 0, 0, 0); + return 0; +} + + +int wpa_auth_resend_m3(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; +#ifdef CONFIG_IEEE80211W + u8 *opos; +#endif /* CONFIG_IEEE80211W */ + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; + u8 *wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], + GTK[GN], IGTK, [FTIE], [TIE * 2]) + */ + + /* Use 0 RSC */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { + /* WPA-only STA, remove RSN IE and possible MDIE */ + wpa_ie = wpa_ie + wpa_ie[1] + 2; + if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) + wpa_ie = wpa_ie + wpa_ie[1] + 2; + wpa_ie_len = wpa_ie[1] + 2; + } + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake (TESTING)"); + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + keyidx = gsm->GN; + _rsc = rsc; + encr = 1; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + gtk_len = 0; + keyidx = 0; + _rsc = NULL; + if (sm->rx_eapol_key_secure) { + /* + * It looks like Windows 7 supplicant tries to use + * Secure bit in msg 2/4 after having reported Michael + * MIC failure and it then rejects the 4-way handshake + * if msg 3/4 does not set Secure bit. Work around this + * by setting the Secure bit here even in the case of + * WPA if the supplicant used it first. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "STA used Secure bit in WPA msg 2/4 - " + "set Secure for 3/4 as workaround"); + secure = 1; + } + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ + kde_len += 300; /* FTIE + 2 * TIE */ + } +#endif /* CONFIG_IEEE80211R_AP */ + kde = os_malloc(kde_len); + if (kde == NULL) + return -1; + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + size_t elen; + + elen = pos - kde; + res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert " + "PMKR1Name into RSN IE in EAPOL-Key data"); + os_free(kde); + return -1; + } + pos -= wpa_ie_len; + pos += elen; + } +#endif /* CONFIG_IEEE80211R_AP */ + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } +#ifdef CONFIG_IEEE80211W + opos = pos; + pos = ieee80211w_kde_add(sm, pos); + if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) { + /* skip KDE header and keyid */ + opos += 2 + RSN_SELECTOR_LEN + 2; + os_memset(opos, 0, 6); /* clear PN */ + } +#endif /* CONFIG_IEEE80211W */ + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde); + return -1; + } + +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + struct wpa_auth_config *conf; + + conf = &sm->wpa_auth->conf; + if (sm->assoc_resp_ftie && + kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { + os_memcpy(pos, sm->assoc_resp_ftie, + 2 + sm->assoc_resp_ftie[1]); + res = 2 + sm->assoc_resp_ftie[1]; + } else { + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + + res = wpa_write_ftie(conf, use_sha384, + conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, + kde + kde_len - pos, + NULL, 0); + } + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " + "into EAPOL-Key Key Data"); + os_free(kde); + return -1; + } + pos += res; + + /* TIE[ReassociationDeadline] (TU) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; + WPA_PUT_LE32(pos, conf->reassociation_deadline); + pos += 4; + + /* TIE[KeyLifetime] (seconds) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(pos, conf->r0_key_lifetime); + pos += 4; + } +#endif /* CONFIG_IEEE80211R_AP */ + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); + return 0; +} + + +int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_group *gsm = sm->group; + const u8 *kde; + u8 *kde_buf = NULL, *pos, hdr[2]; +#ifdef CONFIG_IEEE80211W + u8 *opos; +#endif /* CONFIG_IEEE80211W */ + size_t kde_len; + u8 *gtk; + + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + /* Use 0 RSC */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake (TESTING)"); + + gtk = gsm->GTK[gsm->GN - 1]; + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm) + ocv_oci_len(sm); + kde_buf = os_malloc(kde_len); + if (kde_buf == NULL) + return -1; + + kde = pos = kde_buf; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gsm->GTK_len); +#ifdef CONFIG_IEEE80211W + opos = pos; + pos = ieee80211w_kde_add(sm, pos); + if (pos - opos >= + 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) { + /* skip KDE header and keyid */ + opos += 2 + RSN_SELECTOR_LEN + 2; + os_memset(opos, 0, 6); /* clear PN */ + } +#endif /* CONFIG_IEEE80211W */ + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde_buf); + return -1; + } + kde_len = pos - kde; + } else { + kde = gtk; + kde_len = gsm->GTK_len; + } + + sm->eapol_status_cb = cb; + sm->eapol_status_cb_ctx1 = ctx1; + sm->eapol_status_cb_ctx2 = ctx2; + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, NULL, kde, kde_len, gsm->GN, 1); + + os_free(kde_buf); + return 0; +} + + +int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth) +{ + if (!wpa_auth) + return -1; + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL); +} + +#endif /* CONFIG_TESTING_OPTIONS */ --- contrib/wpa/src/ap/wpa_auth.h.orig +++ contrib/wpa/src/ap/wpa_auth.h @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,8 @@ #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" +struct vlan_description; + #define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER @@ -37,65 +39,100 @@ #define FT_PACKET_REQUEST 0 #define FT_PACKET_RESPONSE 1 -/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ -#define FT_PACKET_R0KH_R1KH_PULL 200 -#define FT_PACKET_R0KH_R1KH_RESP 201 -#define FT_PACKET_R0KH_R1KH_PUSH 202 -#define FT_R0KH_R1KH_PULL_DATA_LEN 44 -#define FT_R0KH_R1KH_RESP_DATA_LEN 76 -#define FT_R0KH_R1KH_PUSH_DATA_LEN 88 -#define FT_R0KH_R1KH_PULL_NONCE_LEN 16 +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These + * use OUI Extended EtherType as the encapsulating format. */ +#define FT_PACKET_R0KH_R1KH_PULL 0x01 +#define FT_PACKET_R0KH_R1KH_RESP 0x02 +#define FT_PACKET_R0KH_R1KH_PUSH 0x03 +#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04 +#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05 -struct ft_r0kh_r1kh_pull_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ - le16 data_length; /* little endian length of data (44) */ - u8 ap_address[ETH_ALEN]; +/* packet layout + * IEEE 802 extended OUI ethertype frame header + * u16 authlen (little endian) + * multiple of struct ft_rrb_tlv (authenticated only, length = authlen) + * multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra + * blocksize length) + * + * AES-SIV AAD; + * source MAC address (6) + * authenticated-only TLVs (authlen) + * subtype (1; FT_PACKET_*) + */ - u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; - u8 pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 r1kh_id[FT_R1KH_ID_LEN]; - u8 s1kh_id[ETH_ALEN]; - u8 pad[4]; /* 8-octet boundary for AES key wrap */ - u8 key_wrap_extra[8]; -} STRUCT_PACKED; +#define FT_RRB_NONCE_LEN 16 -struct ft_r0kh_r1kh_resp_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ - le16 data_length; /* little endian length of data (76) */ - u8 ap_address[ETH_ALEN]; +#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */ - u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ - u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ - u8 s1kh_id[ETH_ALEN]; /* copied from pull */ - u8 pmk_r1[PMK_LEN]; - u8 pmk_r1_name[WPA_PMK_NAME_LEN]; - le16 pairwise; - u8 pad[2]; /* 8-octet boundary for AES key wrap */ - u8 key_wrap_extra[8]; -} STRUCT_PACKED; +#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */ +#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */ +#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */ -struct ft_r0kh_r1kh_push_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ - le16 data_length; /* little endian length of data (88) */ - u8 ap_address[ETH_ALEN]; +#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */ +#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */ +#define FT_RRB_S1KH_ID 6 /* ETH_ALEN */ - /* Encrypted with AES key-wrap */ - u8 timestamp[4]; /* current time in seconds since unix epoch, little - * endian */ - u8 r1kh_id[FT_R1KH_ID_LEN]; - u8 s1kh_id[ETH_ALEN]; - u8 pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 pmk_r1[PMK_LEN]; - u8 pmk_r1_name[WPA_PMK_NAME_LEN]; - le16 pairwise; - u8 pad[6]; /* 8-octet boundary for AES key wrap */ - u8 key_wrap_extra[8]; +#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */ +#define FT_RRB_PMK_R0 8 /* PMK_LEN */ +#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */ +#define FT_RRB_PMK_R1 10 /* PMK_LEN */ + +#define FT_RRB_PAIRWISE 11 /* le16 */ +#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */ + +#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */ +#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */ + +#define FT_RRB_IDENTITY 15 +#define FT_RRB_RADIUS_CUI 16 +#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */ + +struct ft_rrb_tlv { + le16 type; + le16 len; + /* followed by data of length len */ } STRUCT_PACKED; +struct ft_rrb_seq { + le32 dom; + le32 seq; + le32 ts; +} STRUCT_PACKED; + +/* session TLVs: + * required: PMK_R1, PMK_R1_NAME, PAIRWISE + * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI, + * SESSION_TIMEOUT + * + * pull frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + * encrypted: + * required: PMK_R0_NAME, S1KH_ID + * + * response frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + * encrypted: + * required: S1KH_ID + * optional: session TLVs + * + * push frame TLVs: + * auth: + * required: SEQ, R0KH_ID, R1KH_ID + * encrypted: + * required: S1KH_ID, PMK_R0_NAME, session TLVs + * + * sequence number request frame TLVs: + * auth: + * required: R0KH_ID, R1KH_ID, NONCE + * + * sequence number response frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + */ + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ @@ -107,6 +144,8 @@ struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; +struct ft_remote_seq; +struct wpa_channel_info; struct ft_remote_r0kh { @@ -114,7 +153,8 @@ u8 addr[ETH_ALEN]; u8 id[FT_R0KH_ID_MAX_LEN]; size_t id_len; - u8 key[16]; + u8 key[32]; + struct ft_remote_seq *seq; }; @@ -122,7 +162,8 @@ struct ft_remote_r1kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R1KH_ID_LEN]; - u8 key[16]; + u8 key[32]; + struct ft_remote_seq *seq; }; @@ -135,10 +176,12 @@ int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; + u32 wpa_group_update_count; + u32 wpa_pairwise_update_count; + int wpa_disable_eapol_key_retries; int rsn_pairwise; int rsn_preauth; int eapol_version; - int peerkey; int wmm_enabled; int wmm_uapsd; int disable_pmksa_caching; @@ -147,8 +190,12 @@ #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; int group_mgmt_cipher; + int sae_require_mfp; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_OCV + int ocv; /* Operating Channel Validation */ +#endif /* CONFIG_OCV */ +#ifdef CONFIG_IEEE80211R_AP u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; @@ -155,13 +202,19 @@ u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; size_t r0_key_holder_len; u8 r1_key_holder[FT_R1KH_ID_LEN]; - u32 r0_key_lifetime; + u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */ + int rkh_pos_timeout; + int rkh_neg_timeout; + int rkh_pull_timeout; /* ms */ + int rkh_pull_retries; + int r1_max_key_lifetime; u32 reassociation_deadline; - struct ft_remote_r0kh *r0kh_list; - struct ft_remote_r1kh *r1kh_list; + struct ft_remote_r0kh **r0kh_list; + struct ft_remote_r1kh **r1kh_list; int pmk_r1_push; int ft_over_ds; -#endif /* CONFIG_IEEE80211R */ + int ft_psk_generate_local; +#endif /* CONFIG_IEEE80211R_AP */ int disable_gtk; int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS @@ -175,6 +228,10 @@ u8 ip_addr_start[4]; u8 ip_addr_end[4]; #endif /* CONFIG_P2P */ +#ifdef CONFIG_FILS + unsigned int fils_cache_id_set:1; + u8 fils_cache_id[FILS_CACHE_ID_LEN]; +#endif /* CONFIG_FILS */ }; typedef enum { @@ -188,7 +245,6 @@ } wpa_eapol_variable; struct wpa_auth_callbacks { - void *ctx; void (*logger)(void *ctx, const u8 *addr, logger_level level, const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); @@ -198,7 +254,8 @@ int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk); + const u8 *prev_psk, size_t *psk_len, + int *vlan_id); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len); @@ -211,13 +268,34 @@ void *ctx), void *cb_ctx); int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len); -#ifdef CONFIG_IEEE80211R + int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data, + size_t data_len); + int (*channel_info)(void *ctx, struct wpa_channel_info *ci); + int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id); + int (*get_sta_tx_params)(void *ctx, const u8 *addr, + int ap_max_chanwidth, int ap_seg1_idx, + int *bandwidth, int *seg1_idx); +#ifdef CONFIG_IEEE80211R_AP struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*set_vlan)(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan); + int (*get_vlan)(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan); + int (*set_identity)(void *ctx, const u8 *sta_addr, + const u8 *identity, size_t identity_len); + size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf); + int (*set_radius_cui)(void *ctx, const u8 *sta_addr, + const u8 *radius_cui, size_t radius_cui_len); + size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf); + void (*set_session_timeout)(void *ctx, const u8 *sta_addr, + int session_timeout); + int (*get_session_timeout)(void *ctx, const u8 *sta_addr); + int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_MESH int (*start_ampe)(void *ctx, const u8 *sta_addr); #endif /* CONFIG_MESH */ @@ -225,7 +303,8 @@ struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, - struct wpa_auth_callbacks *cb); + const struct wpa_auth_callbacks *cb, + void *cb_ctx); int wpa_init_keys(struct wpa_authenticator *wpa_auth); void wpa_deinit(struct wpa_authenticator *wpa_auth); int wpa_reconfig(struct wpa_authenticator *wpa_auth, @@ -235,17 +314,20 @@ WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, - WPA_INVALID_MDIE, WPA_INVALID_PROTO + WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID }; - + int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, + struct wpa_state_machine *sm, int freq, const u8 *wpa_ie, size_t wpa_ie_len, - const u8 *mdie, size_t mdie_len); + const u8 *mdie, size_t mdie_len, + const u8 *owe_dh, size_t owe_dh_len); int wpa_validate_osen(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *osen_ie, size_t osen_ie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); +void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv); +int wpa_auth_uses_ocv(struct wpa_state_machine *sm); struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr); @@ -258,7 +340,7 @@ u8 *data, size_t data_len); enum wpa_event { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, - WPA_REAUTH_EAPOL, WPA_ASSOC_FT + WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED }; void wpa_remove_ptk(struct wpa_state_machine *sm); int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); @@ -269,9 +351,11 @@ void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); int wpa_auth_pairwise_set(struct wpa_state_machine *sm); int wpa_auth_get_pairwise(struct wpa_state_machine *sm); +const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); +int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm); int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * @@ -280,6 +364,7 @@ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len); int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + unsigned int pmk_len, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, @@ -286,14 +371,38 @@ int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, - const u8 *pmk); + const u8 *pmk, const u8 *pmkid); +void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid); +int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, + int session_timeout, int akmp); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, + size_t len); +void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth); +int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, + char *buf, size_t len); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, + const u8 *pmkid, int expiration); +int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *pmkid); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, const u8 *pmkid); +void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, + struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, + u8 *pmkid, u8 *pmk); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len); @@ -308,8 +417,13 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len); +void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, const u8 *data, + size_t data_len); void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); -#endif /* CONFIG_IEEE80211R */ +void wpa_ft_deinit(struct wpa_authenticator *wpa_auth); +void wpa_ft_sta_deinit(struct wpa_state_machine *sm); +#endif /* CONFIG_IEEE80211R_AP */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); @@ -326,4 +440,54 @@ struct radius_das_attrs *attr); void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); +int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id); +int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id); +int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *snonce, const u8 *anonce, + const u8 *dhss, size_t dhss_len, + struct wpabuf *g_sta, struct wpabuf *g_ap); +int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, + const struct ieee80211_mgmt *mgmt, size_t frame_len, + u8 *pos, size_t left); +int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, + size_t current_len, size_t max_len, + const struct wpabuf *hlp); +int fils_set_tk(struct wpa_state_machine *sm); +u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid, + const u8 *fils_session, + struct wpabuf *fils_hlp_resp); +const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *fils_session); +int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len); + +int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx); + +int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, + 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); +void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *pmkid); +u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, + const u8 *req_ies, size_t req_ies_len); +void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg); +void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z); + +int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_resend_m3(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth); +void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm); + #endif /* WPA_AUTH_H */ --- contrib/wpa/src/ap/wpa_auth_ft.c.orig +++ contrib/wpa/src/ap/wpa_auth_ft.c @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2018, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,7 +13,12 @@ #include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/ocv.h" +#include "drivers/driver.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" #include "crypto/aes_wrap.h" +#include "crypto/sha384.h" #include "crypto/random.h" #include "ap_config.h" #include "ieee802_11.h" @@ -22,57 +27,762 @@ #include "wpa_auth_i.h" -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP +const unsigned int ftRRBseqTimeout = 10; +const unsigned int ftRRBmaxQueueLen = 100; + + static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, const u8 *current_ap, const u8 *sta_addr, u16 status, const u8 *resp_ies, size_t resp_ies_len); +static void ft_finish_pull(struct wpa_state_machine *sm); +static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx); +static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx); +struct tlv_list { + u16 type; + size_t len; + const u8 *data; +}; + +/** + * wpa_ft_rrb_decrypt - Decrypt FT RRB message + * @key: AES-SIV key for AEAD + * @key_len: Length of key in octets + * @enc: Pointer to encrypted TLVs + * @enc_len: Length of encrypted TLVs in octets + * @auth: Pointer to authenticated TLVs + * @auth_len: Length of authenticated TLVs in octets + * @src_addr: MAC address of the frame sender + * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*) + * @plain: Pointer to return the pointer to the allocated plaintext buffer; + * needs to be freed by the caller if not NULL; + * will only be returned on success + * @plain_len: Pointer to return the length of the allocated plaintext buffer + * in octets + * Returns: 0 on success, -1 on error + */ +static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, + const u8 *enc, size_t enc_len, + const u8 *auth, const size_t auth_len, + const u8 *src_addr, u8 type, + u8 **plain, size_t *plain_size) +{ + const u8 *ad[3] = { src_addr, auth, &type }; + size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + + wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u", + MAC2STR(src_addr), type); + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len); + + if (!key) { /* skip decryption */ + *plain = os_memdup(enc, enc_len); + if (enc_len > 0 && !*plain) + goto err; + + *plain_size = enc_len; + + return 0; + } + + *plain = NULL; + + /* SIV overhead */ + if (enc_len < AES_BLOCK_SIZE) + goto err; + + *plain = os_zalloc(enc_len - AES_BLOCK_SIZE); + if (!*plain) + goto err; + + if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, + *plain) < 0) { + if (enc_len < AES_BLOCK_SIZE + 2) + goto err; + + /* Try to work around Ethernet devices that add extra + * two octet padding even if the frame is longer than + * the minimum Ethernet frame. */ + enc_len -= 2; + if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, + *plain) < 0) + goto err; + } + + *plain_size = enc_len - AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs", + *plain, *plain_size); + return 0; +err: + os_free(*plain); + *plain = NULL; + *plain_size = 0; + + wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt"); + + return -1; +} + + +/* get first tlv record in packet matching type + * @data (decrypted) packet + * @return 0 on success else -1 + */ +static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len, + u16 type, size_t *tlv_len, const u8 **tlv_data) +{ + const struct ft_rrb_tlv *f; + size_t left; + le16 type16; + size_t len; + + left = plain_len; + type16 = host_to_le16(type); + + while (left >= sizeof(*f)) { + f = (const struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + len = le_to_host16(f->len); + + if (left < len) { + wpa_printf(MSG_DEBUG, "FT: RRB message truncated"); + break; + } + + if (f->type == type16) { + *tlv_len = len; + *tlv_data = plain; + return 0; + } + + left -= len; + plain += len; + } + + return -1; +} + + +static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len) +{ + const struct ft_rrb_tlv *f; + size_t left; + size_t len; + + left = plain_len; + + wpa_printf(MSG_DEBUG, "FT: RRB dump message"); + while (left >= sizeof(*f)) { + f = (const struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + len = le_to_host16(f->len); + + wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu", + le_to_host16(f->type), len); + + if (left < len) { + wpa_printf(MSG_DEBUG, + "FT: RRB message truncated: left %zu bytes, need %zu", + left, len); + break; + } + + wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len); + + left -= len; + plain += len; + } + + if (left > 0) + wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left); + + wpa_printf(MSG_DEBUG, "FT: RRB dump message end"); +} + + +static int cmp_int(const void *a, const void *b) +{ + int x, y; + + x = *((int *) a); + y = *((int *) b); + return x - y; +} + + +static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len, + struct vlan_description *vlan) +{ + struct ft_rrb_tlv *f; + size_t left; + size_t len; + int taggedidx; + int vlan_id; + int type; + + left = plain_len; + taggedidx = 0; + os_memset(vlan, 0, sizeof(*vlan)); + + while (left >= sizeof(*f)) { + f = (struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + + len = le_to_host16(f->len); + type = le_to_host16(f->type); + + if (left < len) { + wpa_printf(MSG_DEBUG, "FT: RRB message truncated"); + return -1; + } + + if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED) + goto skip; + + if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN_UNTAGGED invalid length"); + return -1; + } + + if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN_TAGGED invalid length"); + return -1; + } + + while (len >= sizeof(le16)) { + vlan_id = WPA_GET_LE16(plain); + plain += sizeof(le16); + left -= sizeof(le16); + len -= sizeof(le16); + + if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN ID invalid %d", + vlan_id); + continue; + } + + if (type == FT_RRB_VLAN_UNTAGGED) + vlan->untagged = vlan_id; + + if (type == FT_RRB_VLAN_TAGGED && + taggedidx < MAX_NUM_TAGGED_VLAN) { + vlan->tagged[taggedidx] = vlan_id; + taggedidx++; + } else if (type == FT_RRB_VLAN_TAGGED) { + wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs"); + } + } + + skip: + left -= len; + plain += len; + } + + if (taggedidx) + qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int); + + vlan->notempty = vlan->untagged || vlan->tagged[0]; + + return 0; +} + + +static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs) +{ + size_t tlv_len = 0; + int i; + + if (!tlvs) + return 0; + + for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { + tlv_len += sizeof(struct ft_rrb_tlv); + tlv_len += tlvs[i].len; + } + + return tlv_len; +} + + +static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start, + u8 *endpos) +{ + int i; + size_t tlv_len; + struct ft_rrb_tlv *hdr; + u8 *pos; + + if (!tlvs) + return 0; + + tlv_len = 0; + pos = start; + for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { + if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start)) + return tlv_len; + tlv_len += sizeof(*hdr); + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(tlvs[i].type); + hdr->len = host_to_le16(tlvs[i].len); + pos = start + tlv_len; + + if (tlv_len + tlvs[i].len > (size_t) (endpos - start)) + return tlv_len; + if (tlvs[i].len == 0) + continue; + tlv_len += tlvs[i].len; + os_memcpy(pos, tlvs[i].data, tlvs[i].len); + pos = start + tlv_len; + } + + return tlv_len; +} + + +static size_t wpa_ft_vlan_len(const struct vlan_description *vlan) +{ + size_t tlv_len = 0; + int i; + + if (!vlan || !vlan->notempty) + return 0; + + if (vlan->untagged) { + tlv_len += sizeof(struct ft_rrb_tlv); + tlv_len += sizeof(le16); + } + if (vlan->tagged[0]) + tlv_len += sizeof(struct ft_rrb_tlv); + for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) + tlv_len += sizeof(le16); + + return tlv_len; +} + + +static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan, + u8 *start, u8 *endpos) +{ + size_t tlv_len; + int i, len; + struct ft_rrb_tlv *hdr; + u8 *pos = start; + + if (!vlan || !vlan->notempty) + return 0; + + tlv_len = 0; + if (vlan->untagged) { + tlv_len += sizeof(*hdr); + if (start + tlv_len > endpos) + return tlv_len; + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED); + hdr->len = host_to_le16(sizeof(le16)); + pos = start + tlv_len; + + tlv_len += sizeof(le16); + if (start + tlv_len > endpos) + return tlv_len; + WPA_PUT_LE16(pos, vlan->untagged); + pos = start + tlv_len; + } + + if (!vlan->tagged[0]) + return tlv_len; + + tlv_len += sizeof(*hdr); + if (start + tlv_len > endpos) + return tlv_len; + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED); + len = 0; /* len is computed below */ + pos = start + tlv_len; + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) { + tlv_len += sizeof(le16); + if (start + tlv_len > endpos) + break; + len += sizeof(le16); + WPA_PUT_LE16(pos, vlan->tagged[i]); + pos = start + tlv_len; + } + + hdr->len = host_to_le16(len); + + return tlv_len; +} + + +static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1, + const struct tlv_list *tlvs2, + const struct vlan_description *vlan, + u8 **plain, size_t *plain_len) +{ + u8 *pos, *endpos; + size_t tlv_len; + + tlv_len = wpa_ft_tlv_len(tlvs1); + tlv_len += wpa_ft_tlv_len(tlvs2); + tlv_len += wpa_ft_vlan_len(vlan); + + *plain_len = tlv_len; + *plain = os_zalloc(tlv_len); + if (!*plain) { + wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext"); + goto err; + } + + pos = *plain; + endpos = *plain + tlv_len; + pos += wpa_ft_tlv_lin(tlvs1, pos, endpos); + pos += wpa_ft_tlv_lin(tlvs2, pos, endpos); + pos += wpa_ft_vlan_lin(vlan, pos, endpos); + + /* sanity check */ + if (pos != endpos) { + wpa_printf(MSG_ERROR, "FT: Length error building RRB"); + goto err; + } + + return 0; + +err: + os_free(*plain); + *plain = NULL; + *plain_len = 0; + return -1; +} + + +static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, + const u8 *plain, const size_t plain_len, + const u8 *auth, const size_t auth_len, + const u8 *src_addr, u8 type, u8 *enc) +{ + const u8 *ad[3] = { src_addr, auth, &type }; + size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + + wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u", + MAC2STR(src_addr), type); + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message", + plain, plain_len); + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len); + + if (!key) { + /* encryption not needed, return plaintext as packet */ + os_memcpy(enc, plain, plain_len); + } else if (aes_siv_encrypt(key, key_len, plain, plain_len, + 3, ad, ad_len, enc) < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", + enc, plain_len + AES_BLOCK_SIZE); + + return 0; +} + + +/** + * wpa_ft_rrb_build - Build and encrypt an FT RRB message + * @key: AES-SIV key for AEAD + * @key_len: Length of key in octets + * @tlvs_enc0: First set of to-be-encrypted TLVs + * @tlvs_enc1: Second set of to-be-encrypted TLVs + * @tlvs_auth: Set of to-be-authenticated TLVs + * @src_addr: MAC address of the frame sender + * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*) + * @packet Pointer to return the pointer to the allocated packet buffer; + * needs to be freed by the caller if not null; + * will only be returned on success + * @packet_len: Pointer to return the length of the allocated buffer in octets + * Returns: 0 on success, -1 on error + */ +static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, + const struct tlv_list *tlvs_enc0, + const struct tlv_list *tlvs_enc1, + const struct tlv_list *tlvs_auth, + const struct vlan_description *vlan, + const u8 *src_addr, u8 type, + u8 **packet, size_t *packet_len) +{ + u8 *plain = NULL, *auth = NULL, *pos, *tmp; + size_t plain_len = 0, auth_len = 0; + int ret = -1; + size_t pad_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, NULL, &auth, &auth_len) < 0) + goto out; + + *packet_len = sizeof(u16) + auth_len + plain_len; + if (key) + *packet_len += AES_BLOCK_SIZE; +#define RRB_MIN_MSG_LEN 64 + if (*packet_len < RRB_MIN_MSG_LEN) { + pad_len = RRB_MIN_MSG_LEN - *packet_len; + if (pad_len < sizeof(struct ft_rrb_tlv)) + pad_len = sizeof(struct ft_rrb_tlv); + wpa_printf(MSG_DEBUG, + "FT: Pad message to minimum Ethernet frame length (%d --> %d)", + (int) *packet_len, (int) (*packet_len + pad_len)); + *packet_len += pad_len; + tmp = os_realloc(auth, auth_len + pad_len); + if (!tmp) + goto out; + auth = tmp; + pos = auth + auth_len; + WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY); + pos += 2; + WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv)); + pos += 2; + os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv)); + auth_len += pad_len; + + } + *packet = os_zalloc(*packet_len); + if (!*packet) + goto out; + + pos = *packet; + WPA_PUT_LE16(pos, auth_len); + pos += 2; + os_memcpy(pos, auth, auth_len); + pos += auth_len; + if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth, + auth_len, src_addr, type, pos) < 0) + goto out; + wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len); + + ret = 0; + +out: + bin_clear_free(plain, plain_len); + os_free(auth); + + if (ret) { + wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message"); + os_free(*packet); + *packet = NULL; + *packet_len = 0; + } + + return ret; +} + + +#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \ + if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \ + &f_##field##_len, &f_##field) < 0 || \ + (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ + wpa_printf(MSG_INFO, "FT: Missing required " #field \ + " in %s from " MACSTR, txt, MAC2STR(src_addr)); \ + wpa_ft_rrb_dump(srcfield, srcfield##_len); \ + goto out; \ + } \ +} while (0) + +#define RRB_GET(type, field, txt, checklength) \ + RRB_GET_SRC(plain, type, field, txt, checklength) +#define RRB_GET_AUTH(type, field, txt, checklength) \ + RRB_GET_SRC(auth, type, field, txt, checklength) + +#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \ + if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \ + &f_##field##_len, &f_##field) < 0 || \ + (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ + wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \ + " in %s from " MACSTR, txt, MAC2STR(src_addr)); \ + f_##field##_len = 0; \ + f_##field = NULL; \ + } \ +} while (0) + +#define RRB_GET_OPTIONAL(type, field, txt, checklength) \ + RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength) +#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \ + RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength) + static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { - if (wpa_auth->cb.send_ether == NULL) + if (wpa_auth->cb->send_ether == NULL) return -1; wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst)); - return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, - data, data_len); + return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB, + data, data_len); } +static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth, + const u8 *dst, u8 oui_suffix, + const u8 *data, size_t data_len) +{ + if (!wpa_auth->cb->send_oui) + return -1; + wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)", + oui_suffix, MAC2STR(dst), (unsigned int) data_len); + return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data, + data_len); +} + + static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { - if (wpa_auth->cb.send_ft_action == NULL) + if (wpa_auth->cb->send_ft_action == NULL) return -1; - return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, - data, data_len); + return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst, + data, data_len); } +static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk) +{ + if (wpa_auth->cb->get_psk == NULL) + return NULL; + return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, + prev_psk, NULL, NULL); +} + + static struct wpa_state_machine * wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { - if (wpa_auth->cb.add_sta == NULL) + if (wpa_auth->cb->add_sta == NULL) return NULL; - return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); + return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr); } +static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, struct vlan_description *vlan) +{ + if (!wpa_auth->cb->set_vlan) + return -1; + return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan); +} + + +static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, struct vlan_description *vlan) +{ + if (!wpa_auth->cb->get_vlan) + return -1; + return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan); +} + + +static int +wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *identity, size_t identity_len) +{ + if (!wpa_auth->cb->set_identity) + return -1; + return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity, + identity_len); +} + + +static size_t +wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 **buf) +{ + *buf = NULL; + if (!wpa_auth->cb->get_identity) + return 0; + return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf); +} + + +static int +wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *radius_cui, size_t radius_cui_len) +{ + if (!wpa_auth->cb->set_radius_cui) + return -1; + return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr, + radius_cui, radius_cui_len); +} + + +static size_t +wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 **buf) +{ + *buf = NULL; + if (!wpa_auth->cb->get_radius_cui) + return 0; + return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf); +} + + +static void +wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, int session_timeout) +{ + if (!wpa_auth->cb->set_session_timeout) + return; + wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr, + session_timeout); +} + + +static int +wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr) +{ + if (!wpa_auth->cb->get_session_timeout) + return 0; + return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr); +} + + static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { - if (wpa_auth->cb.add_tspec == NULL) { + if (wpa_auth->cb->add_tspec == NULL) { wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); return -1; } - return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, - tspec_ielen); + return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie, + tspec_ielen); } +#ifdef CONFIG_OCV +static int wpa_channel_info(struct wpa_authenticator *wpa_auth, + struct wpa_channel_info *ci) +{ + if (!wpa_auth->cb->channel_info) + return -1; + return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci); +} +#endif /* CONFIG_OCV */ + + int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; @@ -93,16 +803,17 @@ } -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; @@ -109,15 +820,28 @@ *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; *pos++ = FT_R1KH_ID_LEN; @@ -142,35 +866,434 @@ } +/* A packet to be handled after seq response */ +struct ft_remote_item { + struct dl_list list; + + u8 nonce[FT_RRB_NONCE_LEN]; + struct os_reltime nonce_ts; + + u8 src_addr[ETH_ALEN]; + u8 *enc; + size_t enc_len; + u8 *auth; + size_t auth_len; + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer); +}; + + +static void wpa_ft_rrb_seq_free(struct ft_remote_item *item) +{ + eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item); + dl_list_del(&item->list); + bin_clear_free(item->enc, item->enc_len); + os_free(item->auth); + os_free(item); +} + + +static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, int cb) +{ + struct ft_remote_item *item, *n; + + dl_list_for_each_safe(item, n, &rkh_seq->rx.queue, + struct ft_remote_item, list) { + if (cb && item->cb) + item->cb(wpa_auth, item->src_addr, item->enc, + item->enc_len, item->auth, item->auth_len, 1); + wpa_ft_rrb_seq_free(item); + } +} + + +static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct ft_remote_item *item = timeout_ctx; + + wpa_ft_rrb_seq_free(item); +} + + +static int +wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *f_r0kh_id, size_t f_r0kh_id_len, + const u8 *f_r1kh_id, const u8 *key, size_t key_len, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer)) +{ + struct ft_remote_item *item = NULL; + u8 *packet = NULL; + size_t packet_len; + struct tlv_list seq_req_auth[] = { + { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, + .data = NULL /* to be filled: item->nonce */ }, + { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len, + .data = f_r0kh_id }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = f_r1kh_id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) { + wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long"); + goto err; + } + + wpa_printf(MSG_DEBUG, "FT: Send out sequence number request to " MACSTR, + MAC2STR(src_addr)); + item = os_zalloc(sizeof(*item)); + if (!item) + goto err; + + os_memcpy(item->src_addr, src_addr, ETH_ALEN); + item->cb = cb; + + if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes"); + goto err; + } + + if (os_get_reltime(&item->nonce_ts) < 0) + goto err; + + if (enc && enc_len > 0) { + item->enc = os_memdup(enc, enc_len); + item->enc_len = enc_len; + if (!item->enc) + goto err; + } + + if (auth && auth_len > 0) { + item->auth = os_memdup(auth, auth_len); + item->auth_len = auth_len; + if (!item->auth) + goto err; + } + + eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout, + wpa_auth, item); + + seq_req_auth[0].data = item->nonce; + + if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL, + wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + &packet, &packet_len) < 0) { + item = NULL; /* some other seq resp might still accept this */ + goto err; + } + + dl_list_add(&rkh_seq->rx.queue, &item->list); + + wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + packet, packet_len); + + os_free(packet); + + return 0; +err: + wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request"); + if (item) { + os_free(item->auth); + bin_clear_free(item->enc, item->enc_len); + os_free(item); + } + + return -1; +} + + +#define FT_RRB_SEQ_OK 0 +#define FT_RRB_SEQ_DROP 1 +#define FT_RRB_SEQ_DEFER 2 + +static int +wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + const char *msgtype, int no_defer) +{ + const u8 *f_seq; + size_t f_seq_len; + const struct ft_rrb_seq *msg_both; + u32 msg_seq, msg_off, rkh_off; + struct os_reltime now; + unsigned int i; + + RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); + wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len); + msg_both = (const struct ft_rrb_seq *) f_seq; + + if (rkh_seq->rx.num_last == 0) { + /* first packet from remote */ + goto defer; + } + + if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) { + /* remote might have rebooted */ + goto defer; + } + + if (os_get_reltime(&now) == 0) { + u32 msg_ts_now_remote, msg_ts_off; + struct os_reltime now_remote; + + os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote); + msg_ts_now_remote = now_remote.sec; + msg_ts_off = le_to_host32(msg_both->ts) - + (msg_ts_now_remote - ftRRBseqTimeout); + if (msg_ts_off > 2 * ftRRBseqTimeout) + goto defer; + } + + msg_seq = le_to_host32(msg_both->seq); + rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; + msg_off = msg_seq - rkh_off; + if (msg_off > 0xC0000000) + goto out; /* too old message, drop it */ + + if (msg_off <= 0x40000000) { + for (i = 0; i < rkh_seq->rx.num_last; i++) { + if (rkh_seq->rx.last[i] == msg_seq) + goto out; /* duplicate message, drop it */ + } + + return FT_RRB_SEQ_OK; + } + +defer: + if (no_defer) + goto out; + + wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from " + MACSTR, msgtype, MAC2STR(src_addr)); + + return FT_RRB_SEQ_DEFER; +out: + wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR, + msgtype, MAC2STR(src_addr)); + + return FT_RRB_SEQ_DROP; +} + + +static void +wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *auth, size_t auth_len, + const char *msgtype) +{ + const u8 *f_seq; + size_t f_seq_len; + const struct ft_rrb_seq *msg_both; + u32 msg_seq, msg_off, min_off, rkh_off; + int minidx = 0; + unsigned int i; + + RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); + msg_both = (const struct ft_rrb_seq *) f_seq; + + msg_seq = le_to_host32(msg_both->seq); + + if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) { + rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq; + rkh_seq->rx.num_last++; + return; + } + + rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; + for (i = 0; i < rkh_seq->rx.num_last; i++) { + msg_off = rkh_seq->rx.last[i] - rkh_off; + min_off = rkh_seq->rx.last[minidx] - rkh_off; + if (msg_off < min_off && i != rkh_seq->rx.offsetidx) + minidx = i; + } + rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq; + rkh_seq->rx.offsetidx = minidx; + + return; +out: + /* RRB_GET_AUTH should never fail here as + * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */ + wpa_printf(MSG_ERROR, "FT: %s() failed", __func__); +} + + +static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq, + struct ft_rrb_seq *f_seq) +{ + struct os_reltime now; + + if (os_get_reltime(&now) < 0) + return -1; + + if (!rkh_seq->tx.dom) { + if (random_get_bytes((u8 *) &rkh_seq->tx.seq, + sizeof(rkh_seq->tx.seq))) { + wpa_printf(MSG_ERROR, + "FT: Failed to get random data for sequence number initialization"); + rkh_seq->tx.seq = now.usec; + } + if (random_get_bytes((u8 *) &rkh_seq->tx.dom, + sizeof(rkh_seq->tx.dom))) { + wpa_printf(MSG_ERROR, + "FT: Failed to get random data for sequence number initialization"); + rkh_seq->tx.dom = now.usec; + } + rkh_seq->tx.dom |= 1; + } + + f_seq->dom = host_to_le32(rkh_seq->tx.dom); + f_seq->seq = host_to_le32(rkh_seq->tx.seq); + f_seq->ts = host_to_le32(now.sec); + + rkh_seq->tx.seq++; + + return 0; +} + + struct wpa_ft_pmk_r0_sa { - struct wpa_ft_pmk_r0_sa *next; - u8 pmk_r0[PMK_LEN]; + struct dl_list list; + u8 pmk_r0[PMK_LEN_MAX]; + size_t pmk_r0_len; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + struct vlan_description *vlan; + os_time_t expiration; /* 0 for no expiration */ + u8 *identity; + size_t identity_len; + u8 *radius_cui; + size_t radius_cui_len; + os_time_t session_timeout; /* 0 for no expiration */ + /* TODO: radius_class, EAP type */ int pmk_r1_pushed; }; struct wpa_ft_pmk_r1_sa { - struct wpa_ft_pmk_r1_sa *next; - u8 pmk_r1[PMK_LEN]; + struct dl_list list; + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + struct vlan_description *vlan; + u8 *identity; + size_t identity_len; + u8 *radius_cui; + size_t radius_cui_len; + os_time_t session_timeout; /* 0 for no expiration */ + /* TODO: radius_class, EAP type */ }; struct wpa_ft_pmk_cache { - struct wpa_ft_pmk_r0_sa *pmk_r0; - struct wpa_ft_pmk_r1_sa *pmk_r1; + struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */ + struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */ }; + +static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx); +static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx); + + +static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0) +{ + if (!r0) + return; + + dl_list_del(&r0->list); + eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL); + + os_memset(r0->pmk_r0, 0, PMK_LEN_MAX); + os_free(r0->vlan); + os_free(r0->identity); + os_free(r0->radius_cui); + os_free(r0); +} + + +static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx; + struct os_reltime now; + int expires_in; + int session_timeout; + + os_get_reltime(&now); + + if (!r0) + return; + + expires_in = r0->expiration - now.sec; + session_timeout = r0->session_timeout - now.sec; + /* conditions to remove from cache: + * a) r0->expiration is set and hit + * -or- + * b) r0->session_timeout is set and hit + */ + if ((!r0->expiration || expires_in > 0) && + (!r0->session_timeout || session_timeout > 0)) { + wpa_printf(MSG_ERROR, + "FT: %s() called for non-expired entry %p", + __func__, r0); + eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL); + if (r0->expiration && expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); + if (r0->session_timeout && session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); + return; + } + + wpa_ft_free_pmk_r0(r0); +} + + +static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1) +{ + if (!r1) + return; + + dl_list_del(&r1->list); + eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL); + + os_memset(r1->pmk_r1, 0, PMK_LEN_MAX); + os_free(r1->vlan); + os_free(r1->identity); + os_free(r1->radius_cui); + os_free(r1); +} + + +static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx; + + wpa_ft_free_pmk_r1(r1); +} + + struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) { struct wpa_ft_pmk_cache *cache; cache = os_zalloc(sizeof(*cache)); + if (cache) { + dl_list_init(&cache->pmk_r0); + dl_list_init(&cache->pmk_r1); + } return cache; } @@ -181,21 +1304,13 @@ struct wpa_ft_pmk_r0_sa *r0, *r0prev; struct wpa_ft_pmk_r1_sa *r1, *r1prev; - r0 = cache->pmk_r0; - while (r0) { - r0prev = r0; - r0 = r0->next; - os_memset(r0prev->pmk_r0, 0, PMK_LEN); - os_free(r0prev); - } + dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0, + struct wpa_ft_pmk_r0_sa, list) + wpa_ft_free_pmk_r0(r0); - r1 = cache->pmk_r1; - while (r1) { - r1prev = r1; - r1 = r1->next; - os_memset(r1prev->pmk_r1, 0, PMK_LEN); - os_free(r1prev); - } + dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1, + struct wpa_ft_pmk_r1_sa, list) + wpa_ft_free_pmk_r1(r1); os_free(cache); } @@ -203,24 +1318,63 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0, - const u8 *pmk_r0_name, int pairwise) + size_t pmk_r0_len, + const u8 *pmk_r0_name, int pairwise, + const struct vlan_description *vlan, + int expires_in, int session_timeout, + const u8 *identity, size_t identity_len, + const u8 *radius_cui, size_t radius_cui_len) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; + struct os_reltime now; - /* TODO: add expiration and limit on number of entries in cache */ + /* TODO: add limit on number of entries in cache */ + os_get_reltime(&now); r0 = os_zalloc(sizeof(*r0)); if (r0 == NULL) return -1; - os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); + os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len); + r0->pmk_r0_len = pmk_r0_len; os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(r0->spa, spa, ETH_ALEN); r0->pairwise = pairwise; + if (expires_in > 0) + r0->expiration = now.sec + expires_in; + if (vlan && vlan->notempty) { + r0->vlan = os_zalloc(sizeof(*vlan)); + if (!r0->vlan) { + bin_clear_free(r0, sizeof(*r0)); + return -1; + } + *r0->vlan = *vlan; + } + if (identity) { + r0->identity = os_malloc(identity_len); + if (r0->identity) { + os_memcpy(r0->identity, identity, identity_len); + r0->identity_len = identity_len; + } + } + if (radius_cui) { + r0->radius_cui = os_malloc(radius_cui_len); + if (r0->radius_cui) { + os_memcpy(r0->radius_cui, radius_cui, radius_cui_len); + r0->radius_cui_len = radius_cui_len; + } + } + if (session_timeout > 0) + r0->session_timeout = now.sec + session_timeout; - r0->next = cache->pmk_r0; - cache->pmk_r0 = r0; + dl_list_add(&cache->pmk_r0, &r0->list); + if (expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0, + r0, NULL); + if (session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); return 0; } @@ -228,25 +1382,23 @@ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0_name, - u8 *pmk_r0, int *pairwise) + const struct wpa_ft_pmk_r0_sa **r0_out) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; + struct os_reltime now; - r0 = cache->pmk_r0; - while (r0) { + os_get_reltime(&now); + dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) { if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && os_memcmp_const(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) == 0) { - os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); - if (pairwise) - *pairwise = r0->pairwise; + *r0_out = r0; return 0; } - - r0 = r0->next; } + *r0_out = NULL; return -1; } @@ -253,25 +1405,67 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1, - const u8 *pmk_r1_name, int pairwise) + size_t pmk_r1_len, + const u8 *pmk_r1_name, int pairwise, + const struct vlan_description *vlan, + int expires_in, int session_timeout, + const u8 *identity, size_t identity_len, + const u8 *radius_cui, size_t radius_cui_len) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + int max_expires_in = wpa_auth->conf.r1_max_key_lifetime; struct wpa_ft_pmk_r1_sa *r1; + struct os_reltime now; - /* TODO: add expiration and limit on number of entries in cache */ + /* TODO: limit on number of entries in cache */ + os_get_reltime(&now); + if (max_expires_in && (max_expires_in < expires_in || expires_in == 0)) + expires_in = max_expires_in; + r1 = os_zalloc(sizeof(*r1)); if (r1 == NULL) return -1; - os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); + os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len); + r1->pmk_r1_len = pmk_r1_len; os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); os_memcpy(r1->spa, spa, ETH_ALEN); r1->pairwise = pairwise; + if (vlan && vlan->notempty) { + r1->vlan = os_zalloc(sizeof(*vlan)); + if (!r1->vlan) { + bin_clear_free(r1, sizeof(*r1)); + return -1; + } + *r1->vlan = *vlan; + } + if (identity) { + r1->identity = os_malloc(identity_len); + if (r1->identity) { + os_memcpy(r1->identity, identity, identity_len); + r1->identity_len = identity_len; + } + } + if (radius_cui) { + r1->radius_cui = os_malloc(radius_cui_len); + if (r1->radius_cui) { + os_memcpy(r1->radius_cui, radius_cui, radius_cui_len); + r1->radius_cui_len = radius_cui_len; + } + } + if (session_timeout > 0) + r1->session_timeout = now.sec + session_timeout; - r1->next = cache->pmk_r1; - cache->pmk_r1 = r1; + dl_list_add(&cache->pmk_r1, &r1->list); + if (expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1, + r1, NULL); + if (session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r1, r1, NULL); + return 0; } @@ -278,23 +1472,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 if (session_timeout) + *session_timeout = 0; return 0; } - - r1 = r1->next; } return -1; @@ -301,20 +1519,463 @@ } +static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh) +{ + if (r0kh->seq) + return 0; + + r0kh->seq = os_zalloc(sizeof(*r0kh->seq)); + if (!r0kh->seq) { + wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq"); + return -1; + } + + dl_list_init(&r0kh->seq->rx.queue); + + return 0; +} + + +static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len, + struct ft_remote_r0kh **r0kh_out, + struct ft_remote_r0kh **r0kh_wildcard) +{ + struct ft_remote_r0kh *r0kh; + + *r0kh_wildcard = NULL; + *r0kh_out = NULL; + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + for (; r0kh; r0kh = r0kh->next) { + if (r0kh->id_len == 1 && r0kh->id[0] == '*') + *r0kh_wildcard = r0kh; + if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len && + os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0) + *r0kh_out = r0kh; + } + + if (!*r0kh_out && !*r0kh_wildcard) + wpa_printf(MSG_DEBUG, "FT: No matching R0KH found"); + + if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0) + *r0kh_out = NULL; +} + + +static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh) +{ + if (r1kh->seq) + return 0; + + r1kh->seq = os_zalloc(sizeof(*r1kh->seq)); + if (!r1kh->seq) { + wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq"); + return -1; + } + + dl_list_init(&r1kh->seq->rx.queue); + + return 0; +} + + +static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r1kh_id, + struct ft_remote_r1kh **r1kh_out, + struct ft_remote_r1kh **r1kh_wildcard) +{ + struct ft_remote_r1kh *r1kh; + + *r1kh_wildcard = NULL; + *r1kh_out = NULL; + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + for (; r1kh; r1kh = r1kh->next) { + if (is_zero_ether_addr(r1kh->addr) && + is_zero_ether_addr(r1kh->id)) + *r1kh_wildcard = r1kh; + if (f_r1kh_id && + os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0) + *r1kh_out = r1kh; + } + + if (!*r1kh_out && !*r1kh_wildcard) + wpa_printf(MSG_DEBUG, "FT: No matching R1KH found"); + + if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0) + *r1kh_out = NULL; +} + + +static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len) +{ + if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len || + os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder, + f_r0kh_id_len) != 0) + return -1; + + return 0; +} + + +static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r1kh_id) +{ + if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) + return -1; + + return 0; +} + + +static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct ft_remote_r0kh *r0kh, *prev = NULL; + + if (!wpa_auth->conf.r0kh_list) + return; + + for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) { + if (r0kh == timeout_ctx) + break; + prev = r0kh; + } + if (!r0kh) + return; + if (prev) + prev->next = r0kh->next; + else + *wpa_auth->conf.r0kh_list = r0kh->next; + if (r0kh->seq) + wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); + os_free(r0kh->seq); + os_free(r0kh); +} + + +static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh, int timeout) +{ + if (timeout > 0) + eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh, int timeout) +{ + eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh); + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static struct ft_remote_r0kh * +wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh_wildcard, + const u8 *src_addr, const u8 *r0kh_id, size_t id_len, + int timeout) +{ + struct ft_remote_r0kh *r0kh; + + if (!wpa_auth->conf.r0kh_list) + return NULL; + + r0kh = os_zalloc(sizeof(*r0kh)); + if (!r0kh) + return NULL; + + if (src_addr) + os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr)); + + if (id_len > FT_R0KH_ID_MAX_LEN) + id_len = FT_R0KH_ID_MAX_LEN; + os_memcpy(r0kh->id, r0kh_id, id_len); + r0kh->id_len = id_len; + + os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key)); + + r0kh->next = *wpa_auth->conf.r0kh_list; + *wpa_auth->conf.r0kh_list = r0kh; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); + + if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0) + return NULL; + + return r0kh; +} + + +static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct ft_remote_r1kh *r1kh, *prev = NULL; + + if (!wpa_auth->conf.r1kh_list) + return; + + for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + if (r1kh == timeout_ctx) + break; + prev = r1kh; + } + if (!r1kh) + return; + if (prev) + prev->next = r1kh->next; + else + *wpa_auth->conf.r1kh_list = r1kh->next; + if (r1kh->seq) + wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); + os_free(r1kh->seq); + os_free(r1kh); +} + + +static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh, int timeout) +{ + if (timeout > 0) + eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, + wpa_auth, r1kh); +} + + +static struct ft_remote_r1kh * +wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh_wildcard, + const u8 *src_addr, const u8 *r1kh_id, int timeout) +{ + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.r1kh_list) + return NULL; + + r1kh = os_zalloc(sizeof(*r1kh)); + if (!r1kh) + return NULL; + + os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr)); + os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id)); + os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key)); + r1kh->next = *wpa_auth->conf.r1kh_list; + *wpa_auth->conf.r1kh_list = r1kh; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, + wpa_auth, r1kh); + + if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) + return NULL; + + return r1kh; +} + + +void wpa_ft_sta_deinit(struct wpa_state_machine *sm) +{ + eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); +} + + +static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth) +{ + struct ft_remote_r0kh *r0kh; + struct ft_remote_r1kh *r1kh; + + eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX); + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + for (; r0kh; r0kh = r0kh->next) { + if (!r0kh->seq) + continue; + wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); + os_free(r0kh->seq); + r0kh->seq = NULL; + } + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + for (; r1kh; r1kh = r1kh->next) { + if (!r1kh->seq) + continue; + wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); + os_free(r1kh->seq); + r1kh->seq = NULL; + } +} + + +static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth) +{ + struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL; + struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL; + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + while (r0kh) { + r0kh_next = r0kh->next; + if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, + r0kh) > 0) { + if (r0kh_prev) + r0kh_prev->next = r0kh_next; + else + *wpa_auth->conf.r0kh_list = r0kh_next; + os_free(r0kh); + } else { + r0kh_prev = r0kh; + } + r0kh = r0kh_next; + } + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + while (r1kh) { + r1kh_next = r1kh->next; + if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth, + r1kh) > 0) { + if (r1kh_prev) + r1kh_prev->next = r1kh_next; + else + *wpa_auth->conf.r1kh_list = r1kh_next; + os_free(r1kh); + } else { + r1kh_prev = r1kh; + } + r1kh = r1kh_next; + } +} + + +void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) +{ + wpa_ft_deinit_seq(wpa_auth); + wpa_ft_deinit_rkh_tmp(wpa_auth); +} + + +static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len) +{ + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + + if (!wpa_auth->conf.rkh_neg_timeout) + return; + + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, + &r0kh, &r0kh_wildcard); + + if (!r0kh_wildcard) { + /* r0kh removed after neg_timeout and might need re-adding */ + return; + } + + wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID", + f_r0kh_id, f_r0kh_id_len); + + if (r0kh) { + wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh, + wpa_auth->conf.rkh_neg_timeout); + os_memset(r0kh->addr, 0, ETH_ALEN); + } else + wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id, + f_r0kh_id_len, + wpa_auth->conf.rkh_neg_timeout); +} + + +static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + + wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR, + MAC2STR(sm->addr)); + if (sm->ft_pending_pull_left_retries <= 0) + wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len); + + /* cancel multiple timeouts */ + eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); + ft_finish_pull(sm); +} + + static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, const u8 *pmk_r0_name) { - struct ft_remote_r0kh *r0kh; - struct ft_r0kh_r1kh_pull_frame frame, f; + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + u8 *packet = NULL; + const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder; + size_t packet_len, key_len; + struct ft_rrb_seq f_seq; + int tsecs, tusecs, first; + struct wpabuf *ft_pending_req_ies; + int r0kh_timeout; + struct tlv_list req_enc[] = { + { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, + .data = pmk_r0_name }, + { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, + .data = sm->addr }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + struct tlv_list req_auth[] = { + { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, + .data = sm->ft_pending_pull_nonce }, + { .type = FT_RRB_SEQ, .len = sizeof(f_seq), + .data = (u8 *) &f_seq }, + { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len, + .data = sm->r0kh_id }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = f_r1kh_id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; - r0kh = sm->wpa_auth->conf.r0kh_list; - while (r0kh) { - if (r0kh->id_len == sm->r0kh_id_len && - os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) == - 0) - break; - r0kh = r0kh->next; + if (sm->ft_pending_pull_left_retries <= 0) + return -1; + first = sm->ft_pending_pull_left_retries == + sm->wpa_auth->conf.rkh_pull_retries; + sm->ft_pending_pull_left_retries--; + + wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len, + &r0kh, &r0kh_wildcard); + + /* Keep r0kh sufficiently long in the list for seq num check */ + r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 + + 1 + ftRRBseqTimeout; + if (r0kh) { + wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout); + } else if (r0kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); + /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */ + r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard, + r0kh_wildcard->addr, + sm->r0kh_id, sm->r0kh_id_len, + r0kh_timeout); } if (r0kh == NULL) { wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", @@ -321,51 +1982,105 @@ sm->r0kh_id, sm->r0kh_id_len); return -1; } + if (is_zero_ether_addr(r0kh->addr)) { + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, + "FT: R0KH-ID points to self - no matching key available"); + return -1; + } + key = r0kh->key; + key_len = sizeof(r0kh->key); + wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); - os_memset(&frame, 0, sizeof(frame)); - frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; - frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); - os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN); + if (r0kh->seq->rx.num_last == 0) { + /* A sequence request will be sent out anyway when pull + * response is received. Send it out now to avoid one RTT. */ + wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr, + r0kh->id, r0kh->id_len, f_r1kh_id, key, + key_len, NULL, 0, NULL, 0, NULL); + } - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) { + if (first && + random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } - os_memcpy(sm->ft_pending_pull_nonce, f.nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN); - os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); - os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); - os_memset(f.pad, 0, sizeof(f.pad)); - if (aes_wrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - f.nonce, frame.nonce) < 0) + if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); return -1; + } + if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL, + sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL, + &packet, &packet_len) < 0) + return -1; + + ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); wpabuf_free(sm->ft_pending_req_ies); - sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); - if (sm->ft_pending_req_ies == NULL) + sm->ft_pending_req_ies = ft_pending_req_ies; + if (!sm->ft_pending_req_ies) { + os_free(packet); return -1; + } - wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000; + tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000; + eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL); + wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL, + packet, packet_len); + + os_free(packet); + return 0; } -int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk) +int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, + const u8 *pmk_r0, const u8 *pmk_r0_name) { - u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 pmk_r1[PMK_LEN]; + 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, struct wpa_ptk *ptk) +{ + u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; + size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? + SHA384_MAC_LEN : PMK_LEN; + size_t pmk_r1_len = pmk_r0_len; + u8 pmk_r1[PMK_LEN_MAX]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *mdid = sm->wpa_auth->conf.mobility_domain; const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; @@ -373,6 +2088,12 @@ const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; const u8 *ssid = sm->wpa_auth->conf.ssid; size_t ssid_len = sm->wpa_auth->conf.ssid_len; + int psk_local = sm->wpa_auth->conf.ft_psk_generate_local; + int expires_in = sm->wpa_auth->conf.r0_key_lifetime; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len, radius_cui_len; + int session_timeout; if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " @@ -380,23 +2101,45 @@ return -1; } - wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, - r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); + 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, + wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name, - sm->pairwise); + if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) + wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, + pmk_r0_name, + sm->pairwise, &vlan, expires_in, + session_timeout, identity, identity_len, + radius_cui, radius_cui_len); - wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, - pmk_r1, sm->pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); + if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr, + pmk_r1, sm->pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, - sm->pairwise); + if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) + wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len, + sm->pmk_r1_name, sm->pairwise, &vlan, + expires_in, session_timeout, identity, + identity_len, radius_cui, radius_cui_len); - return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, - sm->wpa_auth->addr, sm->pmk_r1_name, + return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, + sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise); } @@ -404,9 +2147,9 @@ static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { - if (wpa_auth->cb.get_seqnum == NULL) + if (wpa_auth->cb->get_seqnum == NULL) return -1; - return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); + return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq); } @@ -418,7 +2161,17 @@ 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)) return NULL; @@ -456,8 +2209,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; } @@ -473,10 +2228,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; @@ -488,9 +2256,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; } @@ -637,17 +2408,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)) @@ -655,14 +2430,28 @@ end = pos + max_len; - if (auth_alg == WLAN_AUTH_FT) { + if (auth_alg == WLAN_AUTH_FT || + ((auth_alg == WLAN_AUTH_FILS_SK || + auth_alg == WLAN_AUTH_FILS_SK_PFS || + auth_alg == WLAN_AUTH_FILS_PK) && + (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)))) { + if (!sm->pmk_r1_name_valid) { + wpa_printf(MSG_ERROR, + "FT: PMKR1Name is not valid for Assoc Resp RSNE"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); /* * RSN (only present if this is a Reassociation Response and - * part of a fast BSS transition) + * part of a fast BSS transition; or if this is a + * (Re)Association Response frame during an FT initial mobility + * domain association using FILS) */ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); if (res < 0) - return pos; + return NULL; rsnie = pos; rsnie_len = res; pos += res; @@ -671,7 +2460,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; @@ -679,6 +2468,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; @@ -690,14 +2484,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); @@ -705,6 +2501,35 @@ os_free(igtk); } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + u8 *nbuf, *ocipos; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element"); + os_free(subelem); + return NULL; + } + + subelem_len += 2 + OCV_OCI_LEN; + nbuf = os_realloc(subelem, subelem_len); + if (!nbuf) { + os_free(subelem); + return NULL; + } + subelem = nbuf; + + ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN; + *ocipos++ = FTIE_SUBELEM_OCI; + *ocipos++ = OCV_OCI_LEN; + if (ocv_insert_oci(&ci, &ocipos) < 0) { + os_free(subelem); + return NULL; + } + } +#endif /* CONFIG_OCV */ } else { r0kh_id = conf->r0_key_holder; r0kh_id_len = conf->r0_key_holder_len; @@ -711,30 +2536,38 @@ 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; - 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 (use_sha384) { + struct rsn_ftie_sha384 *_ftie = + (struct rsn_ftie_sha384 *) (ftie + 2); - _ftie = (struct rsn_ftie *) (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); } @@ -741,15 +2574,29 @@ 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) + return NULL; + os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); + return pos; } @@ -759,10 +2606,10 @@ enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { - if (wpa_auth->cb.set_key == NULL) + if (wpa_auth->cb->set_key == NULL) return -1; - return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, - key, key_len); + return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx, + key, key_len); } @@ -804,13 +2651,209 @@ } +/* Derive PMK-R1 from PSK, check all available PSK */ +static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm, + const u8 *req_pmk_r1_name, + u8 *out_pmk_r1, int *out_pairwise, + struct vlan_description *out_vlan, + const u8 **out_identity, size_t *out_identity_len, + const u8 **out_radius_cui, + size_t *out_radius_cui_len, + int *out_session_timeout) +{ + const u8 *pmk = NULL; + u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + const u8 *mdid = wpa_auth->conf.mobility_domain; + const u8 *r0kh = sm->r0kh_id; + size_t r0kh_len = sm->r0kh_id_len; + const u8 *r1kh = wpa_auth->conf.r1_key_holder; + const u8 *ssid = wpa_auth->conf.ssid; + size_t ssid_len = wpa_auth->conf.ssid_len; + int pairwise; + + pairwise = sm->pairwise; + + for (;;) { + pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr, + pmk); + if (pmk == NULL) + break; + + if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh, + r0kh_len, sm->addr, + pmk_r0, pmk_r0_name, 0) < 0 || + wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh, + sm->addr, pmk_r1, pmk_r1_name) < 0 || + os_memcmp_const(pmk_r1_name, req_pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) + continue; + + /* We found a PSK that matches the requested pmk_r1_name */ + wpa_printf(MSG_DEBUG, + "FT: Found PSK to generate PMK-R1 locally"); + os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN); + if (out_pairwise) + *out_pairwise = pairwise; + os_memcpy(sm->PMK, pmk, PMK_LEN); + sm->pmk_len = PMK_LEN; + if (out_vlan && + wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " + MACSTR, MAC2STR(sm->addr)); + return -1; + } + + if (out_identity && out_identity_len) { + *out_identity_len = wpa_ft_get_identity( + sm->wpa_auth, sm->addr, out_identity); + } + + if (out_radius_cui && out_radius_cui_len) { + *out_radius_cui_len = wpa_ft_get_radius_cui( + sm->wpa_auth, sm->addr, out_radius_cui); + } + + if (out_session_timeout) { + *out_session_timeout = wpa_ft_get_session_timeout( + sm->wpa_auth, sm->addr); + } + + return 0; + } + + wpa_printf(MSG_DEBUG, + "FT: Did not find PSK to generate PMK-R1 locally"); + return -1; +} + + +/* Detect the configuration the station asked for. + * Required to detect FT-PSK and pairwise cipher. + */ +static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm, + struct wpa_ft_ies *parse) +{ + int key_mgmt, ciphers; + + if (sm->wpa_key_mgmt) + return 0; + + key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt; + if (!key_mgmt) { + wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from " + MACSTR, parse->key_mgmt, MAC2STR(sm->addr)); + return -1; + } + if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; +#ifdef CONFIG_SHA384 + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +#ifdef CONFIG_FILS + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; +#endif /* CONFIG_FILS */ + ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise; + if (!ciphers) { + wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from " + MACSTR, + parse->pairwise_cipher, MAC2STR(sm->addr)); + return -1; + } + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); + + return 0; +} + + +static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *req_pmk_r0_name, + const u8 *req_pmk_r1_name, + u8 *out_pmk_r1, int *out_pairwise, + struct vlan_description *vlan, + const u8 **identity, size_t *identity_len, + const u8 **radius_cui, + size_t *radius_cui_len, + int *out_session_timeout) +{ + struct wpa_auth_config *conf = &wpa_auth->conf; + const struct wpa_ft_pmk_r0_sa *r0; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + int expires_in = 0; + int session_timeout = 0; + struct os_reltime now; + + if (conf->r0_key_holder_len != r0kh_id_len || + os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) != + 0) + return -1; /* not our R0KH-ID */ + + wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration"); + if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) < + 0) + return -1; /* no matching PMKR0Name in local cache */ + + wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache"); + + if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name, + conf->r1_key_holder, + sm->addr, out_pmk_r1, pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + + os_get_reltime(&now); + if (r0->expiration) + expires_in = r0->expiration - now.sec; + + if (r0->session_timeout) + session_timeout = r0->session_timeout - now.sec; + + wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len, + pmk_r1_name, + sm->pairwise, r0->vlan, expires_in, session_timeout, + r0->identity, r0->identity_len, + r0->radius_cui, r0->radius_cui_len); + + *out_pairwise = sm->pairwise; + if (vlan) { + if (r0->vlan) + *vlan = *r0->vlan; + else + os_memset(vlan, 0, sizeof(*vlan)); + } + + if (identity && identity_len) { + *identity = r0->identity; + *identity_len = r0->identity_len; + } + + if (radius_cui && radius_cui_len) { + *radius_cui = r0->radius_cui; + *radius_cui_len = r0->radius_cui_len; + } + + *out_session_timeout = session_timeout; + + return 0; +} + + static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, u8 **resp_ies, size_t *resp_ies_len) { struct rsn_mdie *mdie; - struct rsn_ftie *ftie; - u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; struct wpa_auth_config *conf; struct wpa_ft_ies parse; @@ -817,7 +2860,12 @@ size_t buflen; int ret; u8 *pos, *end; - int pairwise; + int pairwise, session_timeout = 0; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len = 0, radius_cui_len = 0; + int use_sha384; + size_t pmk_r1_len; *resp_ies = NULL; *resp_ies_len = 0; @@ -828,10 +2876,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) || @@ -842,14 +2892,28 @@ return WLAN_STATUS_INVALID_MDIE; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return WLAN_STATUS_INVALID_FTIE; + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + } 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); } - os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); - if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); return WLAN_STATUS_INVALID_FTIE; @@ -865,28 +2929,62 @@ return WLAN_STATUS_INVALID_PMKID; } + if (wpa_ft_set_key_mgmt(sm, &parse) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", parse.rsn_pmkid, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1_name(parse.rsn_pmkid, - sm->wpa_auth->conf.r1_key_holder, sm->addr, - pmk_r1_name); + if (wpa_derive_pmk_r1_name(parse.rsn_pmkid, + sm->wpa_auth->conf.r1_key_holder, sm->addr, + pmk_r1_name, use_sha384) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); - if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, - &pairwise) < 0) { + if (conf->ft_psk_generate_local && + wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise, + &vlan, &identity, &identity_len, + &radius_cui, &radius_cui_len, + &session_timeout) < 0) + return WLAN_STATUS_INVALID_PMKID; + wpa_printf(MSG_DEBUG, + "FT: Generated PMK-R1 for FT-PSK locally"); + } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, + pmk_r1, &pmk_r1_len, &pairwise, &vlan, + &identity, &identity_len, &radius_cui, + &radius_cui_len, &session_timeout) < 0) { + wpa_printf(MSG_DEBUG, + "FT: No PMK-R1 available in local cache for the requested PMKR1Name"); + if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm, + parse.r0kh_id, parse.r0kh_id_len, + parse.rsn_pmkid, + pmk_r1_name, pmk_r1, &pairwise, + &vlan, &identity, &identity_len, + &radius_cui, &radius_cui_len, + &session_timeout) == 0) { + wpa_printf(MSG_DEBUG, + "FT: Generated PMK-R1 based on local PMK-R0"); + goto pmk_r1_derived; + } + if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) { - wpa_printf(MSG_DEBUG, "FT: Did not have matching " - "PMK-R1 and unknown R0KH-ID"); + wpa_printf(MSG_DEBUG, + "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH"); return WLAN_STATUS_INVALID_PMKID; } return -1; /* Status pending */ + } else { + wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache"); } - wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); +pmk_r1_derived: + wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len); sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len); + sm->pmk_r1_len = pmk_r1_len; if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " @@ -899,8 +2997,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; @@ -910,44 +3008,51 @@ sm->tk_already_set = FALSE; wpa_ft_install_ptk(sm); + if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + if (wpa_ft_set_identity(sm->wpa_auth, sm->addr, + identity, identity_len) < 0 || + wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr, + radius_cui, radius_cui_len) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout); + buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + FT_R1KH_ID_LEN + 200; *resp_ies = os_zalloc(buflen); - if (*resp_ies == NULL) { - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (*resp_ies == NULL) + goto fail; pos = *resp_ies; end = *resp_ies + buflen; ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; ret = wpa_write_mdie(conf, pos, end - pos); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; - ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, + ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len, sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; *resp_ies_len = pos - *resp_ies; return WLAN_STATUS_SUCCESS; +fail: + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -975,6 +3080,7 @@ sm->ft_pending_cb = cb; sm->ft_pending_cb_ctx = ctx; sm->ft_pending_auth_transaction = auth_transaction; + sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, &resp_ies_len); if (res < 0) { @@ -998,17 +3104,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; } @@ -1039,34 +3151,56 @@ return WLAN_STATUS_INVALID_MDIE; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return WLAN_STATUS_INVALID_FTIE; + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; } - if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", - ftie->snonce, WPA_NONCE_LEN); + snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->SNonce, WPA_NONCE_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } - if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", - ftie->anonce, WPA_NONCE_LEN); + anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->ANonce, WPA_NONCE_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r0kh_id_len != sm->r0kh_id_len || @@ -1078,12 +3212,12 @@ parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, @@ -1094,7 +3228,7 @@ parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.rsn_pmkid == NULL || @@ -1102,21 +3236,27 @@ { wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); - return -1; + return WLAN_STATUS_INVALID_PMKID; } count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); - if (ftie->mic_control[1] != count) { + if (fte_elem_count != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", - ftie->mic_control[1], count); - return -1; + fte_elem_count, count); + return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, - sm->wpa_auth->addr, 5, + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kck = sm->PTK.kck2; + kck_len = sm->PTK.kck2_len; + } else { + kck = sm->PTK.kck; + kck_len = sm->PTK.kck_len; + } + if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, @@ -1126,12 +3266,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); @@ -1142,6 +3282,32 @@ return WLAN_STATUS_INVALID_FTIE; } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in (Re)Assoc Request"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } +#endif /* CONFIG_OCV */ + return WLAN_STATUS_SUCCESS; } @@ -1199,6 +3365,11 @@ wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); + if (!sm->wpa_auth->conf.ft_over_ds) { + wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject"); + return -1; + } + /* RRB - Forward action frame to the target AP */ frame = os_malloc(sizeof(*frame) + len); if (frame == NULL) @@ -1251,6 +3422,7 @@ sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb; sm->ft_pending_cb_ctx = sm; os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN); + sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; res = wpa_ft_process_auth_req(sm, body, len, &resp_ies, &resp_ies_len); if (res < 0) { @@ -1316,112 +3488,431 @@ } +static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len, + const struct tlv_list *tlvs, + const struct wpa_ft_pmk_r0_sa *pmk_r0, + const u8 *r1kh_id, const u8 *s1kh_id, + const struct tlv_list *tlv_auth, + const u8 *src_addr, u8 type, + u8 **packet, size_t *packet_len) +{ + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len = pmk_r0->pmk_r0_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 f_pairwise[sizeof(le16)]; + u8 f_expires_in[sizeof(le16)]; + u8 f_session_timeout[sizeof(le32)]; + int expires_in; + int session_timeout; + struct os_reltime now; + int ret; + struct tlv_list sess_tlv[] = { + { .type = FT_RRB_PMK_R1, .len = pmk_r1_len, + .data = pmk_r1 }, + { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name), + .data = pmk_r1_name }, + { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise), + .data = f_pairwise }, + { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in), + .data = f_expires_in }, + { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len, + .data = pmk_r0->identity }, + { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len, + .data = pmk_r0->radius_cui }, + { .type = FT_RRB_SESSION_TIMEOUT, + .len = sizeof(f_session_timeout), + .data = f_session_timeout }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len, + pmk_r0->pmk_r0_name, r1kh_id, + s1kh_id, pmk_r1, pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)", + pmk_r1, pmk_r1_len); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)", + pmk_r1_name, WPA_PMK_NAME_LEN); + WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise); + + os_get_reltime(&now); + if (pmk_r0->expiration > now.sec) + expires_in = pmk_r0->expiration - now.sec; + else if (pmk_r0->expiration) + expires_in = 1; + else + expires_in = 0; + WPA_PUT_LE16(f_expires_in, expires_in); + + if (pmk_r0->session_timeout > now.sec) + session_timeout = pmk_r0->session_timeout - now.sec; + else if (pmk_r0->session_timeout) + session_timeout = 1; + else + session_timeout = 0; + WPA_PUT_LE32(f_session_timeout, session_timeout); + + ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth, + pmk_r0->vlan, src_addr, type, + packet, packet_len); + + os_memset(pmk_r1, 0, sizeof(pmk_r1)); + + return ret; +} + + static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) { - struct ft_r0kh_r1kh_pull_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r1kh *r1kh; - struct ft_r0kh_r1kh_resp_frame resp, r; - u8 pmk_r0[PMK_LEN]; - int pairwise; + const char *msgtype = "pull request"; + u8 *plain = NULL, *packet = NULL; + size_t plain_len = 0, packet_len = 0; + struct ft_remote_r1kh *r1kh, *r1kh_wildcard; + const u8 *key; + size_t key_len; + int seq_ret; + const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name; + size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len; + size_t f_pmk_r0_name_len; + const struct wpa_ft_pmk_r0_sa *r0; + int ret; + struct tlv_list resp[2]; + struct tlv_list resp_auth[5]; + struct ft_rrb_seq f_seq; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); - if (data_len < sizeof(f)) - return -1; + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len); - r1kh = wpa_auth->conf.r1kh_list; - while (r1kh) { - if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) - break; - r1kh = r1kh->next; + if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch"); + goto out; } - if (r1kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " - "PMK-R1 pull source address " MACSTR, - MAC2STR(src_addr)); - return -1; + + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); + + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard); + if (r1kh) { + key = r1kh->key; + key_len = sizeof(r1kh->key); + } else if (r1kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID"); + key = r1kh_wildcard->key; + key_len = sizeof(r1kh_wildcard->key); + } else { + goto out; } - crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " - "request from " MACSTR, MAC2STR(src_addr)); - return -1; + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); + + seq_ret = FT_RRB_SEQ_DROP; + if (r1kh) + seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len, + auth, auth_len, msgtype, no_defer); + if (!no_defer && r1kh_wildcard && + (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) { + /* wildcard: r1kh-id unknown or changed addr -> do a seq req */ + seq_ret = FT_RRB_SEQ_DEFER; } - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", - f.pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + if (seq_ret == FT_RRB_SEQ_DROP) + goto out; - os_memset(&resp, 0, sizeof(resp)); - resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; - resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); - os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); + if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, + src_addr, FT_PACKET_R0KH_R1KH_PULL, + &plain, &plain_len) < 0) + goto out; - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); - os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); - os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); - if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, - &pairwise) < 0) { - wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " - "PMK-R1 pull"); - return -1; + if (!r1kh) + r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr, + f_r1kh_id, + wpa_auth->conf.rkh_pos_timeout); + if (!r1kh) + goto out; + + if (seq_ret == FT_RRB_SEQ_DEFER) { + wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id, + f_r0kh_id_len, f_r1kh_id, key, key_len, + enc, enc_len, auth, auth_len, + &wpa_ft_rrb_rx_pull); + goto out; } - wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, - r.pmk_r1, r.pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, - WPA_PMK_NAME_LEN); - r.pairwise = host_to_le16(pairwise); - os_memset(r.pad, 0, sizeof(r.pad)); + wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len, + msgtype); + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, + wpa_auth->conf.rkh_pos_timeout); - if (aes_wrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - r.nonce, resp.nonce) < 0) { - os_memset(pmk_r0, 0, PMK_LEN); - return -1; + RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name, + f_pmk_r0_name_len); + + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); + + if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + goto out; } - os_memset(pmk_r0, 0, PMK_LEN); + resp[0].type = FT_RRB_S1KH_ID; + resp[0].len = f_s1kh_id_len; + resp[0].data = f_s1kh_id; + resp[1].type = FT_RRB_LAST_EMPTY; + resp[1].len = 0; + resp[1].data = NULL; - wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); + resp_auth[0].type = FT_RRB_NONCE; + resp_auth[0].len = f_nonce_len; + resp_auth[0].data = f_nonce; + resp_auth[1].type = FT_RRB_SEQ; + resp_auth[1].len = sizeof(f_seq); + resp_auth[1].data = (u8 *) &f_seq; + resp_auth[2].type = FT_RRB_R0KH_ID; + resp_auth[2].len = f_r0kh_id_len; + resp_auth[2].data = f_r0kh_id; + resp_auth[3].type = FT_RRB_R1KH_ID; + resp_auth[3].len = f_r1kh_id_len; + resp_auth[3].data = f_r1kh_id; + resp_auth[4].type = FT_RRB_LAST_EMPTY; + resp_auth[4].len = 0; + resp_auth[4].data = NULL; + if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) { + wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found"); + ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth, + NULL, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_RESP, + &packet, &packet_len); + } else { + ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, + f_s1kh_id, resp_auth, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_RESP, + &packet, &packet_len); + } + + if (!ret) + wpa_ft_rrb_oui_send(wpa_auth, src_addr, + FT_PACKET_R0KH_R1KH_RESP, packet, + packet_len); + +out: + os_free(plain); + os_free(packet); + return 0; } -static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) +/* @returns 0 on success + * -1 on error + * -2 if FR_RRB_PAIRWISE is missing + */ +static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, u8 type, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + const char *msgtype, u8 *s1kh_id_out, + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer)) { - struct wpa_state_machine *sm = eloop_ctx; + u8 *plain = NULL; + size_t plain_len = 0; + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + const u8 *key; + size_t key_len; + int seq_ret; + const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id; + const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1; + const u8 *f_expires_in; + size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len; + const u8 *f_identity, *f_radius_cui; + const u8 *f_session_timeout; + size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len; + size_t f_expires_in_len; + size_t f_identity_len, f_radius_cui_len; + size_t f_session_timeout_len; + int pairwise; + int ret = -1; + int expires_in; + int session_timeout; + struct vlan_description vlan; + size_t pmk_r1_len; + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len); + + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); + + if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) { + wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch"); + goto out; + } + + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh, + &r0kh_wildcard); + if (r0kh) { + key = r0kh->key; + key_len = sizeof(r0kh->key); + } else if (r0kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); + key = r0kh_wildcard->key; + key_len = sizeof(r0kh_wildcard->key); + } else { + goto out; + } + + seq_ret = FT_RRB_SEQ_DROP; + if (r0kh) { + seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, + auth, auth_len, msgtype, + cb ? 0 : 1); + } + if (cb && r0kh_wildcard && + (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) { + /* wildcard: r0kh-id unknown or changed addr -> do a seq req */ + seq_ret = FT_RRB_SEQ_DEFER; + } + + if (seq_ret == FT_RRB_SEQ_DROP) + goto out; + + if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, + src_addr, type, &plain, &plain_len) < 0) + goto out; + + if (!r0kh) + r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr, + f_r0kh_id, f_r0kh_id_len, + wpa_auth->conf.rkh_pos_timeout); + if (!r0kh) + goto out; + + if (seq_ret == FT_RRB_SEQ_DEFER) { + wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id, + f_r0kh_id_len, f_r1kh_id, key, key_len, + enc, enc_len, auth, auth_len, cb); + goto out; + } + + wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len, + msgtype); + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, + wpa_auth->conf.rkh_pos_timeout); + + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); + + if (s1kh_id_out) + os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN); + + ret = -2; + RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16)); + wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len); + + ret = -1; + RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", + f_pmk_r1_name, WPA_PMK_NAME_LEN); + + pmk_r1_len = PMK_LEN; + if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len, + &f_pmk_r1) == 0 && + (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN)) + pmk_r1_len = f_pmk_r1_len; + RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len); + + pairwise = WPA_GET_LE16(f_pairwise); + + RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype, + sizeof(le16)); + if (f_expires_in) + expires_in = WPA_GET_LE16(f_expires_in); + else + expires_in = 0; + + wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype, + expires_in); + + if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan"); + wpa_ft_rrb_dump(plain, plain_len); + goto out; + } + + wpa_printf(MSG_DEBUG, "FT: vlan %d%s", + le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : ""); + + RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1); + if (f_identity) + wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity, + f_identity_len); + + RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1); + if (f_radius_cui) + wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui, + f_radius_cui_len); + + RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype, + sizeof(le32)); + if (f_session_timeout) + session_timeout = WPA_GET_LE32(f_session_timeout); + else + session_timeout = 0; + wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout); + + if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len, + f_pmk_r1_name, + pairwise, &vlan, expires_in, session_timeout, + f_identity, f_identity_len, f_radius_cui, + f_radius_cui_len) < 0) + goto out; + + ret = 0; +out: + if (plain) { + os_memset(plain, 0, plain_len); + os_free(plain); + } + + return ret; + +} + + +static void ft_finish_pull(struct wpa_state_machine *sm) +{ int res; u8 *resp_ies; size_t resp_ies_len; u16 status; + if (!sm->ft_pending_cb || !sm->ft_pending_req_ies) + return; + res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies), wpabuf_len(sm->ft_pending_req_ies), &resp_ies, &resp_ies_len); + if (res < 0) { + /* this loop is broken by ft_pending_pull_left_retries */ + wpa_printf(MSG_DEBUG, + "FT: Callback postponed until response is available"); + return; + } wpabuf_free(sm->ft_pending_req_ies); sm->ft_pending_req_ies = NULL; - if (res < 0) - res = WLAN_STATUS_UNSPECIFIED_FAILURE; status = res; wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR " - status %u", MAC2STR(sm->addr), status); @@ -1433,21 +3924,26 @@ } -static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) +struct ft_get_sta_ctx { + const u8 *nonce; + const u8 *s1kh_id; + struct wpa_state_machine *sm; +}; + + +static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx) { - struct ft_r0kh_r1kh_resp_frame *frame = ctx; + struct ft_get_sta_ctx *info = ctx; - if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0) + if ((info->s1kh_id && + os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) || + os_memcmp(info->nonce, sm->ft_pending_pull_nonce, + FT_RRB_NONCE_LEN) != 0 || + sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) return 0; - if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN) != 0) - return 0; - if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) - return 0; - wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for " - MACSTR " - process from timeout", MAC2STR(sm->addr)); - eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL); + info->sm = sm; + return 1; } @@ -1454,153 +3950,360 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) { - struct ft_r0kh_r1kh_resp_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r0kh *r0kh; - int pairwise, res; + const char *msgtype = "pull response"; + int nak, ret = -1; + struct ft_get_sta_ctx ctx; + u8 s1kh_id[ETH_ALEN]; + const u8 *f_nonce; + size_t f_nonce_len; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); - if (data_len < sizeof(f)) - return -1; + RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); - r0kh = wpa_auth->conf.r0kh_list; - while (r0kh) { - if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) - break; - r0kh = r0kh->next; - } - if (r0kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " - "PMK-R0 pull response source address " MACSTR, - MAC2STR(src_addr)); + os_memset(&ctx, 0, sizeof(ctx)); + ctx.nonce = f_nonce; + if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { + /* nonce not found */ + wpa_printf(MSG_DEBUG, "FT: Invalid nonce"); return -1; } - crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " - "response from " MACSTR, MAC2STR(src_addr)); + ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP, + enc, enc_len, auth, auth_len, msgtype, s1kh_id, + no_defer ? NULL : &wpa_ft_rrb_rx_resp); + if (ret == -2) { + ret = 0; + nak = 1; + } else { + nak = 0; + } + if (ret < 0) return -1; - } - if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " - "matching R1KH-ID"); - return -1; + ctx.s1kh_id = s1kh_id; + if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { + wpa_printf(MSG_DEBUG, + "FT: Response to a pending pull request for " MACSTR, + MAC2STR(ctx.sm->addr)); + eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL); + if (nak) + ctx.sm->ft_pending_pull_left_retries = 0; + ft_finish_pull(ctx.sm); } - pairwise = le_to_host16(f.pairwise); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR " pairwise=0x%x", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", - f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", - f.pmk_r1_name, WPA_PMK_NAME_LEN); - - res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, - pairwise); - wpa_printf(MSG_DEBUG, "FT: Look for pending pull request"); - wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f); - os_memset(f.pmk_r1, 0, PMK_LEN); - - return res ? 0 : -1; +out: + return ret; } static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, int no_defer) { - struct ft_r0kh_r1kh_push_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r0kh *r0kh; - struct os_time now; - os_time_t tsend; - int pairwise; + const char *msgtype = "push"; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); - if (data_len < sizeof(f)) + if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH, + enc, enc_len, auth, auth_len, msgtype, NULL, + no_defer ? NULL : wpa_ft_rrb_rx_push) < 0) return -1; - r0kh = wpa_auth->conf.r0kh_list; - while (r0kh) { - if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) - break; - r0kh = r0kh->next; + return 0; +} + + +static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, int type, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + struct ft_remote_seq **rkh_seq, + u8 **key, size_t *key_len, + struct ft_remote_r0kh **r0kh_out, + struct ft_remote_r1kh **r1kh_out, + struct ft_remote_r0kh **r0kh_wildcard_out, + struct ft_remote_r1kh **r1kh_wildcard_out) +{ + struct ft_remote_r0kh *r0kh = NULL; + struct ft_remote_r1kh *r1kh = NULL; + const u8 *f_r0kh_id, *f_r1kh_id; + size_t f_r0kh_id_len, f_r1kh_id_len; + int to_r0kh, to_r1kh; + u8 *plain = NULL; + size_t plain_len = 0; + struct ft_remote_r0kh *r0kh_wildcard; + struct ft_remote_r1kh *r1kh_wildcard; + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); + + to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len); + to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id); + + if (to_r0kh && to_r1kh) { + wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID"); + goto out; } - if (r0kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " - "PMK-R0 push source address " MACSTR, - MAC2STR(src_addr)); - return -1; + + if (!to_r0kh && !to_r1kh) { + wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID"); + goto out; } - crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " - MACSTR, MAC2STR(src_addr)); - return -1; + if (!to_r0kh) { + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, + &r0kh, &r0kh_wildcard); + if (!r0kh_wildcard && + (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", + f_r0kh_id, f_r0kh_id_len); + goto out; + } + if (r0kh) { + *key = r0kh->key; + *key_len = sizeof(r0kh->key); + } else { + *key = r0kh_wildcard->key; + *key_len = sizeof(r0kh_wildcard->key); + } } - os_get_time(&now); - tsend = WPA_GET_LE32(f.timestamp); - if ((now.sec > tsend && now.sec - tsend > 60) || - (now.sec < tsend && tsend - now.sec > 60)) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " - "timestamp: sender time %d own time %d\n", - (int) tsend, (int) now.sec); - return -1; + if (!to_r1kh) { + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, + &r1kh_wildcard); + if (!r1kh_wildcard && + (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID", + f_r1kh_id, FT_R1KH_ID_LEN); + goto out; + } + if (r1kh) { + *key = r1kh->key; + *key_len = sizeof(r1kh->key); + } else { + *key = r1kh_wildcard->key; + *key_len = sizeof(r1kh_wildcard->key); + } } - if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " - "R1KH-ID (received " MACSTR " own " MACSTR ")", - MAC2STR(f.r1kh_id), - MAC2STR(wpa_auth->conf.r1_key_holder)); - return -1; + if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len, + src_addr, type, &plain, &plain_len) < 0) + goto out; + + os_free(plain); + + if (!to_r0kh) { + if (!r0kh) + r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, + src_addr, f_r0kh_id, + f_r0kh_id_len, + ftRRBseqTimeout); + if (!r0kh) + goto out; + + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout); + *rkh_seq = r0kh->seq; + if (r0kh_out) + *r0kh_out = r0kh; + if (r0kh_wildcard_out) + *r0kh_wildcard_out = r0kh_wildcard; } - pairwise = le_to_host16(f.pairwise); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR " pairwise=0x%x", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", - f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", - f.pmk_r1_name, WPA_PMK_NAME_LEN); + if (!to_r1kh) { + if (!r1kh) + r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, + src_addr, f_r1kh_id, + ftRRBseqTimeout); + if (!r1kh) + goto out; - wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, - pairwise); - os_memset(f.pmk_r1, 0, PMK_LEN); + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout); + *rkh_seq = r1kh->seq; + if (r1kh_out) + *r1kh_out = r1kh; + if (r1kh_wildcard_out) + *r1kh_wildcard_out = r1kh_wildcard; + } return 0; +out: + return -1; } +static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) +{ + int ret = -1; + struct ft_rrb_seq f_seq; + const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id; + size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len; + struct ft_remote_seq *rkh_seq = NULL; + u8 *packet = NULL, *key = NULL; + size_t packet_len = 0, key_len = 0; + struct tlv_list seq_resp_auth[5]; + + wpa_printf(MSG_DEBUG, "FT: Received sequence number request"); + + if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + enc, enc_len, auth, auth_len, &rkh_seq, &key, + &key_len, NULL, NULL, NULL, NULL) < 0) + goto out; + + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len); + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); + + if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + goto out; + } + + seq_resp_auth[0].type = FT_RRB_NONCE; + seq_resp_auth[0].len = f_nonce_len; + seq_resp_auth[0].data = f_nonce; + seq_resp_auth[1].type = FT_RRB_SEQ; + seq_resp_auth[1].len = sizeof(f_seq); + seq_resp_auth[1].data = (u8 *) &f_seq; + seq_resp_auth[2].type = FT_RRB_R0KH_ID; + seq_resp_auth[2].len = f_r0kh_id_len; + seq_resp_auth[2].data = f_r0kh_id; + seq_resp_auth[3].type = FT_RRB_R1KH_ID; + seq_resp_auth[3].len = FT_R1KH_ID_LEN; + seq_resp_auth[3].data = f_r1kh_id; + seq_resp_auth[4].type = FT_RRB_LAST_EMPTY; + seq_resp_auth[4].len = 0; + seq_resp_auth[4].data = NULL; + + if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL, + wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, + &packet, &packet_len) < 0) + goto out; + + wpa_ft_rrb_oui_send(wpa_auth, src_addr, + FT_PACKET_R0KH_R1KH_SEQ_RESP, packet, + packet_len); + +out: + os_free(packet); + + return ret; +} + + +static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) +{ + u8 *key = NULL; + size_t key_len = 0; + struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL; + struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL; + const u8 *f_nonce, *f_seq; + size_t f_nonce_len, f_seq_len; + struct ft_remote_seq *rkh_seq = NULL; + struct ft_remote_item *item; + struct os_reltime now, now_remote; + int seq_ret, found; + const struct ft_rrb_seq *msg_both; + u32 msg_dom, msg_seq; + + wpa_printf(MSG_DEBUG, "FT: Received sequence number response"); + + if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, + enc, enc_len, auth, auth_len, &rkh_seq, &key, + &key_len, &r0kh, &r1kh, &r0kh_wildcard, + &r1kh_wildcard) < 0) + goto out; + + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce, + f_nonce_len); + + found = 0; + dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item, + list) { + if (os_memcmp_const(f_nonce, item->nonce, + FT_RRB_NONCE_LEN) != 0 || + os_get_reltime(&now) < 0 || + os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout)) + continue; + + found = 1; + break; + } + if (!found) { + wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce"); + goto out; + } + + if (r0kh) { + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, + wpa_auth->conf.rkh_pos_timeout); + if (r0kh_wildcard) + os_memcpy(r0kh->addr, src_addr, ETH_ALEN); + } + + if (r1kh) { + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, + wpa_auth->conf.rkh_pos_timeout); + if (r1kh_wildcard) + os_memcpy(r1kh->addr, src_addr, ETH_ALEN); + } + + seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth, + auth_len, "seq response", 1); + if (seq_ret == FT_RRB_SEQ_OK) { + wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number"); + wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth, + auth_len, "seq response"); + } else { + wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number"); + + RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response", + sizeof(*msg_both)); + msg_both = (const struct ft_rrb_seq *) f_seq; + + msg_dom = le_to_host32(msg_both->dom); + msg_seq = le_to_host32(msg_both->seq); + now_remote.sec = le_to_host32(msg_both->ts); + now_remote.usec = 0; + + rkh_seq->rx.num_last = 2; + rkh_seq->rx.dom = msg_dom; + rkh_seq->rx.offsetidx = 0; + /* Accept some older, possibly cached packets as well */ + rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG - + dl_list_len(&rkh_seq->rx.queue); + rkh_seq->rx.last[1] = msg_seq; + + /* local time - offset = remote time + * <=> local time - remote time = offset */ + os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset); + } + + wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1); + + return 0; +out: + return -1; +} + + int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { @@ -1642,13 +4345,6 @@ return -1; } - if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) - return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); - if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) - return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); - if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) - return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); - wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); if (alen < 1 + 1 + 2 * ETH_ALEN) { @@ -1726,65 +4422,140 @@ } -static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, - struct wpa_ft_pmk_r0_sa *pmk_r0, - struct ft_remote_r1kh *r1kh, - const u8 *s1kh_id, int pairwise) +void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, const u8 *data, + size_t data_len) { - struct ft_r0kh_r1kh_push_frame frame, f; - struct os_time now; - const u8 *plain; - u8 *crypt; + const u8 *auth, *enc; + size_t alen, elen; + int no_defer = 0; - os_memset(&frame, 0, sizeof(frame)); - frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; - frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); - os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " + MACSTR, MAC2STR(src_addr)); + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix); + wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len); - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); - os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, - s1kh_id, f.pmk_r1, f.pmk_r1_name); - wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, - WPA_PMK_NAME_LEN); - os_get_time(&now); - WPA_PUT_LE32(f.timestamp, now.sec); - f.pairwise = host_to_le16(pairwise); - os_memset(f.pad, 0, sizeof(f.pad)); - plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - if (aes_wrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - plain, crypt) < 0) + if (is_multicast_ether_addr(src_addr)) { + wpa_printf(MSG_DEBUG, + "FT: RRB-OUI received frame from multicast address " + MACSTR, MAC2STR(src_addr)); return; + } - wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); + if (is_multicast_ether_addr(dst_addr)) { + wpa_printf(MSG_DEBUG, + "FT: RRB-OUI received frame from remote AP " MACSTR + " to multicast address " MACSTR, + MAC2STR(src_addr), MAC2STR(dst_addr)); + no_defer = 1; + } + + if (data_len < sizeof(u16)) { + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short"); + return; + } + + alen = WPA_GET_LE16(data); + if (data_len < sizeof(u16) + alen) { + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short"); + return; + } + + auth = data + sizeof(u16); + wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen); + enc = data + sizeof(u16) + alen; + elen = data_len - sizeof(u16) - alen; + wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen); + + switch (oui_suffix) { + case FT_PACKET_R0KH_R1KH_PULL: + wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_RESP: + wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_PUSH: + wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_SEQ_REQ: + wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_SEQ_RESP: + wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth, + alen, no_defer); + break; + } } +static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_ft_pmk_r0_sa *pmk_r0, + struct ft_remote_r1kh *r1kh, + const u8 *s1kh_id) +{ + u8 *packet; + size_t packet_len; + struct ft_rrb_seq f_seq; + struct tlv_list push[] = { + { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, + .data = s1kh_id }, + { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, + .data = pmk_r0->pmk_r0_name }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + struct tlv_list push_auth[] = { + { .type = FT_RRB_SEQ, .len = sizeof(f_seq), + .data = (u8 *) &f_seq }, + { .type = FT_RRB_R0KH_ID, + .len = wpa_auth->conf.r0_key_holder_len, + .data = wpa_auth->conf.r0_key_holder }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = r1kh->id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + return -1; + } + + if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0, + r1kh->id, s1kh_id, push_auth, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_PUSH, + &packet, &packet_len) < 0) + return -1; + + wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH, + packet, packet_len); + + os_free(packet); + return 0; +} + + void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) { - struct wpa_ft_pmk_r0_sa *r0; + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL; struct ft_remote_r1kh *r1kh; if (!wpa_auth->conf.pmk_r1_push) return; + if (!wpa_auth->conf.r1kh_list) + return; - r0 = wpa_auth->ft_pmk_cache->pmk_r0; - while (r0) { - if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) + dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) { + if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) { + r0found = r0; break; - r0 = r0->next; + } } + r0 = r0found; if (r0 == NULL || r0->pmk_r1_pushed) return; r0->pmk_r1_pushed = 1; @@ -1792,11 +4563,14 @@ wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " "for STA " MACSTR, MAC2STR(addr)); - r1kh = wpa_auth->conf.r1kh_list; - while (r1kh) { - wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); - r1kh = r1kh->next; + for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + if (is_zero_ether_addr(r1kh->addr) || + is_zero_ether_addr(r1kh->id)) + continue; + if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) + continue; + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ --- contrib/wpa/src/ap/wpa_auth_glue.c.orig +++ contrib/wpa/src/ap/wpa_auth_glue.c @@ -9,13 +9,17 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" +#include "crypto/sha1.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" #include "l2_packet/l2_packet.h" +#include "eth_p_oui.h" #include "hostapd.h" #include "ieee802_1x.h" #include "preauth_auth.h" @@ -23,6 +27,8 @@ #include "tkip_countermeasures.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "ieee802_11.h" +#include "pmksa_cache_auth.h" #include "wpa_auth.h" #include "wpa_auth_glue.h" @@ -40,19 +46,26 @@ wconf->wpa_strict_rekey = conf->wpa_strict_rekey; wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; + wconf->wpa_group_update_count = conf->wpa_group_update_count; + wconf->wpa_disable_eapol_key_retries = + conf->wpa_disable_eapol_key_retries; + wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count; wconf->rsn_pairwise = conf->rsn_pairwise; wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; - wconf->peerkey = conf->peerkey; wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; wconf->disable_pmksa_caching = conf->disable_pmksa_caching; +#ifdef CONFIG_OCV + wconf->ocv = conf->ocv; +#endif /* CONFIG_OCV */ wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; wconf->group_mgmt_cipher = conf->group_mgmt_cipher; + wconf->sae_require_mfp = conf->sae_require_mfp; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wconf->ssid_len = conf->ssid.ssid_len; if (wconf->ssid_len > SSID_MAX_LEN) wconf->ssid_len = SSID_MAX_LEN; @@ -67,12 +80,18 @@ } os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); wconf->r0_key_lifetime = conf->r0_key_lifetime; + wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime; wconf->reassociation_deadline = conf->reassociation_deadline; - wconf->r0kh_list = conf->r0kh_list; - wconf->r1kh_list = conf->r1kh_list; + wconf->rkh_pos_timeout = conf->rkh_pos_timeout; + wconf->rkh_neg_timeout = conf->rkh_neg_timeout; + wconf->rkh_pull_timeout = conf->rkh_pull_timeout; + wconf->rkh_pull_retries = conf->rkh_pull_retries; + wconf->r0kh_list = &conf->r0kh_list; + wconf->r1kh_list = &conf->r1kh_list; wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; -#endif /* CONFIG_IEEE80211R */ + wconf->ft_psk_generate_local = conf->ft_psk_generate_local; +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_HS20 wconf->disable_gtk = conf->disable_dgaf; if (conf->osen) { @@ -106,6 +125,11 @@ os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4); os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FILS + wconf->fils_cache_id_set = conf->fils_cache_id_set; + os_memcpy(wconf->fils_cache_id, conf->fils_cache_id, + FILS_CACHE_ID_LEN); +#endif /* CONFIG_FILS */ } @@ -222,12 +246,18 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk) + const u8 *prev_psk, size_t *psk_len, + int *vlan_id) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); const u8 *psk; + if (vlan_id) + *vlan_id = 0; + if (psk_len) + *psk_len = PMK_LEN; + #ifdef CONFIG_SAE if (sta && sta->auth_alg == WLAN_AUTH_SAE) { if (!sta->sae || prev_psk) @@ -234,9 +264,34 @@ return NULL; return sta->sae->pmk; } + if (sta && wpa_auth_uses_sae(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, + "No PSK for STA trying to use SAE with PMKSA caching"); + return NULL; + } #endif /* CONFIG_SAE */ - psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + sta && sta->owe_pmk) { + if (psk_len) + *psk_len = sta->owe_pmk_len; + return sta->owe_pmk; + } + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) { + struct rsn_pmksa_cache_entry *sa; + + sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (sa && sa->akmp == WPA_KEY_MGMT_OWE) { + if (psk_len) + *psk_len = sa->pmk_len; + return sa->pmk; + } + } +#endif /* CONFIG_OWE */ + + psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk, + vlan_id); /* * This is about to iterate over all psks, prev_psk gives the last * returned psk which should not be returned again. @@ -244,8 +299,18 @@ */ if (sta && sta->psk && !psk) { struct hostapd_sta_wpa_psk_short *pos; + + if (vlan_id) + *vlan_id = 0; psk = sta->psk->psk; for (pos = sta->psk; pos; pos = pos->next) { + if (pos->is_passphrase) { + pbkdf2_sha1(pos->passphrase, + hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, 4096, + pos->psk, PMK_LEN); + pos->is_passphrase = 0; + } if (pos->psk == prev_psk) { psk = pos->next ? pos->next->psk : NULL; break; @@ -299,6 +364,37 @@ return -1; } +#ifdef CONFIG_TESTING_OPTIONS + if (addr && !is_broadcast_ether_addr(addr)) { + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) { + sta->last_tk_alg = alg; + sta->last_tk_key_idx = idx; + if (key) + os_memcpy(sta->last_tk, key, key_len); + sta->last_tk_len = key_len; + } +#ifdef CONFIG_IEEE80211W + } else if (alg == WPA_ALG_IGTK || + alg == WPA_ALG_BIP_GMAC_128 || + alg == WPA_ALG_BIP_GMAC_256 || + alg == WPA_ALG_BIP_CMAC_256) { + hapd->last_igtk_alg = alg; + hapd->last_igtk_key_idx = idx; + if (key) + os_memcpy(hapd->last_igtk, key, key_len); + hapd->last_igtk_len = key_len; +#endif /* CONFIG_IEEE80211W */ + } else { + hapd->last_gtk_alg = alg; + hapd->last_gtk_key_idx = idx; + if (key) + os_memcpy(hapd->last_gtk, key, key_len); + hapd->last_gtk_len = key_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, key, key_len); } @@ -393,8 +489,33 @@ } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP +struct wpa_ft_rrb_rx_later_data { + struct dl_list list; + u8 addr[ETH_ALEN]; + size_t data_len; + /* followed by data_len octets of data */ +}; + +static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct wpa_ft_rrb_rx_later_data *data, *n; + + dl_list_for_each_safe(data, n, &hapd->l2_queue, + struct wpa_ft_rrb_rx_later_data, list) { + if (hapd->wpa_auth) { + wpa_ft_rrb_rx(hapd->wpa_auth, data->addr, + (const u8 *) (data + 1), + data->data_len); + } + dl_list_del(&data->list); + os_free(data); + } +} + + struct wpa_auth_ft_iface_iter_data { struct hostapd_data *src_hapd; const u8 *dst; @@ -406,31 +527,54 @@ static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) { struct wpa_auth_ft_iface_iter_data *idata = ctx; + struct wpa_ft_rrb_rx_later_data *data; struct hostapd_data *hapd; size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; - if (hapd == idata->src_hapd) + if (hapd == idata->src_hapd || + !hapd->wpa_auth || + os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0) continue; - if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { - wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " - "locally managed BSS " MACSTR "@%s -> " - MACSTR "@%s", - MAC2STR(idata->src_hapd->own_addr), - idata->src_hapd->conf->iface, - MAC2STR(hapd->own_addr), hapd->conf->iface); - wpa_ft_rrb_rx(hapd->wpa_auth, - idata->src_hapd->own_addr, - idata->data, idata->data_len); + + wpa_printf(MSG_DEBUG, + "FT: Send RRB data directly to locally managed BSS " + MACSTR "@%s -> " MACSTR "@%s", + MAC2STR(idata->src_hapd->own_addr), + idata->src_hapd->conf->iface, + MAC2STR(hapd->own_addr), hapd->conf->iface); + + /* Defer wpa_ft_rrb_rx() until next eloop step as this is + * when it would be triggered when reading from a socket. + * This avoids + * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, + * that is calling hapd0:recv handler from within + * hapd0:send directly. + */ + data = os_zalloc(sizeof(*data) + idata->data_len); + if (!data) return 1; - } + + os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN); + os_memcpy(data + 1, idata->data, idata->data_len); + data->data_len = idata->data_len; + + dl_list_add(&hapd->l2_queue, &data->list); + + if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later, + hapd, NULL)) + eloop_register_timeout(0, 0, + hostapd_wpa_ft_rrb_rx_later, + hapd, NULL); + + return 1; } return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, @@ -455,7 +599,7 @@ } #endif /* CONFIG_TESTING_OPTIONS */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (proto == ETH_P_RRB && hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) { int res; @@ -470,7 +614,7 @@ if (res == 1) return data_len; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (hapd->driver && hapd->driver->send_ether) return hapd->driver->send_ether(hapd->drv_priv, dst, @@ -493,8 +637,226 @@ } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_ETH_P_OUI +static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd, + u8 oui_suffix) +{ + switch (oui_suffix) { +#ifdef CONFIG_IEEE80211R_AP + case FT_PACKET_R0KH_R1KH_PULL: + return hapd->oui_pull; + case FT_PACKET_R0KH_R1KH_RESP: + return hapd->oui_resp; + case FT_PACKET_R0KH_R1KH_PUSH: + return hapd->oui_push; + case FT_PACKET_R0KH_R1KH_SEQ_REQ: + return hapd->oui_sreq; + case FT_PACKET_R0KH_R1KH_SEQ_RESP: + return hapd->oui_sresp; +#endif /* CONFIG_IEEE80211R_AP */ + default: + return NULL; + } +} +#endif /* CONFIG_ETH_P_OUI */ + +#ifdef CONFIG_IEEE80211R_AP + +struct oui_deliver_later_data { + struct dl_list list; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; + size_t data_len; + u8 oui_suffix; + /* followed by data_len octets of data */ +}; + +static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct oui_deliver_later_data *data, *n; + struct eth_p_oui_ctx *oui_ctx; + + dl_list_for_each_safe(data, n, &hapd->l2_oui_queue, + struct oui_deliver_later_data, list) { + oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix); + if (hapd->wpa_auth && oui_ctx) { + eth_p_oui_deliver(oui_ctx, data->src_addr, + data->dst_addr, + (const u8 *) (data + 1), + data->data_len); + } + dl_list_del(&data->list); + os_free(data); + } +} + + +struct wpa_auth_oui_iface_iter_data { + struct hostapd_data *src_hapd; + const u8 *dst_addr; + const u8 *data; + size_t data_len; + u8 oui_suffix; +}; + +static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx) +{ + struct wpa_auth_oui_iface_iter_data *idata = ctx; + struct oui_deliver_later_data *data; + struct hostapd_data *hapd; + size_t j; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (hapd == idata->src_hapd) + continue; + if (!is_multicast_ether_addr(idata->dst_addr) && + os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0) + continue; + + /* defer eth_p_oui_deliver until next eloop step as this is + * when it would be triggerd from reading from sock + * This avoids + * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, + * that is calling hapd0:recv handler from within + * hapd0:send directly. + */ + data = os_zalloc(sizeof(*data) + idata->data_len); + if (!data) + return 1; + + os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN); + os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN); + os_memcpy(data + 1, idata->data, idata->data_len); + data->data_len = idata->data_len; + data->oui_suffix = idata->oui_suffix; + + dl_list_add(&hapd->l2_oui_queue, &data->list); + + if (!eloop_is_timeout_registered(hostapd_oui_deliver_later, + hapd, NULL)) + eloop_register_timeout(0, 0, + hostapd_oui_deliver_later, + hapd, NULL); + + return 1; + } + + return 0; +} + +#endif /* CONFIG_IEEE80211R_AP */ + + +static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix, + const u8 *data, size_t data_len) +{ +#ifdef CONFIG_ETH_P_OUI + struct hostapd_data *hapd = ctx; + struct eth_p_oui_ctx *oui_ctx; + +#ifdef CONFIG_IEEE80211R_AP + if (hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) { + struct wpa_auth_oui_iface_iter_data idata; + int res; + + idata.src_hapd = hapd; + idata.dst_addr = dst; + idata.data = data; + idata.data_len = data_len; + idata.oui_suffix = oui_suffix; + res = hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_wpa_auth_oui_iter, + &idata); + if (res == 1) + return data_len; + } +#endif /* CONFIG_IEEE80211R_AP */ + + oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix); + if (!oui_ctx) + return -1; + + return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len); +#else /* CONFIG_ETH_P_OUI */ + return -1; +#endif /* CONFIG_ETH_P_OUI */ +} + + +static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_drv_channel_info(hapd, ci); +} + + +static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id) +{ +#ifndef CONFIG_NO_VLAN + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + struct vlan_description vlan_desc; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = 1; + vlan_desc.untagged = vlan_id; + if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file", + vlan_id); + return -1; + } + + if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) { + wpa_printf(MSG_INFO, + "Failed to assign VLAN ID %d from wpa_psk_file to " + MACSTR, vlan_id, MAC2STR(sta->addr)); + return -1; + } + + wpa_printf(MSG_INFO, + "Assigned VLAN ID %d from wpa_psk_file to " MACSTR, + vlan_id, MAC2STR(sta->addr)); + if ((sta->flags & WLAN_STA_ASSOC) && + ap_sta_bind_vlan(hapd, sta) < 0) + return -1; +#endif /* CONFIG_NO_VLAN */ + + return 0; +} + + +#ifdef CONFIG_OCV +static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr, + int ap_max_chanwidth, int ap_seg1_idx, + int *bandwidth, int *seg1_idx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO, + "Failed to get STA info to validate received OCI"); + return -1; + } + + return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth, + seg1_idx); +} +#endif /* CONFIG_OCV */ + + +#ifdef CONFIG_IEEE80211R_AP + static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, const u8 *data, size_t data_len) { @@ -531,6 +893,9 @@ struct hostapd_data *hapd = ctx; struct sta_info *sta; + wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR + " based on WPA authenticator callback", + MAC2STR(sta_addr)); if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) return NULL; @@ -537,6 +902,9 @@ sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; + if (hapd->driver && hapd->driver->add_sta_node) + sta->added_unassoc = 1; + sta->ft_over_ds = 1; if (sta->wpa_sm) { sta->auth_alg = WLAN_AUTH_FT; return sta->wpa_sm; @@ -553,6 +921,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) { @@ -563,11 +1169,30 @@ ethhdr = (struct l2_ethhdr *) buf; wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); + if (!is_multicast_ether_addr(ethhdr->h_dest) && + os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0) + return; wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), len - sizeof(*ethhdr)); } +static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len) +{ + struct hostapd_data *hapd = ctx; + + wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " + MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr)); + if (!is_multicast_ether_addr(dst_addr) && + os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0) + return; + wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf, + len); +} + + static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { @@ -575,13 +1200,99 @@ return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); } -#endif /* CONFIG_IEEE80211R */ +static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd, + const char *ft_iface) +{ + hapd->oui_pull = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_PULL, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_pull) + return -1; + + hapd->oui_resp = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_RESP, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_resp) + return -1; + + hapd->oui_push = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_PUSH, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_push) + return -1; + + hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_SEQ_REQ, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_sreq) + return -1; + + hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_SEQ_RESP, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_sresp) + return -1; + + return 0; +} + + +static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd) +{ + eth_p_oui_unregister(hapd->oui_pull); + hapd->oui_pull = NULL; + eth_p_oui_unregister(hapd->oui_resp); + hapd->oui_resp = NULL; + eth_p_oui_unregister(hapd->oui_push); + hapd->oui_push = NULL; + eth_p_oui_unregister(hapd->oui_sreq); + hapd->oui_sreq = NULL; + eth_p_oui_unregister(hapd->oui_sresp); + hapd->oui_sresp = NULL; +} +#endif /* CONFIG_IEEE80211R_AP */ + + int hostapd_setup_wpa(struct hostapd_data *hapd) { struct wpa_auth_config _conf; - struct wpa_auth_callbacks cb; + static const struct wpa_auth_callbacks cb = { + .logger = hostapd_wpa_auth_logger, + .disconnect = hostapd_wpa_auth_disconnect, + .mic_failure_report = hostapd_wpa_auth_mic_failure_report, + .psk_failure_report = hostapd_wpa_auth_psk_failure_report, + .set_eapol = hostapd_wpa_auth_set_eapol, + .get_eapol = hostapd_wpa_auth_get_eapol, + .get_psk = hostapd_wpa_auth_get_psk, + .get_msk = hostapd_wpa_auth_get_msk, + .set_key = hostapd_wpa_auth_set_key, + .get_seqnum = hostapd_wpa_auth_get_seqnum, + .send_eapol = hostapd_wpa_auth_send_eapol, + .for_each_sta = hostapd_wpa_auth_for_each_sta, + .for_each_auth = hostapd_wpa_auth_for_each_auth, + .send_ether = hostapd_wpa_auth_send_ether, + .send_oui = hostapd_wpa_auth_send_oui, + .channel_info = hostapd_channel_info, + .update_vlan = hostapd_wpa_auth_update_vlan, +#ifdef CONFIG_OCV + .get_sta_tx_params = hostapd_get_sta_tx_params, +#endif /* CONFIG_OCV */ +#ifdef CONFIG_IEEE80211R_AP + .send_ft_action = hostapd_wpa_auth_send_ft_action, + .add_sta = hostapd_wpa_auth_add_sta, + .add_tspec = hostapd_wpa_auth_add_tspec, + .set_vlan = hostapd_wpa_auth_set_vlan, + .get_vlan = hostapd_wpa_auth_get_vlan, + .set_identity = hostapd_wpa_auth_set_identity, + .get_identity = hostapd_wpa_auth_get_identity, + .set_radius_cui = hostapd_wpa_auth_set_radius_cui, + .get_radius_cui = hostapd_wpa_auth_get_radius_cui, + .set_session_timeout = hostapd_wpa_auth_set_session_timeout, + .get_session_timeout = hostapd_wpa_auth_get_session_timeout, +#endif /* CONFIG_IEEE80211R_AP */ + }; const u8 *wpa_ie; size_t wpa_ie_len; @@ -590,28 +1301,7 @@ _conf.tx_status = 1; if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) _conf.ap_mlme = 1; - os_memset(&cb, 0, sizeof(cb)); - cb.ctx = hapd; - cb.logger = hostapd_wpa_auth_logger; - cb.disconnect = hostapd_wpa_auth_disconnect; - cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; - cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report; - cb.set_eapol = hostapd_wpa_auth_set_eapol; - cb.get_eapol = hostapd_wpa_auth_get_eapol; - cb.get_psk = hostapd_wpa_auth_get_psk; - cb.get_msk = hostapd_wpa_auth_get_msk; - cb.set_key = hostapd_wpa_auth_set_key; - cb.get_seqnum = hostapd_wpa_auth_get_seqnum; - cb.send_eapol = hostapd_wpa_auth_send_eapol; - cb.for_each_sta = hostapd_wpa_auth_for_each_sta; - cb.for_each_auth = hostapd_wpa_auth_for_each_auth; - cb.send_ether = hostapd_wpa_auth_send_ether; -#ifdef CONFIG_IEEE80211R - cb.send_ft_action = hostapd_wpa_auth_send_ft_action; - cb.add_sta = hostapd_wpa_auth_add_sta; - cb.add_tspec = hostapd_wpa_auth_add_tspec; -#endif /* CONFIG_IEEE80211R */ - hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); + hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); if (hapd->wpa_auth == NULL) { wpa_printf(MSG_ERROR, "WPA initialization failed."); return -1; @@ -636,12 +1326,14 @@ return -1; } -#ifdef CONFIG_IEEE80211R - if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds && +#ifdef CONFIG_IEEE80211R_AP + if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { - hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? - hapd->conf->bridge : - hapd->conf->iface, NULL, ETH_P_RRB, + const char *ft_iface; + + ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge : + hapd->conf->iface; + hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB, hostapd_rrb_receive, hapd, 1); if (hapd->l2 == NULL && (hapd->driver == NULL || @@ -650,8 +1342,14 @@ "interface"); return -1; } + + if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) { + wpa_printf(MSG_ERROR, + "Failed to open ETH_P_OUI interface"); + return -1; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ return 0; @@ -674,13 +1372,14 @@ wpa_deinit(hapd->wpa_auth); hapd->wpa_auth = NULL; - if (hostapd_set_privacy(hapd, 0)) { + if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) { wpa_printf(MSG_DEBUG, "Could not disable " "PrivacyInvoked for interface %s", hapd->conf->iface); } - if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + if (hapd->drv_priv && + hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { wpa_printf(MSG_DEBUG, "Could not remove generic " "information element from interface %s", hapd->conf->iface); @@ -688,8 +1387,13 @@ } ieee802_1x_deinit(hapd); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX); + hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */ + eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX); + hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */ l2_packet_deinit(hapd->l2); hapd->l2 = NULL; -#endif /* CONFIG_IEEE80211R */ + hostapd_wpa_unregister_ft_oui(hapd); +#endif /* CONFIG_IEEE80211R_AP */ } --- contrib/wpa/src/ap/wpa_auth_i.h.orig +++ contrib/wpa/src/ap/wpa_auth_i.h @@ -9,18 +9,13 @@ #ifndef WPA_AUTH_I_H #define WPA_AUTH_I_H +#include "utils/list.h" + /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ #define RSNA_MAX_EAPOL_RETRIES 4 struct wpa_group; -struct wpa_stsl_negotiation { - struct wpa_stsl_negotiation *next; - u8 initiator[ETH_ALEN]; - u8 peer[ETH_ALEN]; -}; - - struct wpa_state_machine { struct wpa_authenticator *wpa_auth; struct wpa_group *group; @@ -27,6 +22,7 @@ u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + u16 auth_alg; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, @@ -48,8 +44,9 @@ Boolean AuthenticationRequest; Boolean ReAuthenticationRequest; Boolean Disconnect; - int TimeoutCtr; - int GTimeoutCtr; + u16 disconnect_reason; /* specific reason code to use with Disconnect */ + u32 TimeoutCtr; + u32 GTimeoutCtr; Boolean TimeoutEvt; Boolean EAPOLKeyReceived; Boolean EAPOLKeyPairwise; @@ -60,7 +57,9 @@ u8 SNonce[WPA_NONCE_LEN]; u8 alt_SNonce[WPA_NONCE_LEN]; u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; - u8 PMK[PMK_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; @@ -88,11 +87,15 @@ unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; unsigned int alt_snonce_valid:1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ unsigned int is_wnmsleep:1; + unsigned int pmkid_set:1; +#ifdef CONFIG_OCV + unsigned int ocv_enabled:1; +#endif /* CONFIG_OCV */ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; @@ -112,9 +115,12 @@ u32 dot11RSNAStatsTKIPLocalMICFailures; u32 dot11RSNAStatsTKIPRemoteMICFailures; -#ifdef CONFIG_IEEE80211R - u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ +#ifdef CONFIG_IEEE80211R_AP + u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the + * first 384 bits of MSK */ size_t xxkey_len; + u8 pmk_r1[PMK_LEN_MAX]; + unsigned int pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth * Request */ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ @@ -128,10 +134,11 @@ const u8 *ies, size_t ies_len); void *ft_pending_cb_ctx; struct wpabuf *ft_pending_req_ies; - u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; + u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN]; u8 ft_pending_auth_transaction; u8 ft_pending_current_ap[ETH_ALEN]; -#endif /* CONFIG_IEEE80211R */ + int ft_pending_pull_left_retries; +#endif /* CONFIG_IEEE80211R_AP */ int pending_1_of_4_timeout; @@ -138,6 +145,23 @@ #ifdef CONFIG_P2P u8 ip_addr[4]; #endif /* CONFIG_P2P */ + +#ifdef CONFIG_FILS + u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; + u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; + size_t fils_key_auth_len; + unsigned int fils_completed:1; +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_DPP2 + struct wpabuf *dpp_z; +#endif /* CONFIG_DPP2 */ + +#ifdef CONFIG_TESTING_OPTIONS + void (*eapol_status_cb)(void *ctx1, void *ctx2); + void *eapol_status_cb_ctx1; + void *eapol_status_cb_ctx2; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -172,6 +196,7 @@ #endif /* CONFIG_IEEE80211W */ /* Number of references except those in struct wpa_group->next */ unsigned int references; + unsigned int num_setup_iface; }; @@ -192,10 +217,9 @@ unsigned int dot11RSNATKIPCounterMeasuresInvoked; unsigned int dot11RSNA4WayHandshakeFailures; - struct wpa_stsl_negotiation *stsl_negotiations; - struct wpa_auth_config conf; - struct wpa_auth_callbacks cb; + const struct wpa_auth_callbacks *cb; + void *cb_ctx; u8 *wpa_ie; size_t wpa_ie_len; @@ -211,6 +235,38 @@ }; +#ifdef CONFIG_IEEE80211R_AP + +#define FT_REMOTE_SEQ_BACKLOG 16 +struct ft_remote_seq_rx { + u32 dom; + struct os_reltime time_offset; /* local time - offset = remote time */ + + /* accepted sequence numbers: (offset ... offset + 0x40000000] + * (except those in last) + * dropped sequence numbers: (offset - 0x40000000 ... offset] + * all others trigger SEQ_REQ message (except first message) + */ + u32 last[FT_REMOTE_SEQ_BACKLOG]; + unsigned int num_last; + u32 offsetidx; + + struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */ +}; + +struct ft_remote_seq_tx { + u32 dom; /* non zero if initialized */ + u32 seq; +}; + +struct ft_remote_seq { + struct ft_remote_seq_rx rx; + struct ft_remote_seq_tx tx; +}; + +#endif /* CONFIG_IEEE80211R_AP */ + + int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid); void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, @@ -229,32 +285,19 @@ int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); -#ifdef CONFIG_PEERKEY -int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, - struct wpa_stsl_negotiation *neg); -void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *key_data, size_t key_data_len); -void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len); -void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len); -#endif /* CONFIG_PEERKEY */ - -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); -int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, - size_t r0kh_id_len, +int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, + const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len); -int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk); struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); void wpa_ft_install_ptk(struct wpa_state_machine *sm); -#endif /* CONFIG_IEEE80211R */ +int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0, + const u8 *pmk_r0_name); +#endif /* CONFIG_IEEE80211R_AP */ #endif /* WPA_AUTH_I_H */ --- contrib/wpa/src/ap/wpa_auth_ie.c.orig +++ contrib/wpa/src/ap/wpa_auth_ie.c @@ -1,6 +1,6 @@ /* * hostapd - WPA/RSN IE and KDE definitions - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2018, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -164,18 +164,25 @@ pos += RSN_SELECTOR_LEN; num_suites++; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } +#ifdef CONFIG_SHA384 + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_SHA384 */ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); pos += RSN_SELECTOR_LEN; num_suites++; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); @@ -210,6 +217,51 @@ pos += RSN_SELECTOR_LEN; num_suites++; } +#ifdef CONFIG_FILS + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#ifdef CONFIG_IEEE80211R_AP + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211R_AP */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_HS20 */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -230,8 +282,6 @@ capab = 0; if (conf->rsn_preauth) capab |= WPA_CAPABILITY_PREAUTH; - if (conf->peerkey) - capab |= WPA_CAPABILITY_PEERKEY_ENABLED; if (conf->wmm_enabled) { /* 4 PTKSA replay counters when using WMM */ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); @@ -243,15 +293,19 @@ capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) - capab |= BIT(8) | BIT(14) | BIT(15); + capab |= BIT(8) | BIT(15); #endif /* CONFIG_RSN_TESTING */ WPA_PUT_LE16(pos, capab); pos += 2; if (pmkid) { - if (pos + 2 + PMKID_LEN > buf + len) + if (2 + PMKID_LEN > buf + len - pos) return -1; /* PMKID Count */ WPA_PUT_LE16(pos, 1); @@ -263,7 +317,7 @@ #ifdef CONFIG_IEEE80211W if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION && conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { - if (pos + 2 + 4 > buf + len) + if (2 + 4 > buf + len - pos) return -1; if (pmkid == NULL) { /* PMKID Count */ @@ -364,6 +418,10 @@ capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ WPA_PUT_LE16(eid, capab); eid += 2; @@ -407,7 +465,7 @@ return res; pos += res; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { res = wpa_write_mdie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); @@ -415,7 +473,7 @@ return res; pos += res; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { res = wpa_write_wpa_ie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); @@ -472,9 +530,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, + struct wpa_state_machine *sm, int freq, const u8 *wpa_ie, size_t wpa_ie_len, - const u8 *mdie, size_t mdie_len) + const u8 *mdie, size_t mdie_len, + const u8 *owe_dh, size_t owe_dh_len) { struct wpa_ie_data data; int ciphers, key_mgmt, res, version; @@ -501,7 +560,24 @@ if (version == WPA_PROTO_RSN) { res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + if (!data.has_pairwise) + data.pairwise_cipher = wpa_default_rsn_cipher(freq); + if (!data.has_group) + data.group_cipher = wpa_default_rsn_cipher(freq); + if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie && + !wpa_key_mgmt_only_ft(data.key_mgmt)) { + /* Workaround for some HP and Epson printers that seem + * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP + * advertised RSNE to Association Request frame. */ + wpa_printf(MSG_DEBUG, + "RSN: FT set in RSNE AKM but MDE is missing from " + MACSTR + " - ignore FT AKM(s) because there's also a non-FT AKM", + MAC2STR(sm->addr)); + data.key_mgmt &= ~WPA_KEY_MGMT_FT; + } + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (0) { } @@ -509,12 +585,28 @@ selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R_AP + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; +#endif /* CONFIG_IEEE80211R_AP */ + else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) + selector = RSN_AUTH_KEY_MGMT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) + selector = RSN_AUTH_KEY_MGMT_FILS_SHA256; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP +#ifdef CONFIG_SHA384 + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; +#endif /* CONFIG_SHA384 */ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) selector = RSN_AUTH_KEY_MGMT_FT_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) selector = RSN_AUTH_KEY_MGMT_FT_PSK; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; @@ -531,6 +623,18 @@ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; +#ifdef CONFIG_OWE + else if (data.key_mgmt & WPA_KEY_MGMT_OWE) + selector = RSN_AUTH_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (data.key_mgmt & WPA_KEY_MGMT_DPP) + selector = RSN_AUTH_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + else if (data.key_mgmt & WPA_KEY_MGMT_OSEN) + selector = RSN_AUTH_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; selector = wpa_cipher_to_suite(WPA_PROTO_RSN, @@ -591,12 +695,28 @@ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R_AP + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; +#endif /* CONFIG_IEEE80211R_AP */ + else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384; + else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP +#ifdef CONFIG_SHA384 + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; @@ -611,6 +731,18 @@ #endif /* CONFIG_SAE */ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; +#ifdef CONFIG_OWE + else if (key_mgmt & WPA_KEY_MGMT_OWE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (key_mgmt & WPA_KEY_MGMT_DPP) + sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + else if (key_mgmt & WPA_KEY_MGMT_OSEN) + sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ else sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; @@ -634,12 +766,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 " @@ -648,14 +774,42 @@ } } +#ifdef CONFIG_SAE + if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL && + wpa_auth->conf.sae_require_mfp && + wpa_key_mgmt_sae(sm->wpa_key_mgmt) && + !(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, + "Management frame protection required with SAE, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_OCV + if ((data.capabilities & WPA_CAPABILITY_OCVC) && + !(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, + "Management frame protection required with OCV, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + wpa_auth_set_ocv(sm, wpa_auth->conf.ocv && + (data.capabilities & WPA_CAPABILITY_OCVC)); +#endif /* CONFIG_OCV */ + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || !(data.capabilities & WPA_CAPABILITY_MFPC)) sm->mgmt_frame_prot = 0; else sm->mgmt_frame_prot = 1; + + if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) { + wpa_printf(MSG_DEBUG, + "Management frame protection cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " @@ -668,9 +822,32 @@ "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); return WPA_INVALID_MDIE; } + } else if (mdie != NULL) { + wpa_printf(MSG_DEBUG, + "RSN: Trying to use non-FT AKM suite, but MDIE included"); + return WPA_INVALID_AKMP; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_OWE + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) { + wpa_printf(MSG_DEBUG, + "OWE: No Diffie-Hellman Parameter element"); + return WPA_INVALID_AKMP; + } +#ifdef CONFIG_DPP + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && owe_dh) { + /* Diffie-Hellman Parameter element can be used with DPP as + * well, so allow this to proceed. */ + } else +#endif /* CONFIG_DPP */ + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) { + wpa_printf(MSG_DEBUG, + "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM"); + return WPA_INVALID_AKMP; + } +#endif /* CONFIG_OWE */ + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); if (sm->pairwise < 0) return WPA_INVALID_PAIRWISE; @@ -681,6 +858,21 @@ else sm->wpa = WPA_VERSION_WPA; +#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS) + if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 || + sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) && + (sm->auth_alg == WLAN_AUTH_FILS_SK || + sm->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sm->auth_alg == WLAN_AUTH_FILS_PK) && + (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid || + os_memcmp_const(data.pmkid, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0)) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKR1Name match for FILS+FT"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */ + sm->pmksa = NULL; for (i = 0; i < data.num_pmkid; i++) { wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", @@ -712,14 +904,34 @@ } } if (sm->pmksa && pmkid) { + struct vlan_description *vlan; + + vlan = sm->pmksa->vlan_desc; wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, - "PMKID found from PMKSA cache " - "eap_type=%d vlan_id=%d", + "PMKID found from PMKSA cache eap_type=%d vlan=%d%s", sm->pmksa->eap_type_authsrv, - sm->pmksa->vlan_id); + vlan ? vlan->untagged : 0, + (vlan && vlan->tagged[0]) ? "+" : ""); os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } +#ifdef CONFIG_SAE + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid && + !sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKSA cache entry found for SAE"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_DPP + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKSA cache entry found for DPP"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_DPP */ + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { os_free(sm->wpa_ie); sm->wpa_ie = os_malloc(wpa_ie_len); @@ -791,7 +1003,7 @@ return 0; } - if (pos + 1 + RSN_SELECTOR_LEN < end && + if (1 + RSN_SELECTOR_LEN < end - pos && pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; @@ -812,36 +1024,6 @@ return 0; } -#ifdef CONFIG_PEERKEY - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { - ie->smk = pos + 2 + RSN_SELECTOR_LEN; - ie->smk_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { - ie->nonce = pos + 2 + RSN_SELECTOR_LEN; - ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { - ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; - ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { - ie->error = pos + 2 + RSN_SELECTOR_LEN; - ie->error_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } -#endif /* CONFIG_PEERKEY */ - #ifdef CONFIG_IEEE80211W if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { @@ -870,6 +1052,15 @@ } #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) { + ie->oci = pos + 2 + RSN_SELECTOR_LEN; + ie->oci_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_OCV */ + return 0; } @@ -887,13 +1078,13 @@ int ret = 0; os_memset(ie, 0, sizeof(*ie)); - for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) { if (pos[0] == 0xdd && ((pos == buf + len - 1) || pos[1] == 0)) { /* Ignore padding */ break; } - if (pos + 2 + pos[1] > end) { + if (2 + pos[1] > end - pos) { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " "underflow (ie=%d len=%d pos=%d)", pos[0], pos[1], (int) (pos - buf)); @@ -905,7 +1096,7 @@ if (*pos == WLAN_EID_RSN) { ie->rsn_ie = pos; ie->rsn_ie_len = pos[1] + 2; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; @@ -912,7 +1103,7 @@ } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { ie->ftie = pos; ie->ftie_len = pos[1] + 2; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) @@ -935,3 +1126,53 @@ { return sm ? sm->mgmt_frame_prot : 0; } + + +#ifdef CONFIG_OCV + +void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv) +{ + if (sm) + sm->ocv_enabled = ocv; +} + + +int wpa_auth_uses_ocv(struct wpa_state_machine *sm) +{ + return sm ? sm->ocv_enabled : 0; +} + +#endif /* CONFIG_OCV */ + + +#ifdef CONFIG_OWE +u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, + const u8 *req_ies, size_t req_ies_len) +{ + int res; + struct wpa_auth_config *conf; + + if (!sm) + return pos; + conf = &sm->wpa_auth->conf; + +#ifdef CONFIG_TESTING_OPTIONS + if (conf->own_ie_override_len) { + if (max_len < conf->own_ie_override_len) + return NULL; + wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing", + conf->own_ie_override, conf->own_ie_override_len); + os_memcpy(pos, conf->own_ie_override, + conf->own_ie_override_len); + return pos + conf->own_ie_override_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + res = wpa_write_rsn_ie(conf, pos, max_len, + sm->pmksa ? sm->pmksa->pmkid : NULL); + if (res < 0) + return pos; + return pos + res; +} +#endif /* CONFIG_OWE */ --- contrib/wpa/src/ap/wpa_auth_ie.h.orig +++ contrib/wpa/src/ap/wpa_auth_ie.h @@ -19,30 +19,24 @@ size_t gtk_len; const u8 *mac_addr; size_t mac_addr_len; -#ifdef CONFIG_PEERKEY - const u8 *smk; - size_t smk_len; - const u8 *nonce; - size_t nonce_len; - const u8 *lifetime; - size_t lifetime_len; - const u8 *error; - size_t error_len; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W const u8 *igtk; size_t igtk_len; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P const u8 *ip_addr_req; const u8 *ip_addr_alloc; #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + const u8 *oci; + size_t oci_len; +#endif /* CONFIG_OCV */ const u8 *osen; size_t osen_len; --- contrib/wpa/src/ap/wps_hostapd.c.orig +++ contrib/wpa/src/ap/wps_hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2012, Jouni Malinen + * Copyright (c) 2008-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -269,12 +269,6 @@ } -static int str_starts(const char *str, const char *start) -{ - return os_strncmp(str, start, os_strlen(start)) == 0; -} - - static void wps_reload_config(void *eloop_data, void *user_ctx) { struct hostapd_iface *iface = eloop_data; @@ -360,6 +354,18 @@ bss->wpa_pairwise, bss->rsn_pairwise); + if (hapd->conf->wps_cred_add_sae && + (cred->auth_type & WPS_AUTH_WPA2PSK) && + cred->key_len != 2 * PMK_LEN) { + bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE; +#ifdef CONFIG_IEEE80211W + if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) + bss->ieee80211w = + MGMT_FRAME_PROTECTION_OPTIONAL; + bss->sae_require_mfp = 1; +#endif /* CONFIG_IEEE80211W */ + } + if (cred->key_len >= 8 && cred->key_len < 64) { os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1); @@ -407,6 +413,7 @@ char buf[1024]; int multi_bss; int wpa; + int pmf_changed = 0; if (hapd->wps == NULL) return 0; @@ -445,6 +452,8 @@ os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); hapd->wps->ssid_len = cred->ssid_len; hapd->wps->encr_types = cred->encr_type; + hapd->wps->encr_types_rsn = cred->encr_type; + hapd->wps->encr_types_wpa = cred->encr_type; hapd->wps->auth_types = cred->auth_type; hapd->wps->ap_encr_type = cred->encr_type; hapd->wps->ap_auth_type = cred->auth_type; @@ -524,6 +533,10 @@ if (wpa) { char *prefix; +#ifdef CONFIG_IEEE80211W + int sae = 0; +#endif /* CONFIG_IEEE80211W */ + fprintf(nconf, "wpa=%d\n", wpa); fprintf(nconf, "wpa_key_mgmt="); @@ -532,10 +545,30 @@ fprintf(nconf, "WPA-EAP"); prefix = " "; } - if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { fprintf(nconf, "%sWPA-PSK", prefix); + prefix = " "; + } + if (hapd->conf->wps_cred_add_sae && + (cred->auth_type & WPS_AUTH_WPA2PSK) && + cred->key_len != 2 * PMK_LEN) { + fprintf(nconf, "%sSAE", prefix); +#ifdef CONFIG_IEEE80211W + sae = 1; +#endif /* CONFIG_IEEE80211W */ + } fprintf(nconf, "\n"); +#ifdef CONFIG_IEEE80211W + if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + fprintf(nconf, "ieee80211w=%d\n", + MGMT_FRAME_PROTECTION_OPTIONAL); + pmf_changed = 1; + } + if (sae) + fprintf(nconf, "sae_require_mfp=1\n"); +#endif /* CONFIG_IEEE80211W */ + fprintf(nconf, "wpa_pairwise="); prefix = ""; if (cred->encr_type & WPS_ENCR_AES) { @@ -589,6 +622,7 @@ str_starts(buf, "wep_default_key=") || str_starts(buf, "wep_key") || str_starts(buf, "wps_state=") || + (pmf_changed && str_starts(buf, "ieee80211w=")) || str_starts(buf, "wpa=") || str_starts(buf, "wpa_psk=") || str_starts(buf, "wpa_pairwise=") || @@ -872,7 +906,8 @@ hapd->wps_probe_resp_ie = NULL; if (deinit_only) { - hostapd_reset_ap_wps_ie(hapd); + if (hapd->drv_priv) + hostapd_reset_ap_wps_ie(hapd); return; } @@ -978,6 +1013,7 @@ { struct wps_context *wps; struct wps_registrar_config cfg; + u8 *multi_ap_netw_key = NULL; if (conf->wps_state == 0) { hostapd_wps_clear_ies(hapd, 0); @@ -1067,10 +1103,16 @@ 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; - if (conf->rsn_pairwise & WPA_CIPHER_TKIP) + wps->encr_types_rsn |= WPS_ENCR_AES; + } + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { wps->encr_types |= WPS_ENCR_TKIP; + wps->encr_types_rsn |= WPS_ENCR_TKIP; + } } if (conf->wpa & WPA_PROTO_WPA) { @@ -1079,10 +1121,14 @@ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA; - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { wps->encr_types |= WPS_ENCR_AES; - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + wps->encr_types_wpa |= WPS_ENCR_AES; + } + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { wps->encr_types |= WPS_ENCR_TKIP; + wps->encr_types_wpa |= WPS_ENCR_TKIP; + } } if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { @@ -1122,8 +1168,35 @@ /* Override parameters to enable security by default */ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; + wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP; + wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP; } + if ((hapd->conf->multi_ap & FRONTHAUL_BSS) && + hapd->conf->multi_ap_backhaul_ssid.ssid_len) { + cfg.multi_ap_backhaul_ssid_len = + hapd->conf->multi_ap_backhaul_ssid.ssid_len; + cfg.multi_ap_backhaul_ssid = + hapd->conf->multi_ap_backhaul_ssid.ssid; + + if (conf->multi_ap_backhaul_ssid.wpa_passphrase) { + cfg.multi_ap_backhaul_network_key = (const u8 *) + conf->multi_ap_backhaul_ssid.wpa_passphrase; + cfg.multi_ap_backhaul_network_key_len = + os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase); + } else if (conf->multi_ap_backhaul_ssid.wpa_psk) { + multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1); + if (!multi_ap_netw_key) + goto fail; + wpa_snprintf_hex((char *) multi_ap_netw_key, + 2 * PMK_LEN + 1, + conf->multi_ap_backhaul_ssid.wpa_psk->psk, + PMK_LEN); + cfg.multi_ap_backhaul_network_key = multi_ap_netw_key; + cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN; + } + } + wps->ap_settings = conf->ap_settings; wps->ap_settings_len = conf->ap_settings_len; @@ -1165,10 +1238,12 @@ hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); hapd->wps = wps; + bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); return 0; fail: + bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); hostapd_free_wps(wps); return -1; } @@ -1614,7 +1689,8 @@ unsigned int pin; struct wps_ap_pin_data data; - pin = wps_generate_pin(); + if (wps_generate_pin(&pin) < 0) + return NULL; os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); data.timeout = timeout; hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); --- contrib/wpa/src/common/cli.c.orig +++ contrib/wpa/src/common/cli.c @@ -0,0 +1,267 @@ +/* + * Common hostapd/wpa_supplicant command line interface functions + * Copyright (c) 2004-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "common/cli.h" + + +const char *const cli_license = +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n"; + +const char *const cli_full_license = +"This software may be distributed under the terms of the BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + + +void cli_txt_list_free(struct cli_txt_entry *e) +{ + dl_list_del(&e->list); + os_free(e->txt); + os_free(e); +} + + +void cli_txt_list_flush(struct dl_list *list) +{ + struct cli_txt_entry *e; + + while ((e = dl_list_first(list, struct cli_txt_entry, list))) + cli_txt_list_free(e); +} + + +struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list, + const char *txt) +{ + struct cli_txt_entry *e; + + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + if (os_strcmp(e->txt, txt) == 0) + return e; + } + return NULL; +} + + +void cli_txt_list_del(struct dl_list *txt_list, const char *txt) +{ + struct cli_txt_entry *e; + + e = cli_txt_list_get(txt_list, txt); + if (e) + cli_txt_list_free(e); +} + + +void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) +{ + u8 addr[ETH_ALEN]; + char buf[18]; + + if (hwaddr_aton(txt, addr) < 0) + return; + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + cli_txt_list_del(txt_list, buf); +} + + +void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, + int separator) +{ + const char *end; + char *buf; + + end = os_strchr(txt, separator); + if (end == NULL) + end = txt + os_strlen(txt); + buf = dup_binstr(txt, end - txt); + if (buf == NULL) + return; + cli_txt_list_del(txt_list, buf); + os_free(buf); +} + + +int cli_txt_list_add(struct dl_list *txt_list, const char *txt) +{ + struct cli_txt_entry *e; + + e = cli_txt_list_get(txt_list, txt); + if (e) + return 0; + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return -1; + e->txt = os_strdup(txt); + if (e->txt == NULL) { + os_free(e); + return -1; + } + dl_list_add(txt_list, &e->list); + return 0; +} + + +int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) +{ + u8 addr[ETH_ALEN]; + char buf[18]; + + if (hwaddr_aton(txt, addr) < 0) + return -1; + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + return cli_txt_list_add(txt_list, buf); +} + + +int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, + int separator) +{ + const char *end; + char *buf; + int ret; + + end = os_strchr(txt, separator); + if (end == NULL) + end = txt + os_strlen(txt); + buf = dup_binstr(txt, end - txt); + if (buf == NULL) + return -1; + ret = cli_txt_list_add(txt_list, buf); + os_free(buf); + return ret; +} + + +char ** cli_txt_list_array(struct dl_list *txt_list) +{ + unsigned int i, count = dl_list_len(txt_list); + char **res; + struct cli_txt_entry *e; + + res = os_calloc(count + 1, sizeof(char *)); + if (res == NULL) + return NULL; + + i = 0; + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + res[i] = os_strdup(e->txt); + if (res[i] == NULL) + break; + i++; + } + + return res; +} + + +int get_cmd_arg_num(const char *str, int pos) +{ + int arg = 0, i; + + for (i = 0; i <= pos; i++) { + if (str[i] != ' ') { + arg++; + while (i <= pos && str[i] != ' ') + i++; + } + } + + if (arg > 0) + arg--; + return arg; +} + + +int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, char *argv[]) +{ + int i, res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, "%s", cmd); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + + for (i = 0; i < argc; i++) { + res = os_snprintf(pos, end - pos, " %s", argv[i]); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + } + + buf[buflen - 1] = '\0'; + return 0; + +fail: + printf("Too long command\n"); + return -1; +} + + +int tokenize_cmd(char *cmd, char *argv[]) +{ + char *pos; + int argc = 0; + + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + if (*pos == '"') { + char *pos2 = os_strrchr(pos, '"'); + if (pos2) + pos = pos2 + 1; + } + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } + + return argc; +} --- contrib/wpa/src/common/cli.h.orig +++ contrib/wpa/src/common/cli.h @@ -0,0 +1,47 @@ +/* + * Common hostapd/wpa_supplicant command line interface functionality + * Copyright (c) 2004-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CLI_H +#define CLI_H + +#include "utils/list.h" + +extern const char *const cli_license; +extern const char *const cli_full_license; + +struct cli_txt_entry { + struct dl_list list; + char *txt; +}; + +void cli_txt_list_free(struct cli_txt_entry *e); +void cli_txt_list_flush(struct dl_list *list); + +struct cli_txt_entry * +cli_txt_list_get(struct dl_list *txt_list, const char *txt); + +void cli_txt_list_del(struct dl_list *txt_list, const char *txt); +void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt); +void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, + int separator); + +int cli_txt_list_add(struct dl_list *txt_list, const char *txt); +int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt); +int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, + int separator); + +char ** cli_txt_list_array(struct dl_list *txt_list); + +int get_cmd_arg_num(const char *str, int pos); +int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, + char *argv[]); + +#define max_args 10 +int tokenize_cmd(char *cmd, char *argv[]); + +#endif /* CLI_H */ --- contrib/wpa/src/common/common_module_tests.c.orig +++ contrib/wpa/src/common/common_module_tests.c @@ -1,6 +1,6 @@ /* * common module tests - * Copyright (c) 2014-2015, Jouni Malinen + * Copyright (c) 2014-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,10 +9,13 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" +#include "crypto/crypto.h" #include "ieee802_11_common.h" #include "ieee802_11_defs.h" #include "gas.h" #include "wpa_common.h" +#include "sae.h" struct ieee802_11_parse_test_data { @@ -52,6 +55,31 @@ 18, ParseOK, 9 }, { (u8 *) "\x8b\x00", 2, ParseOK, 1 }, { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 }, + { (u8 *) "\xed\x00", 2, ParseOK, 1 }, + { (u8 *) "\xef\x00", 2, ParseOK, 1 }, + { (u8 *) "\xef\x01\x11", 3, ParseOK, 1 }, + { (u8 *) "\xf0\x00", 2, ParseOK, 1 }, + { (u8 *) "\xf1\x00", 2, ParseOK, 1 }, + { (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 }, + { (u8 *) "\xf2\x00", 2, ParseOK, 1 }, + { (u8 *) "\xff\x00", 2, ParseUnknown, 1 }, + { (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 }, + { (u8 *) "\xff\x01\x01", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x02", 3, ParseOK, 1 }, + { (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 }, + { (u8 *) "\xff\x01\x04", 3, ParseOK, 1 }, + { (u8 *) "\xff\x01\x05", 3, ParseOK, 1 }, + { (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55", + 15, ParseOK, 1 }, + { (u8 *) "\xff\x01\x06", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x07", 3, ParseOK, 1 }, + { (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11, + ParseOK, 1 }, + { (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 }, { NULL, 0, ParseOK, 0 } }; @@ -58,6 +86,7 @@ static int ieee802_11_parse_tests(void) { int i, ret = 0; + struct wpabuf *buf; wpa_printf(MSG_INFO, "ieee802_11_parse tests"); @@ -83,6 +112,35 @@ ret = -1; } + buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01", + 16, 0x11223344); + do { + const u8 *pos; + + if (!buf) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 2 failed"); + ret = -1; + break; + } + + if (wpabuf_len(buf) != 2) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 3 failed"); + ret = -1; + break; + } + + pos = wpabuf_head(buf); + if (pos[0] != 0x01 || pos[1] != 0x02) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 3 failed"); + ret = -1; + break; + } + } while (0); + wpabuf_free(buf); + return ret; } @@ -192,6 +250,179 @@ } +static int sae_tests(void) +{ +#ifdef CONFIG_SAE + struct sae_data sae; + int ret = -1; + /* IEEE P802.11-REVmd/D2.1, Annex J.10 */ + const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 }; + const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 }; + const char *pw = "mekmitasdigoat"; + const char *pwid = "psk4internet"; + const u8 local_rand[] = { + 0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e, + 0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd, + 0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5, + 0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf + }; + const u8 local_mask[] = { + 0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c, + 0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4, + 0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e, + 0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15 + }; + const u8 local_commit[] = { + 0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4, + 0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39, + 0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0, + 0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4, + 0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58, + 0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25, + 0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5, + 0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61, + 0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4, + 0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98, + 0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88, + 0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52, + 0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b, + 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74 + }; + const u8 peer_commit[] = { + 0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea, + 0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70, + 0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55, + 0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2, + 0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9, + 0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d, + 0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38, + 0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f, + 0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc, + 0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf, + 0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf, + 0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62, + 0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b, + 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74 + }; + const u8 kck[] = { + 0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8, + 0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94, + 0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3, + 0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed + }; + const u8 pmk[] = { + 0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21, + 0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85, + 0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16, + 0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5 + }; + const u8 pmkid[] = { + 0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00, + 0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f + }; + const u8 local_confirm[] = { + 0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50, + 0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a, + 0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca, + 0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9, + 0xfc, 0x77 + }; + const u8 peer_confirm[] = { + 0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89, + 0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe, + 0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e, + 0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa, + 0xc2, 0xfd + }; + struct wpabuf *buf = NULL; + struct crypto_bignum *mask = NULL; + + os_memset(&sae, 0, sizeof(sae)); + buf = wpabuf_alloc(1000); + if (!buf || + sae_set_group(&sae, 19) < 0 || + sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw), + pwid, &sae) < 0) + goto fail; + + /* Override local values based on SAE test vector */ + crypto_bignum_deinit(sae.tmp->sae_rand, 1); + sae.tmp->sae_rand = crypto_bignum_init_set(local_rand, + sizeof(local_rand)); + mask = crypto_bignum_init_set(local_mask, sizeof(local_mask)); + if (!sae.tmp->sae_rand || !mask) + goto fail; + + if (crypto_bignum_add(sae.tmp->sae_rand, mask, + sae.tmp->own_commit_scalar) < 0 || + crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order, + sae.tmp->own_commit_scalar) < 0 || + crypto_ec_point_mul(sae.tmp->ec, sae.tmp->pwe_ecc, mask, + sae.tmp->own_commit_element_ecc) < 0 || + crypto_ec_point_invert(sae.tmp->ec, + sae.tmp->own_commit_element_ecc) < 0) + goto fail; + + /* Check that output matches the test vector */ + sae_write_commit(&sae, buf, NULL, pwid); + wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf); + + if (wpabuf_len(buf) != sizeof(local_commit) || + os_memcmp(wpabuf_head(buf), local_commit, + sizeof(local_commit)) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit"); + goto fail; + } + + if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL, + NULL) != 0 || + sae_process_commit(&sae) < 0) + goto fail; + + if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK"); + goto fail; + } + + if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK"); + goto fail; + } + + if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID"); + goto fail; + } + + buf->used = 0; + sae.send_confirm = 1; + sae_write_confirm(&sae, buf); + wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf); + + if (wpabuf_len(buf) != sizeof(local_confirm) || + os_memcmp(wpabuf_head(buf), local_confirm, + sizeof(local_confirm)) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm"); + goto fail; + } + + if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0) + goto fail; + + ret = 0; +fail: + sae_clear_data(&sae); + wpabuf_free(buf); + crypto_bignum_deinit(mask, 1); + return ret; +#else /* CONFIG_SAE */ + return 0; +#endif /* CONFIG_SAE */ +} + + int common_module_tests(void) { int ret = 0; @@ -200,6 +431,7 @@ if (ieee802_11_parse_tests() < 0 || gas_tests() < 0 || + sae_tests() < 0 || rsn_ie_parse_tests() < 0) ret = -1; --- contrib/wpa/src/common/ctrl_iface_common.c.orig +++ contrib/wpa/src/common/ctrl_iface_common.c @@ -0,0 +1,209 @@ +/* + * Common hostapd/wpa_supplicant ctrl iface code. + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include + +#include "utils/common.h" +#include "ctrl_iface_common.h" + +static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len, + struct sockaddr_storage *b, socklen_t b_len) +{ + if (a->ss_family != b->ss_family) + return 1; + + switch (a->ss_family) { +#ifdef CONFIG_CTRL_IFACE_UDP + case AF_INET: + { + struct sockaddr_in *in_a, *in_b; + + in_a = (struct sockaddr_in *) a; + in_b = (struct sockaddr_in *) b; + + if (in_a->sin_port != in_b->sin_port) + return 1; + if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr) + return 1; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *in6_a, *in6_b; + + in6_a = (struct sockaddr_in6 *) a; + in6_b = (struct sockaddr_in6 *) b; + + if (in6_a->sin6_port != in6_b->sin6_port) + return 1; + if (os_memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr, + sizeof(in6_a->sin6_addr)) != 0) + return 1; + break; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + case AF_UNIX: + { + struct sockaddr_un *u_a, *u_b; + + u_a = (struct sockaddr_un *) a; + u_b = (struct sockaddr_un *) b; + + if (a_len != b_len || + os_memcmp(u_a->sun_path, u_b->sun_path, + a_len - offsetof(struct sockaddr_un, sun_path)) + != 0) + return 1; + break; + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ + default: + return 1; + } + + return 0; +} + + +void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, + socklen_t socklen) +{ + switch (sock->ss_family) { +#ifdef CONFIG_CTRL_IFACE_UDP + case AF_INET: + case AF_INET6: + { + char host[NI_MAXHOST] = { 0 }; + char service[NI_MAXSERV] = { 0 }; + + getnameinfo((struct sockaddr *) sock, socklen, + host, sizeof(host), + service, sizeof(service), + NI_NUMERICHOST); + + wpa_printf(level, "%s %s:%s", msg, host, service); + break; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + case AF_UNIX: + { + char addr_txt[200]; + + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) ((struct sockaddr_un *) sock)->sun_path, + socklen - offsetof(struct sockaddr_un, sun_path)); + wpa_printf(level, "%s %s", msg, addr_txt); + break; + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ + default: + wpa_printf(level, "%s", msg); + break; + } +} + + +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, 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); + return 0; +} + + +int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) { + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached", + from, fromlen); + dl_list_del(&dst->list); + os_free(dst); + return 0; + } + } + + return -1; +} + + +int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen, const char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) { + sockaddr_print(MSG_DEBUG, + "CTRL_IFACE changed monitor level", + from, fromlen); + dst->debug_level = atoi(level); + return 0; + } + } + + return -1; +} --- contrib/wpa/src/common/ctrl_iface_common.h.orig +++ contrib/wpa/src/common/ctrl_iface_common.h @@ -0,0 +1,42 @@ +/* + * Common hostapd/wpa_supplicant ctrl iface code. + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifndef CONTROL_IFACE_COMMON_H +#define CONTROL_IFACE_COMMON_H + +#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 + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. + */ +struct wpa_ctrl_dst { + struct dl_list list; + struct sockaddr_storage addr; + 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, 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, + socklen_t fromlen, const char *level); + +#endif /* CONTROL_IFACE_COMMON_H */ --- contrib/wpa/src/common/defs.h.orig +++ contrib/wpa/src/common/defs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - Common definitions - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2018, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -51,16 +51,35 @@ #define WPA_KEY_MGMT_OSEN BIT(15) #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16) #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17) +#define WPA_KEY_MGMT_FILS_SHA256 BIT(18) +#define WPA_KEY_MGMT_FILS_SHA384 BIT(19) +#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20) +#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21) +#define WPA_KEY_MGMT_OWE BIT(22) +#define WPA_KEY_MGMT_DPP BIT(23) +#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24) +#define WPA_KEY_MGMT_FT (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) + static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { return !!(akm & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | WPA_KEY_MGMT_CCKM | WPA_KEY_MGMT_OSEN | WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_IEEE8021X_SUITE_B | - WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)); + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | + WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)); } static inline int wpa_key_mgmt_wpa_psk(int akm) @@ -74,11 +93,21 @@ static inline int wpa_key_mgmt_ft(int akm) { - return !!(akm & (WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_SAE)); + return !!(akm & WPA_KEY_MGMT_FT); } +static inline int wpa_key_mgmt_only_ft(int akm) +{ + int ft = wpa_key_mgmt_ft(akm); + akm &= ~WPA_KEY_MGMT_FT; + return ft && !akm; +} + +static inline int wpa_key_mgmt_ft_psk(int akm) +{ + return !!(akm & WPA_KEY_MGMT_FT_PSK); +} + static inline int wpa_key_mgmt_sae(int akm) { return !!(akm & (WPA_KEY_MGMT_SAE | @@ -85,17 +114,32 @@ WPA_KEY_MGMT_FT_SAE)); } +static inline int wpa_key_mgmt_fils(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)); +} + static inline int wpa_key_mgmt_sha256(int akm) { return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_OSEN | - WPA_KEY_MGMT_IEEE8021X_SUITE_B)); + WPA_KEY_MGMT_IEEE8021X_SUITE_B | + WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA256)); } static inline int wpa_key_mgmt_sha384(int akm) { - return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192); + return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA384)); } static inline int wpa_key_mgmt_suite_b(int akm) @@ -108,7 +152,10 @@ { return wpa_key_mgmt_wpa_ieee8021x(akm) || wpa_key_mgmt_wpa_psk(akm) || - wpa_key_mgmt_sae(akm); + wpa_key_mgmt_fils(akm) || + wpa_key_mgmt_sae(akm) || + akm == WPA_KEY_MGMT_OWE || + akm == WPA_KEY_MGMT_DPP; } static inline int wpa_key_mgmt_wpa_any(int akm) @@ -132,7 +179,13 @@ #define WPA_AUTH_ALG_LEAP BIT(2) #define WPA_AUTH_ALG_FT BIT(3) #define WPA_AUTH_ALG_SAE BIT(4) +#define WPA_AUTH_ALG_FILS BIT(5) +#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6) +static inline int wpa_auth_alg_fils(int alg) +{ + return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS)); +} enum wpa_alg { WPA_ALG_NONE, @@ -312,6 +365,7 @@ WPA_CTRL_REQ_EAP_PASSPHRASE, WPA_CTRL_REQ_SIM, WPA_CTRL_REQ_PSK_PASSPHRASE, + WPA_CTRL_REQ_EXT_CERT_CHECK, NUM_WPA_CTRL_REQS }; @@ -319,13 +373,13 @@ #define EAP_MAX_METHODS 8 enum mesh_plink_state { - PLINK_LISTEN = 1, - PLINK_OPEN_SENT, - PLINK_OPEN_RCVD, + PLINK_IDLE = 1, + PLINK_OPN_SNT, + PLINK_OPN_RCVD, PLINK_CNF_RCVD, PLINK_ESTAB, PLINK_HOLDING, - PLINK_BLOCKED, + PLINK_BLOCKED, /* not defined in the IEEE 802.11 standard */ }; enum set_band { @@ -334,4 +388,35 @@ WPA_SETBAND_2G }; +enum wpa_radio_work_band { + BAND_2_4_GHZ = BIT(0), + BAND_5_GHZ = BIT(1), + BAND_60_GHZ = BIT(2), +}; + +enum beacon_rate_type { + BEACON_RATE_LEGACY, + BEACON_RATE_HT, + BEACON_RATE_VHT +}; + +enum eap_proxy_sim_state { + SIM_STATE_ERROR, +}; + +#define OCE_STA BIT(0) +#define OCE_STA_CFON BIT(1) +#define OCE_AP BIT(2) + +/* enum chan_width - Channel width definitions */ +enum chan_width { + CHAN_WIDTH_20_NOHT, + CHAN_WIDTH_20, + CHAN_WIDTH_40, + CHAN_WIDTH_80, + CHAN_WIDTH_80P80, + CHAN_WIDTH_160, + CHAN_WIDTH_UNKNOWN +}; + #endif /* DEFS_H */ --- contrib/wpa/src/common/dhcp.h.orig +++ contrib/wpa/src/common/dhcp.h @@ -0,0 +1,279 @@ +/* + * DHCP definitions + * Copyright (c) 2014-2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DHCP_H +#define DHCP_H + +/* + * Translate Linux to FreeBSD + */ +#define iphdr ip +#define ihl ip_hl +#define verson ip_v +#define tos ip_tos +#define tot_len ip_len +#define id ip_id +#define frag_off ip_off +#define ttl ip_ttl +#define protocol ip_p +#define check ip_sum +#define saddr ip_src +#define daddr ip_dst + +#include +#if __FAVOR_BSD +#include +#else +#define __FAVOR_BSD 1 +#include +#undef __FAVOR_BSD +#endif + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +struct dhcp_data { + u8 op; + u8 htype; + u8 hlen; + u8 hops; + be32 xid; + be16 secs; + be16 flags; + be32 client_ip; + be32 your_ip; + be32 server_ip; + be32 relay_ip; + u8 hw_addr[16]; + u8 serv_name[64]; + u8 boot_file[128]; +} STRUCT_PACKED; + +struct bootp_pkt { + struct iphdr iph; + struct udphdr udph; + u8 op; + u8 htype; + u8 hlen; + u8 hops; + be32 xid; + be16 secs; + be16 flags; + be32 client_ip; + be32 your_ip; + be32 server_ip; + be32 relay_ip; + u8 hw_addr[16]; + u8 serv_name[64]; + u8 boot_file[128]; + u8 exten[312]; +} STRUCT_PACKED; + +#define DHCP_MAGIC 0x63825363 + +/* + * IANA DHCP/BOOTP registry + * http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml +*/ +enum dhcp_options { + DHCP_OPT_PAD = 0, + DHCP_OPT_SUBNET_MASK = 1, + DHCP_OPT_TIME_OFFSET = 2, + DHCP_OPT_ROUTER = 3, + DHCP_OPT_TIME_SERVER = 4, + DHCP_OPT_NAME_SERVER = 5, + DHCP_OPT_DOMAIN_NAME_SERVER = 6, + DHCP_OPT_LOG_SERVER = 7, + DHCP_OPT_QUOTES_SERVER = 8, + DHCP_OPT_LPR_SERVER = 9, + DHCP_OPT_IMPRESS_SERVER = 10, + DHCP_OPT_RLP_SERVER = 11, + DHCP_OPT_HOSTNAME = 12, + DHCP_OPT_BOOT_FILE_SIZE = 13, + DHCP_OPT_MERIT_DUMP_FILE = 14, + DHCP_OPT_DOMAIN_NAME = 15, + DHCP_OPT_SWAP_SERVER = 16, + DHCP_OPT_ROOT_PATH = 17, + DHCP_OPT_EXTENSION_PATH = 18, + DHCP_OPT_FORWARD = 19, + DHCP_OPT_SRC_RTE = 20, + DHCP_OPT_POLICY_FILTER = 21, + DHCP_OPT_MAX_DG_ASSEMBLY = 22, + DHCP_OPT_DEFAULT_IP_TTL = 23, + DHCP_OPT_MTU_TIMEOUT = 24, + DHCP_OPT_MTU_PLATEAU = 25, + DHCP_OPT_MTU_INTERFACE = 26, + DHCP_OPT_ALL_SUBNETS_LOCAL = 27, + DHCP_OPT_BROADCAST_ADDRESS = 28, + DHCP_OPT_MASK_DISCOVERY = 29, + DHCP_OPT_MASK_SUPPLIER = 30, + DHCP_OPT_ROUTER_DISCOVERY = 31, + DHCP_OPT_ROUTER_SOLICITATION_ADDRESS = 32, + DHCP_OPT_STATIC_ROUTE = 33, + DHCP_OPT_TRAILERS = 34, + DHCP_OPT_ARP_TIMEOUT = 35, + DHCP_OPT_ETHERNET = 36, + DHCP_OPT_TCP_DEFAULT_TTL = 37, + DHCP_OPT_TCP_KEEPALIVE_INTERVAL = 38, + DHCP_OPT_TCP_KEEPALIVE_GARBAGE = 39, + DHCP_OPT_NIS_DOMAIN = 40, + DHCP_OPT_NIS_SERVERS = 41, + DHCP_OPT_NTP_SERVERS = 42, + DHCP_OPT_VENDOR_SPECIFIC = 43, + DHCP_OPT_NETBIOS_NAME_SERVER = 44, + DHCP_OPT_NETBIOS_DISTRIBUTION_SERVER = 45, + DHCP_OPT_NETBIOS_NODE_TYPE = 46, + DHCP_OPT_NETBIOS_SCOPE = 47, + DHCP_OPT_FONT_SERVER = 48, + DHCP_OPT_DISPLAY_MANAGER = 49, + DHCP_OPT_REQUESTED_IP_ADDRESS = 50, + DHCP_OPT_IP_ADDRESS_LEASE_TIME = 51, + DHCP_OPT_OVERLOAD = 52, + DHCP_OPT_MSG_TYPE = 53, + DHCP_OPT_SERVER_ID = 54, + DHCP_OPT_PARAMETER_REQ_LIST = 55, + DHCP_OPT_MESSAGE = 56, + DHCP_OPT_MAX_MESSAGE_SIZE = 57, + DHCP_OPT_RENEWAL_TIME = 58, + DHCP_OPT_REBINDING_TIME = 59, + DHCP_OPT_VENDOR_CLASS_ID = 60, + DHCP_OPT_CLIENT_ID = 61, + DHCP_OPT_NETWARE_IP_DOMAIN = 62, + DHCP_OPT_NETWARE_IP_OPTION = 63, + DHCP_OPT_NIS_V3_DOMAIN = 64, + DHCP_OPT_NIS_V3_SERVERS = 65, + DHCP_OPT_TFTP_SERVER_NAME = 66, + DHCP_OPT_BOOT_FILE_NAME = 67, + DHCP_OPT_HOME_AGENT_ADDRESSES = 68, + DHCP_OPT_SMTP_SERVER = 69, + DHCP_OPT_POP3_SERVER = 70, + DHCP_OPT_NNTP_SERVER = 71, + DHCP_OPT_WWW_SERVER = 72, + DHCP_OPT_FINGER_SERVER = 73, + DHCP_OPT_IRC_SERVER = 74, + DHCP_OPT_STREETTALK_SERVER = 75, + DHCP_OPT_STDA_SERVER = 76, + DHCP_OPT_USER_CLASS = 77, + DHCP_OPT_DIRECTORY_AGENT = 78, + DHCP_OPT_SERVICE_SCOPE = 79, + DHCP_OPT_RAPID_COMMIT = 80, + DHCP_OPT_CLIENT_FQDN = 81, + DHCP_OPT_RELAY_AGENT_INFO = 82, + DHCP_OPT_ISNS = 83, + DHCP_OPT_NDS_SERVERS = 85, + DHCP_OPT_NDS_TREE_NAME = 86, + DHCP_OPT_NDS_CONTEXT = 87, + DHCP_OPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88, + DHCP_OPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89, + DHCP_OPT_AUTHENTICATION = 90, + DHCP_OPT_CLIENT_LAST_TRANSACTION_TIME = 91, + DHCP_OPT_ASSOCIATED_IP = 92, + DHCP_OPT_CLIENT_SYSYEM = 93, + DHCP_OPT_CLIENT_NDI = 94, + DHCP_OPT_LDAP = 95, + DHCP_OPT_UUID_GUID = 97, + DHCP_OPT_USER_AUTH = 98, + DHCP_OPT_GEOCONF_CIVIC = 99, + DHCP_OPT_PCODE = 100, + DHCP_OPT_TCODE = 101, + DHCP_OPT_NETINFO_ADDRESS = 112, + DHCP_OPT_NETINFO_TAG = 113, + DHCP_OPT_URL = 114, + DHCP_OPT_AUTO_CONFIG = 116, + DHCP_OPT_NAME_SERVICE_SEARCH = 117, + DHCP_OPT_SUBNET_SELECTION = 118, + DHCP_OPT_DOMAIN_SEARCH = 119, + DHCP_OPT_SIP_SERVERS_DCP = 120, + DHCP_OPT_CLASSLESS_STATIC_ROUTE = 121, + DHCP_OPT_CCC = 122, + DHCP_OPT_GEOCONF = 123, + DHCP_OPT_V_I_VENDOR_CLASS = 124, + DHCP_OPT_V_I_VENDOR_SPECIFIC_INFO = 125, + DHCP_OPT_PANA_AGENT = 136, + DHCP_OPT_V4_LOST = 137, + DHCP_OPT_CAPWAP_AC_V4 = 138, + DHCP_OPT_IPV4_ADDRESS_MOS = 139, + DHCP_OPT_IPV4_FQDN_MOS = 140, + DHCP_OPT_SIP_UA_CONF = 141, + DHCP_OPT_IPV4_ADDRESS_ANDSF = 142, + DHCP_OPT_GEOLOC = 144, + DHCP_OPT_FORCERENEW_NONCE_CAPABLE = 145, + DHCP_OPT_RDNSS_SELECTION = 146, + DHCP_OPT_TFTP_SERVER_ADDRESS = 150, + DHCP_OPT_STATUS_CODE = 151, + DHCP_OPT_BASE_TIME = 152, + DHCP_OPT_START_TIME_OF_STATE = 153, + DHCP_OPT_QUERY_START_TIME = 154, + DHCP_OPT_QUERY_END_TIME = 155, + DHCP_OPT_STATE = 156, + DHCP_OPT_DATA_SOURCE = 157, + DHCP_OPT_V4_PCP_SERVER = 158, + DHCP_OPT_V4_PORTPARAMS = 159, + DHCP_OPT_CAPTIVE_PORTAL = 160, + DHCP_OPT_CONF_FILE = 209, + DHCP_OPT_PATH_PREFIX = 210, + DHCP_OPT_REBOOT_TIME = 211, + DHCP_OPT_6RD = 212, + DHCP_OPT_V4_ACCESS_DOMAIN = 213, + DHCP_OPT_SUBNET_ALLOCATION = 220, + DHCP_OPT_VSS = 221, + DHCP_OPT_END = 255 +}; + +enum dhcp_message_types { + DHCPDISCOVER = 1, + DHCPOFFER = 2, + DHCPREQUEST = 3, + DHCPDECLINE = 4, + DHCPACK = 5, + DHCPNAK = 6, + DHCPRELEASE = 7, + DHCPINFORM = 8, + DHCPFORCERENEW = 9, + DHCPLEASEQUERY = 10, + DHCPLEASEUNASSIGNED = 11, + DHCPLEASEUNKNOWN = 12, + DHCPLEASEACTIVE = 13, + DHCPBULKLEASEQUERY = 14, + DHCPLEASEQUERYDONE = 15, + DHCPACTIVELEASEQUERY = 16, + DHCPLEASEQUERYSTATUS = 17, + DHCPTLS = 18, +}; + +enum dhcp_relay_agent_suboptions { + DHCP_RELAY_OPT_AGENT_CIRCUIT_ID = 1, + DHCP_RELAY_OPT_AGENT_REMOTE_ID = 2, + DHCP_RELAY_OPT_DOCSIS_DEVICE_CLASS = 4, + DHCP_RELAY_OPT_LINK_SELECTION = 5, + DHCP_RELAY_OPT_SUBSCRIBE_ID = 6, + DHCP_RELAY_OPT_RADIUS_ATTRIBUTES = 7, + DHCP_RELAY_OPT_AUTHENTICATION = 8, + DHCP_RELAY_OPT_VEDOR_SPECIFIC = 9, + DHCP_RELAY_OPT_RELAY_AGENT_FLAGS = 10, + DHCP_RELAY_OPT_SERVER_ID_OVERRIDE = 11, + DHCP_RELAY_OPT_RELAY_AGENT_ID = 12, + DHCP_RELAY_OPT_ACCESS_TECHNOLOGY_TYPE = 13, + DHCP_RELAY_OPT_ACCESS_NETWORK_NAME = 14, + DHCP_RELAY_OPT_ACCESS_POINT_NAME = 15, + DHCP_RELAY_OPT_ACCESS_POINT_BSSID = 16, + DHCP_RELAY_OPT_OPERATOR_ID = 17, + DHCP_RELAY_OPT_OPERATOR_REALM = 18, + DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION = 151, + DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION_CONTROL = 152, +}; + +enum access_technology_types { + ACCESS_TECHNOLOGY_VIRTUAL = 1, + ACCESS_TECHNOLOGY_PPP = 2, + ACCESS_TECHNOLOGY_ETHERNET = 3, + ACCESS_TECHNOLOGY_WLAN = 4, + ACCESS_TECHNOLOGY_WIMAX = 5, +}; + +#endif /* DHCP_H */ --- contrib/wpa/src/common/dpp.c.orig +++ contrib/wpa/src/common/dpp.c @@ -0,0 +1,8721 @@ +/* + * DPP functionality shared between hostapd and wpa_supplicant + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include +#include +#include + +#include "utils/common.h" +#include "utils/base64.h" +#include "utils/json.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "common/gas.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" +#include "drivers/driver.h" +#include "dpp.h" + + +#ifdef CONFIG_TESTING_OPTIONS +enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED; +u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +u8 dpp_pkex_ephemeral_key_override[600]; +size_t dpp_pkex_ephemeral_key_override_len = 0; +u8 dpp_protocol_key_override[600]; +size_t dpp_protocol_key_override_len = 0; +u8 dpp_nonce_override[DPP_MAX_NONCE_LEN]; +size_t dpp_nonce_override_len = 0; + +static int dpp_test_gen_invalid_key(struct wpabuf *msg, + const struct dpp_curve_params *curve); +#endif /* CONFIG_TESTING_OPTIONS */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) +/* Compatibility wrappers for older versions. */ + +static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + sig->r = r; + sig->s = s; + return 1; +} + + +static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, + const BIGNUM **ps) +{ + if (pr) + *pr = sig->r; + if (ps) + *ps = sig->s; +} + +#endif + + +struct dpp_global { + struct dl_list bootstrap; /* struct dpp_bootstrap_info */ + struct dl_list configurator; /* struct dpp_configurator */ +}; + +static const struct dpp_curve_params dpp_curves[] = { + /* The mandatory to support and the default NIST P-256 curve needs to + * be the first entry on this list. */ + { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" }, + { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" }, + { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" }, + { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" }, + { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" }, + { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" }, + { NULL, 0, 0, 0, 0, NULL, 0, NULL } +}; + + +/* Role-specific elements for PKEX */ + +/* NIST P-256 */ +static const u8 pkex_init_x_p256[32] = { + 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b, + 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54, + 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07, + 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25 + }; +static const u8 pkex_init_y_p256[32] = { + 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b, + 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc, + 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45, + 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4 + }; +static const u8 pkex_resp_x_p256[32] = { + 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39, + 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f, + 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f, + 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76 +}; +static const u8 pkex_resp_y_p256[32] = { + 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19, + 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1, + 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a, + 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67 +}; + +/* NIST P-384 */ +static const u8 pkex_init_x_p384[48] = { + 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa, + 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68, + 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53, + 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac, + 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12, + 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3 +}; +static const u8 pkex_init_y_p384[48] = { + 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29, + 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56, + 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7, + 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6, + 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94, + 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18 +}; +static const u8 pkex_resp_x_p384[48] = { + 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98, + 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97, + 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92, + 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44, + 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf, + 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf +}; +static const u8 pkex_resp_y_p384[48] = { + 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c, + 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c, + 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3, + 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1, + 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63, + 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06 +}; + +/* NIST P-521 */ +static const u8 pkex_init_x_p521[66] = { + 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23, + 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0, + 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76, + 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5, + 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38, + 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01, + 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e, + 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d, + 0x97, 0x76 +}; +static const u8 pkex_init_y_p521[66] = { + 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59, + 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99, + 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b, + 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd, + 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f, + 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf, + 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02, + 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d, + 0x03, 0xa8 +}; +static const u8 pkex_resp_x_p521[66] = { + 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a, + 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44, + 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f, + 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb, + 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48, + 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e, + 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a, + 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97, + 0x84, 0xb4 +}; +static const u8 pkex_resp_y_p521[66] = { + 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d, + 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20, + 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3, + 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84, + 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9, + 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2, + 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80, + 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53, + 0xce, 0xe1 +}; + +/* Brainpool P-256r1 */ +static const u8 pkex_init_x_bp_p256r1[32] = { + 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10, + 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca, + 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75, + 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8 +}; +static const u8 pkex_init_y_bp_p256r1[32] = { + 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd, + 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30, + 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe, + 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b +}; +static const u8 pkex_resp_x_bp_p256r1[32] = { + 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f, + 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a, + 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a, + 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3 +}; +static const u8 pkex_resp_y_bp_p256r1[32] = { + 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd, + 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2, + 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e, + 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64 +}; + +/* Brainpool P-384r1 */ +static const u8 pkex_init_x_bp_p384r1[48] = { + 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd, + 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19, + 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06, + 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62, + 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30, + 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe +}; +static const u8 pkex_init_y_bp_p384r1[48] = { + 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99, + 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86, + 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32, + 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9, + 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e, + 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52 +}; +static const u8 pkex_resp_x_bp_p384r1[48] = { + 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0, + 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25, + 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b, + 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71, + 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce, + 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c +}; +static const u8 pkex_resp_y_bp_p384r1[48] = { + 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65, + 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04, + 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70, + 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c, + 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb, + 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1 +}; + +/* Brainpool P-512r1 */ +static const u8 pkex_init_x_bp_p512r1[64] = { + 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c, + 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51, + 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc, + 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95, + 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d, + 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff, + 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc, + 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f +}; +static const u8 pkex_init_y_bp_p512r1[64] = { + 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94, + 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8, + 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3, + 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45, + 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e, + 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58, + 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71, + 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99 +}; +static const u8 pkex_resp_x_bp_p512r1[64] = { + 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72, + 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76, + 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19, + 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e, + 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9, + 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88, + 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29, + 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e +}; +static const u8 pkex_resp_y_bp_p512r1[64] = { + 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81, + 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68, + 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa, + 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d, + 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c, + 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09, + 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56, + 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7 +}; + + +static void dpp_debug_print_point(const char *title, const EC_GROUP *group, + const EC_POINT *point) +{ + BIGNUM *x, *y; + BN_CTX *ctx; + char *x_str = NULL, *y_str = NULL; + + if (!wpa_debug_show_keys) + return; + + ctx = BN_CTX_new(); + x = BN_new(); + y = BN_new(); + if (!ctx || !x || !y || + EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1) + goto fail; + + x_str = BN_bn2hex(x); + y_str = BN_bn2hex(y); + if (!x_str || !y_str) + goto fail; + + wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str); + +fail: + OPENSSL_free(x_str); + OPENSSL_free(y_str); + BN_free(x); + BN_free(y); + BN_CTX_free(ctx); +} + + +static int dpp_hash_vector(const struct dpp_curve_params *curve, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + if (curve->hash_len == 32) + return sha256_vector(num_elem, addr, len, mac); + if (curve->hash_len == 48) + return sha384_vector(num_elem, addr, len, mac); + if (curve->hash_len == 64) + return sha512_vector(num_elem, addr, len, mac); + return -1; +} + + +static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len, + const char *label, u8 *out, size_t outlen) +{ + if (hash_len == 32) + return hmac_sha256_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + if (hash_len == 48) + return hmac_sha384_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + if (hash_len == 64) + return hmac_sha512_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + return -1; +} + + +static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) +{ + if (hash_len == 32) + return hmac_sha256_vector(key, key_len, num_elem, addr, len, + mac); + if (hash_len == 48) + return hmac_sha384_vector(key, key_len, num_elem, addr, len, + mac); + if (hash_len == 64) + return hmac_sha512_vector(key, key_len, num_elem, addr, len, + mac); + return -1; +} + + +static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, u8 *mac) +{ + if (hash_len == 32) + return hmac_sha256(key, key_len, data, data_len, mac); + if (hash_len == 48) + return hmac_sha384(key, key_len, data, data_len, mac); + if (hash_len == 64) + return hmac_sha512(key, key_len, data, data_len, mac); + return -1; +} + + +static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len) +{ + int num_bytes, offset; + + num_bytes = BN_num_bytes(bn); + if ((size_t) num_bytes > len) + return -1; + offset = len - num_bytes; + os_memset(pos, 0, offset); + BN_bn2bin(bn, pos + offset); + return 0; +} + + +static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix) +{ + int len, res; + EC_KEY *eckey; + struct wpabuf *buf; + unsigned char *pos; + + eckey = EVP_PKEY_get1_EC_KEY(pkey); + if (!eckey) + return NULL; + EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED); + len = i2o_ECPublicKey(eckey, NULL); + if (len <= 0) { + wpa_printf(MSG_ERROR, + "DDP: Failed to determine public key encoding length"); + EC_KEY_free(eckey); + return NULL; + } + + buf = wpabuf_alloc(len); + if (!buf) { + EC_KEY_free(eckey); + return NULL; + } + + pos = wpabuf_put(buf, len); + res = i2o_ECPublicKey(eckey, &pos); + EC_KEY_free(eckey); + if (res != len) { + wpa_printf(MSG_ERROR, + "DDP: Failed to encode public key (res=%d/%d)", + res, len); + wpabuf_free(buf); + return NULL; + } + + if (!prefix) { + /* Remove 0x04 prefix to match DPP definition */ + pos = wpabuf_mhead(buf); + os_memmove(pos, pos + 1, len - 1); + buf->used--; + } + + return buf; +} + + +static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group, + const u8 *buf_x, const u8 *buf_y, + size_t len) +{ + EC_KEY *eckey = NULL; + BN_CTX *ctx; + EC_POINT *point = NULL; + BIGNUM *x = NULL, *y = NULL; + EVP_PKEY *pkey = NULL; + + ctx = BN_CTX_new(); + if (!ctx) { + wpa_printf(MSG_ERROR, "DPP: Out of memory"); + return NULL; + } + + point = EC_POINT_new(group); + x = BN_bin2bn(buf_x, len, NULL); + y = BN_bin2bn(buf_y, len, NULL); + if (!point || !x || !y) { + wpa_printf(MSG_ERROR, "DPP: Out of memory"); + goto fail; + } + + if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) { + wpa_printf(MSG_ERROR, + "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (!EC_POINT_is_on_curve(group, point, ctx) || + EC_POINT_is_at_infinity(group, point)) { + wpa_printf(MSG_ERROR, "DPP: Invalid point"); + goto fail; + } + dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point); + + eckey = EC_KEY_new(); + if (!eckey || + EC_KEY_set_group(eckey, group) != 1 || + EC_KEY_set_public_key(eckey, point) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to set EC_KEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + + pkey = EVP_PKEY_new(); + if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { + wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY"); + goto fail; + } + +out: + BN_free(x); + BN_free(y); + EC_KEY_free(eckey); + EC_POINT_free(point); + BN_CTX_free(ctx); + return pkey; +fail: + EVP_PKEY_free(pkey); + pkey = NULL; + goto out; +} + + +static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, + const u8 *buf, size_t len) +{ + EC_KEY *eckey; + const EC_GROUP *group; + EVP_PKEY *pkey = NULL; + + if (len & 1) + return NULL; + + eckey = EVP_PKEY_get1_EC_KEY(group_key); + if (!eckey) { + wpa_printf(MSG_ERROR, + "DPP: Could not get EC_KEY from group_key"); + return NULL; + } + + group = EC_KEY_get0_group(eckey); + if (group) + pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2, + len / 2); + else + wpa_printf(MSG_ERROR, "DPP: Could not get EC group"); + + EC_KEY_free(eckey); + return pkey; +} + + +static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt) +{ + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); +} + + +struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, + size_t len) +{ + struct wpabuf *msg; + + msg = wpabuf_alloc(8 + len); + if (!msg) + return NULL; + wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be24(msg, OUI_WFA); + wpabuf_put_u8(msg, DPP_OUI_TYPE); + wpabuf_put_u8(msg, 1); /* Crypto Suite */ + wpabuf_put_u8(msg, type); + return msg; +} + + +const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len) +{ + u16 id, alen; + const u8 *pos = buf, *end = buf + len; + + while (end - pos >= 4) { + id = WPA_GET_LE16(pos); + pos += 2; + alen = WPA_GET_LE16(pos); + pos += 2; + if (alen > end - pos) + return NULL; + if (id == req_id) { + *ret_len = alen; + return pos; + } + pos += alen; + } + + return NULL; +} + + +int dpp_check_attrs(const u8 *buf, size_t len) +{ + const u8 *pos, *end; + int wrapped_data = 0; + + pos = buf; + end = buf + len; + while (end - pos >= 4) { + u16 id, alen; + + id = WPA_GET_LE16(pos); + pos += 2; + alen = WPA_GET_LE16(pos); + pos += 2; + wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u", + id, alen); + if (alen > end - pos) { + wpa_printf(MSG_DEBUG, + "DPP: Truncated message - not enough room for the attribute - dropped"); + return -1; + } + if (wrapped_data) { + wpa_printf(MSG_DEBUG, + "DPP: An unexpected attribute included after the Wrapped Data attribute"); + return -1; + } + if (id == DPP_ATTR_WRAPPED_DATA) + wrapped_data = 1; + pos += alen; + } + + if (end != pos) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected octets (%d) after the last attribute", + (int) (end - pos)); + return -1; + } + + return 0; +} + + +void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info) +{ + if (!info) + return; + os_free(info->uri); + os_free(info->info); + EVP_PKEY_free(info->pubkey); + os_free(info); +} + + +const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type) +{ + switch (type) { + case DPP_BOOTSTRAP_QR_CODE: + return "QRCODE"; + case DPP_BOOTSTRAP_PKEX: + return "PKEX"; + } + return "??"; +} + + +static int dpp_uri_valid_info(const char *info) +{ + while (*info) { + unsigned char val = *info++; + + if (val < 0x20 || val > 0x7e || val == 0x3b) + return 0; + } + + return 1; +} + + +static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri) +{ + bi->uri = os_strdup(uri); + return bi->uri ? 0 : -1; +} + + +int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, + const char *chan_list) +{ + const char *pos = chan_list; + int opclass, channel, freq; + + while (pos && *pos && *pos != ';') { + opclass = atoi(pos); + if (opclass <= 0) + goto fail; + pos = os_strchr(pos, '/'); + if (!pos) + goto fail; + pos++; + channel = atoi(pos); + if (channel <= 0) + goto fail; + while (*pos >= '0' && *pos <= '9') + pos++; + freq = ieee80211_chan_to_freq(NULL, opclass, channel); + wpa_printf(MSG_DEBUG, + "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d", + opclass, channel, freq); + if (freq < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)", + opclass, channel); + } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { + wpa_printf(MSG_DEBUG, + "DPP: Too many channels in URI channel-list - ignore list"); + bi->num_freq = 0; + break; + } else { + bi->freq[bi->num_freq++] = freq; + } + + if (*pos == ';' || *pos == '\0') + break; + if (*pos != ',') + goto fail; + pos++; + } + + return 0; +fail: + wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list"); + return -1; +} + + +int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac) +{ + if (!mac) + return 0; + + if (hwaddr_aton2(mac, bi->mac_addr) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac"); + return -1; + } + + wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr)); + + return 0; +} + + +int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info) +{ + const char *end; + + if (!info) + return 0; + + end = os_strchr(info, ';'); + if (!end) + end = info + os_strlen(info); + bi->info = os_malloc(end - info + 1); + if (!bi->info) + return -1; + os_memcpy(bi->info, info, end - info); + bi->info[end - info] = '\0'; + wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info); + if (!dpp_uri_valid_info(bi->info)) { + wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload"); + return -1; + } + + return 0; +} + + +static const struct dpp_curve_params * +dpp_get_curve_oid(const ASN1_OBJECT *poid) +{ + ASN1_OBJECT *oid; + int i; + + for (i = 0; dpp_curves[i].name; i++) { + oid = OBJ_txt2obj(dpp_curves[i].name, 0); + if (oid && OBJ_cmp(poid, oid) == 0) + return &dpp_curves[i]; + } + return NULL; +} + + +static const struct dpp_curve_params * dpp_get_curve_nid(int nid) +{ + int i, tmp; + + if (!nid) + return NULL; + for (i = 0; dpp_curves[i].name; i++) { + tmp = OBJ_txt2nid(dpp_curves[i].name); + if (tmp == nid) + return &dpp_curves[i]; + } + return NULL; +} + + +static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) +{ + const char *end; + u8 *data; + size_t data_len; + EVP_PKEY *pkey; + const unsigned char *p; + int res; + X509_PUBKEY *pub = NULL; + ASN1_OBJECT *ppkalg; + const unsigned char *pk; + int ppklen; + X509_ALGOR *pa; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20800000L) + ASN1_OBJECT *pa_oid; +#else + const ASN1_OBJECT *pa_oid; +#endif + const void *pval; + int ptype; + const ASN1_OBJECT *poid; + char buf[100]; + + end = os_strchr(info, ';'); + if (!end) + return -1; + + data = base64_decode((const unsigned char *) info, end - info, + &data_len); + if (!data) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid base64 encoding on URI public-key"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key", + data, data_len); + + if (sha256_vector(1, (const u8 **) &data, &data_len, + bi->pubkey_hash) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + os_free(data); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", + bi->pubkey_hash, SHA256_MAC_LEN); + + /* DER encoded ASN.1 SubjectPublicKeyInfo + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + * + * subjectPublicKey = compressed format public key per ANSI X9.63 + * algorithm = ecPublicKey (1.2.840.10045.2.1) + * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g., + * prime256v1 (1.2.840.10045.3.1.7) + */ + + p = data; + pkey = d2i_PUBKEY(NULL, &p, data_len); + os_free(data); + + if (!pkey) { + wpa_printf(MSG_DEBUG, + "DPP: Could not parse URI public-key SubjectPublicKeyInfo"); + return -1; + } + + if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { + wpa_printf(MSG_DEBUG, + "DPP: SubjectPublicKeyInfo does not describe an EC key"); + EVP_PKEY_free(pkey); + return -1; + } + + res = X509_PUBKEY_set(&pub, pkey); + if (res != 1) { + wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey"); + goto fail; + } + + res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub); + if (res != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo parameters"); + goto fail; + } + res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0); + if (res < 0 || (size_t) res >= sizeof(buf)) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo algorithm"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf); + if (os_strcmp(buf, "id-ecPublicKey") != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported SubjectPublicKeyInfo algorithm"); + goto fail; + } + + X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa); + if (ptype != V_ASN1_OBJECT) { + wpa_printf(MSG_DEBUG, + "DPP: SubjectPublicKeyInfo parameters did not contain an OID"); + goto fail; + } + poid = pval; + res = OBJ_obj2txt(buf, sizeof(buf), poid, 0); + if (res < 0 || (size_t) res >= sizeof(buf)) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo parameters OID"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf); + bi->curve = dpp_get_curve_oid(poid); + if (!bi->curve) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported SubjectPublicKeyInfo curve: %s", + buf); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen); + + X509_PUBKEY_free(pub); + bi->pubkey = pkey; + return 0; +fail: + X509_PUBKEY_free(pub); + EVP_PKEY_free(pkey); + return -1; +} + + +static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri) +{ + const char *pos = uri; + const char *end; + const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL; + struct dpp_bootstrap_info *bi; + + wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri)); + + if (os_strncmp(pos, "DPP:", 4) != 0) { + wpa_printf(MSG_INFO, "DPP: Not a DPP URI"); + return NULL; + } + pos += 4; + + for (;;) { + end = os_strchr(pos, ';'); + if (!end) + break; + + if (end == pos) { + /* Handle terminating ";;" and ignore unexpected ";" + * for parsing robustness. */ + pos++; + continue; + } + + if (pos[0] == 'C' && pos[1] == ':' && !chan_list) + chan_list = pos + 2; + else if (pos[0] == 'M' && pos[1] == ':' && !mac) + mac = pos + 2; + else if (pos[0] == 'I' && pos[1] == ':' && !info) + info = pos + 2; + else if (pos[0] == 'K' && pos[1] == ':' && !pk) + pk = pos + 2; + else + wpa_hexdump_ascii(MSG_DEBUG, + "DPP: Ignore unrecognized URI parameter", + pos, end - pos); + pos = end + 1; + } + + if (!pk) { + wpa_printf(MSG_INFO, "DPP: URI missing public-key"); + return NULL; + } + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return NULL; + + if (dpp_clone_uri(bi, uri) < 0 || + dpp_parse_uri_chan_list(bi, chan_list) < 0 || + dpp_parse_uri_mac(bi, mac) < 0 || + dpp_parse_uri_info(bi, info) < 0 || + dpp_parse_uri_pk(bi, pk) < 0) { + dpp_bootstrap_info_free(bi); + bi = NULL; + } + + return bi; +} + + +struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_parse_uri(uri); + if (bi) + bi->type = DPP_BOOTSTRAP_QR_CODE; + return bi; +} + + +static void dpp_debug_print_key(const char *title, EVP_PKEY *key) +{ + EC_KEY *eckey; + BIO *out; + size_t rlen; + char *txt; + int res; + unsigned char *der = NULL; + int der_len; + const EC_GROUP *group; + const EC_POINT *point; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + EVP_PKEY_print_private(out, key, 0, NULL); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (txt) { + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "%s: %s", title, txt); + } + os_free(txt); + } + BIO_free(out); + + eckey = EVP_PKEY_get1_EC_KEY(key); + if (!eckey) + return; + + group = EC_KEY_get0_group(eckey); + point = EC_KEY_get0_public_key(eckey); + if (group && point) + dpp_debug_print_point(title, group, point); + + der_len = i2d_ECPrivateKey(eckey, &der); + if (der_len > 0) + wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len); + OPENSSL_free(der); + if (der_len <= 0) { + der = NULL; + der_len = i2d_EC_PUBKEY(eckey, &der); + if (der_len > 0) + wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len); + OPENSSL_free(der); + } + + EC_KEY_free(eckey); +} + + +static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve) +{ + EVP_PKEY_CTX *kctx = NULL; + EC_KEY *ec_params; + EVP_PKEY *params = NULL, *key = NULL; + int nid; + + wpa_printf(MSG_DEBUG, "DPP: Generating a keypair"); + + nid = OBJ_txt2nid(curve->name); + if (nid == NID_undef) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name); + return NULL; + } + + ec_params = EC_KEY_new_by_curve_name(nid); + if (!ec_params) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate EC_KEY parameters"); + goto fail; + } + EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE); + params = EVP_PKEY_new(); + if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate EVP_PKEY parameters"); + goto fail; + } + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (!kctx || + EVP_PKEY_keygen_init(kctx) != 1 || + EVP_PKEY_keygen(kctx, &key) != 1) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key"); + goto fail; + } + + if (wpa_debug_show_keys) + dpp_debug_print_key("Own generated key", key); + + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(kctx); + return key; +fail: + EVP_PKEY_CTX_free(kctx); + EVP_PKEY_free(params); + return NULL; +} + + +static const struct dpp_curve_params * +dpp_get_curve_name(const char *name) +{ + int i; + + for (i = 0; dpp_curves[i].name; i++) { + if (os_strcmp(name, dpp_curves[i].name) == 0 || + (dpp_curves[i].jwk_crv && + os_strcmp(name, dpp_curves[i].jwk_crv) == 0)) + return &dpp_curves[i]; + } + return NULL; +} + + +static const struct dpp_curve_params * +dpp_get_curve_jwk_crv(const char *name) +{ + int i; + + for (i = 0; dpp_curves[i].name; i++) { + if (dpp_curves[i].jwk_crv && + os_strcmp(name, dpp_curves[i].jwk_crv) == 0) + return &dpp_curves[i]; + } + return NULL; +} + + +static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve, + const u8 *privkey, size_t privkey_len) +{ + EVP_PKEY *pkey; + EC_KEY *eckey; + const EC_GROUP *group; + int nid; + + pkey = EVP_PKEY_new(); + if (!pkey) + return NULL; + eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len); + if (!eckey) { + wpa_printf(MSG_INFO, + "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_PKEY_free(pkey); + return NULL; + } + group = EC_KEY_get0_group(eckey); + if (!group) { + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + nid = EC_GROUP_get_curve_name(group); + *curve = dpp_get_curve_nid(nid); + if (!*curve) { + wpa_printf(MSG_INFO, + "DPP: Unsupported curve (nid=%d) in pre-assigned key", + nid); + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + + if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) { + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + return pkey; +} + + +typedef struct { + /* AlgorithmIdentifier ecPublicKey with optional parameters present + * as an OID identifying the curve */ + X509_ALGOR *alg; + /* Compressed format public key per ANSI X9.63 */ + ASN1_BIT_STRING *pub_key; +} DPP_BOOTSTRAPPING_KEY; + +ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = { + ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR), + ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY); + +IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY); + + +static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key) +{ + unsigned char *der = NULL; + int der_len; + EC_KEY *eckey; + struct wpabuf *ret = NULL; + size_t len; + const EC_GROUP *group; + const EC_POINT *point; + BN_CTX *ctx; + DPP_BOOTSTRAPPING_KEY *bootstrap = NULL; + int nid; + + ctx = BN_CTX_new(); + eckey = EVP_PKEY_get1_EC_KEY(key); + if (!ctx || !eckey) + goto fail; + + group = EC_KEY_get0_group(eckey); + point = EC_KEY_get0_public_key(eckey); + if (!group || !point) + goto fail; + dpp_debug_print_point("DPP: bootstrap public key", group, point); + nid = EC_GROUP_get_curve_name(group); + + bootstrap = DPP_BOOTSTRAPPING_KEY_new(); + if (!bootstrap || + X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC), + V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1) + goto fail; + + len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, + NULL, 0, ctx); + if (len == 0) + goto fail; + + der = OPENSSL_malloc(len); + if (!der) + goto fail; + len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, + der, len, ctx); + + OPENSSL_free(bootstrap->pub_key->data); + bootstrap->pub_key->data = der; + der = NULL; + bootstrap->pub_key->length = len; + /* No unused bits */ + bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); + bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT; + + der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der); + if (der_len <= 0) { + wpa_printf(MSG_ERROR, + "DDP: Failed to build DER encoded public key"); + goto fail; + } + + ret = wpabuf_alloc_copy(der, der_len); +fail: + DPP_BOOTSTRAPPING_KEY_free(bootstrap); + OPENSSL_free(der); + EC_KEY_free(eckey); + BN_CTX_free(ctx); + return ret; +} + + +int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi) +{ + struct wpabuf *der; + int res; + const u8 *addr[1]; + size_t len[1]; + + der = dpp_bootstrap_key_der(bi->pubkey); + if (!der) + return -1; + wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", + der); + + addr[0] = wpabuf_head(der); + len[0] = wpabuf_len(der); + res = sha256_vector(1, addr, len, bi->pubkey_hash); + if (res < 0) + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + else + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, + SHA256_MAC_LEN); + wpabuf_free(der); + return res; +} + + +char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, + const u8 *privkey, size_t privkey_len) +{ + unsigned char *base64 = NULL; + char *pos, *end; + size_t len; + struct wpabuf *der = NULL; + const u8 *addr[1]; + int res; + + if (!curve) { + bi->curve = &dpp_curves[0]; + } else { + bi->curve = dpp_get_curve_name(curve); + if (!bi->curve) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", + curve); + return NULL; + } + } + if (privkey) + bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len); + else + bi->pubkey = dpp_gen_keypair(bi->curve); + if (!bi->pubkey) + goto fail; + bi->own = 1; + + der = dpp_bootstrap_key_der(bi->pubkey); + if (!der) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", + der); + + addr[0] = wpabuf_head(der); + len = wpabuf_len(der); + res = sha256_vector(1, addr, &len, bi->pubkey_hash); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, + SHA256_MAC_LEN); + + base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len); + wpabuf_free(der); + der = NULL; + if (!base64) + goto fail; + pos = (char *) base64; + end = pos + len; + for (;;) { + pos = os_strchr(pos, '\n'); + if (!pos) + break; + os_memmove(pos, pos + 1, end - pos); + } + return (char *) base64; +fail: + os_free(base64); + wpabuf_free(der); + return NULL; +} + + +static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "first intermediate key"; + int res; + + /* k1 = HKDF(<>, "first intermediate key", M.x) */ + + /* HKDF-Extract(<>, M.x) */ + os_memset(salt, 0, hash_len); + if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)", + k1, hash_len); + return 0; +} + + +static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "second intermediate key"; + int res; + + /* k2 = HKDF(<>, "second intermediate key", N.x) */ + + /* HKDF-Extract(<>, N.x) */ + os_memset(salt, 0, hash_len); + res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk); + if (res < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)", + k2, hash_len); + return 0; +} + + +static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke, + unsigned int hash_len) +{ + size_t nonce_len; + u8 nonces[2 * DPP_MAX_NONCE_LEN]; + const char *info_ke = "DPP Key"; + u8 prk[DPP_MAX_HASH_LEN]; + int res; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem = 0; + + if (!auth->Mx_len || !auth->Nx_len) { + wpa_printf(MSG_DEBUG, + "DPP: Mx/Nx not available - cannot derive ke"); + return -1; + } + + /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */ + + /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */ + nonce_len = auth->curve->nonce_len; + os_memcpy(nonces, auth->i_nonce, nonce_len); + os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len); + addr[num_elem] = auth->Mx; + len[num_elem] = auth->Mx_len; + num_elem++; + addr[num_elem] = auth->Nx; + len[num_elem] = auth->Nx_len; + num_elem++; + if (auth->peer_bi && auth->own_bi) { + if (!auth->Lx_len) { + wpa_printf(MSG_DEBUG, + "DPP: Lx not available - cannot derive ke"); + return -1; + } + addr[num_elem] = auth->Lx; + len[num_elem] = auth->secret_len; + num_elem++; + } + res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len, + num_elem, addr, len, prk); + if (res < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)", + ke, hash_len); + return 0; +} + + +static void dpp_build_attr_status(struct wpabuf *msg, + enum dpp_status_error status) +{ + wpa_printf(MSG_DEBUG, "DPP: Status %d", status); + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, status); +} + + +static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, + const u8 *hash) +{ + if (hash) { + wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash"); + wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, hash, SHA256_MAC_LEN); + } +} + + +static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg, + const u8 *hash) +{ + if (hash) { + wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash"); + wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, hash, SHA256_MAC_LEN); + } +} + + +static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, + const struct wpabuf *pi, + size_t nonce_len, + const u8 *r_pubkey_hash, + const u8 *i_pubkey_hash, + unsigned int neg_freq) +{ + struct wpabuf *msg; + u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1]; + u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE]; + u8 *pos; + const u8 *addr[2]; + size_t len[2], siv_len, attr_len; + u8 *attr_start, *attr_end; + + /* Build DPP Authentication Request frame attributes */ + attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) + + 4 + sizeof(wrapped_data); + if (neg_freq > 0) + attr_len += 4 + 2; +#ifdef CONFIG_DPP2 + attr_len += 5; +#endif /* CONFIG_DPP2 */ +#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_DPP2 + /* Protocol Version */ + wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, 2); +#endif /* CONFIG_DPP2 */ + +#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_DPP2 + attr_len += 5; +#endif /* CONFIG_DPP2 */ +#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); + } + +#ifdef CONFIG_DPP2 + /* Protocol Version */ + wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, 2); +#endif /* CONFIG_DPP2 */ + + attr_end = wpabuf_put(msg, 0); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */ + pos = clear; + + if (r_nonce) { + /* R-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, r_nonce, nonce_len); + pos += nonce_len; + } + + if (i_nonce) { + /* I-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, i_nonce, nonce_len); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch"); + pos[nonce_len / 2] ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + pos += nonce_len; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab"); + goto skip_r_capab; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* R-capabilities */ + WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES); + pos += 2; + WPA_PUT_LE16(pos, 1); + pos += 2; + auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR : + DPP_CAPAB_ENROLLEE; + *pos++ = auth->r_capab; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_ZERO_R_CAPAB) { + wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities"); + pos[-1] = 0; + } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - incompatible R-capabilities"); + if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) == + (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) + pos[-1] = 0; + else + pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE : + DPP_CAPAB_CONFIGURATOR; + } +skip_r_capab: +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wrapped_r_auth) { + /* {R-auth}ke */ + WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA); + pos += 2; + WPA_PUT_LE16(pos, wrapped_r_auth_len); + pos += 2; + os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len); + pos += wrapped_r_auth_len; + } + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data */ + addr[1] = attr_start; + len[1] = attr_end - attr_start; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + siv_len = pos - clear; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); + if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len, + 2, addr, len, wrapped_data) < 0) { + wpabuf_free(msg); + return NULL; + } + siv_len += AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, siv_len); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, siv_len); + wpabuf_put_data(msg, wrapped_data, siv_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Response frame attributes", msg); + return msg; +} + + +static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes, + u16 num_modes, unsigned int freq) +{ + u16 m; + int c, flag; + + if (!own_modes || !num_modes) + return 1; + + for (m = 0; m < num_modes; m++) { + for (c = 0; c < own_modes[m].num_channels; c++) { + if ((unsigned int) own_modes[m].channels[c].freq != + freq) + continue; + flag = own_modes[m].channels[c].flag; + if (!(flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR))) + return 1; + } + } + + wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq); + return 0; +} + + +static int freq_included(const unsigned int freqs[], unsigned int num, + unsigned int freq) +{ + while (num > 0) { + if (freqs[--num] == freq) + return 1; + } + return 0; +} + + +static void freq_to_start(unsigned int freqs[], unsigned int num, + unsigned int freq) +{ + unsigned int i; + + for (i = 0; i < num; i++) { + if (freqs[i] == freq) + break; + } + if (i == 0 || i >= num) + return; + os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0])); + freqs[0] = freq; +} + + +static int dpp_channel_intersect(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + struct dpp_bootstrap_info *peer_bi = auth->peer_bi; + unsigned int i, freq; + + for (i = 0; i < peer_bi->num_freq; i++) { + freq = peer_bi->freq[i]; + if (freq_included(auth->freq, auth->num_freq, freq)) + continue; + if (dpp_channel_ok_init(own_modes, num_modes, freq)) + auth->freq[auth->num_freq++] = freq; + } + if (!auth->num_freq) { + wpa_printf(MSG_INFO, + "DPP: No available channels for initiating DPP Authentication"); + return -1; + } + auth->curr_freq = auth->freq[0]; + return 0; +} + + +static int dpp_channel_local_list(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + u16 m; + int c, flag; + unsigned int freq; + + auth->num_freq = 0; + + if (!own_modes || !num_modes) { + auth->freq[0] = 2412; + auth->freq[1] = 2437; + auth->freq[2] = 2462; + auth->num_freq = 3; + return 0; + } + + for (m = 0; m < num_modes; m++) { + for (c = 0; c < own_modes[m].num_channels; c++) { + freq = own_modes[m].channels[c].freq; + flag = own_modes[m].channels[c].flag; + if (flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)) + continue; + if (freq_included(auth->freq, auth->num_freq, freq)) + continue; + auth->freq[auth->num_freq++] = freq; + if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { + m = num_modes; + break; + } + } + } + + return auth->num_freq == 0 ? -1 : 0; +} + + +static int dpp_prepare_channel_list(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + int res; + char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end; + unsigned int i; + + if (auth->peer_bi->num_freq > 0) + res = dpp_channel_intersect(auth, own_modes, num_modes); + else + res = dpp_channel_local_list(auth, own_modes, num_modes); + if (res < 0) + return res; + + /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most + * likely channels first. */ + freq_to_start(auth->freq, auth->num_freq, 2462); + freq_to_start(auth->freq, auth->num_freq, 2412); + freq_to_start(auth->freq, auth->num_freq, 2437); + + auth->freq_idx = 0; + auth->curr_freq = auth->freq[0]; + + pos = freqs; + end = pos + sizeof(freqs); + for (i = 0; i < auth->num_freq; i++) { + res = os_snprintf(pos, end - pos, " %u", auth->freq[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + *pos = '\0'; + wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s", + freqs); + + return 0; +} + + +static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth) +{ + struct dpp_bootstrap_info *bi; + char *pk = NULL; + size_t len; + + if (auth->own_bi) + return 0; /* already generated */ + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return -1; + bi->type = DPP_BOOTSTRAP_QR_CODE; + pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0); + if (!pk) + goto fail; + + len = 4; /* "DPP:" */ + len += 4 + os_strlen(pk); + bi->uri = os_malloc(len + 1); + if (!bi->uri) + goto fail; + os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk); + wpa_printf(MSG_DEBUG, + "DPP: Auto-generated own bootstrapping key info: URI %s", + bi->uri); + + auth->tmp_own_bi = auth->own_bi = bi; + + os_free(pk); + + return 0; +fail: + os_free(pk); + dpp_bootstrap_info_free(bi); + return -1; +} + + +struct dpp_authentication * dpp_auth_init(void *msg_ctx, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + u8 dpp_allowed_roles, + unsigned int neg_freq, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + struct dpp_authentication *auth; + size_t nonce_len; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + struct wpabuf *pi = NULL; + const u8 *r_pubkey_hash, *i_pubkey_hash; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + return NULL; + auth->msg_ctx = msg_ctx; + auth->initiator = 1; + auth->waiting_auth_resp = 1; + auth->allowed_roles = dpp_allowed_roles; + auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR); + auth->peer_bi = peer_bi; + auth->own_bi = own_bi; + auth->curve = peer_bi->curve; + + if (dpp_autogen_bootstrap_key(auth) < 0 || + dpp_prepare_channel_list(auth, own_modes, num_modes) < 0) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_nonce_override_len > 0) { + wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce"); + nonce_len = dpp_nonce_override_len; + os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len); + } else { + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->i_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate I-nonce"); + goto fail; + } + } +#else /* CONFIG_TESTING_OPTIONS */ + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->i_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce"); + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_protocol_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override protocol key"); + auth->own_protocol_key = dpp_set_keypair( + &tmp_curve, dpp_protocol_key_override, + dpp_protocol_key_override_len); + } else { + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + auth->own_protocol_key = dpp_gen_keypair(auth->curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!auth->own_protocol_key) + goto fail; + + pi = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (!pi) + goto fail; + + /* ECDH: M = pI * BR */ + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + auth->secret_len = secret_len; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", + auth->Mx, auth->secret_len); + auth->Mx_len = auth->secret_len; + + if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, + auth->curve->hash_len) < 0) + goto fail; + + r_pubkey_hash = auth->peer_bi->pubkey_hash; + i_pubkey_hash = auth->own_bi->pubkey_hash; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key"); + wpabuf_free(pi); + pi = NULL; + } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key"); + wpabuf_free(pi); + pi = wpabuf_alloc(2 * auth->curve->prime_len); + if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0) + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash, + i_pubkey_hash, neg_freq); + if (!auth->req_msg) + goto fail; + +out: + wpabuf_free(pi); + EVP_PKEY_CTX_free(ctx); + return auth; +fail: + dpp_auth_deinit(auth); + auth = NULL; + goto out; +} + + +static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth, + const char *json) +{ + size_t nonce_len; + size_t json_len, clear_len; + struct wpabuf *clear = NULL, *msg = NULL; + u8 *wrapped; + size_t attr_len; + + wpa_printf(MSG_DEBUG, "DPP: Build configuration request"); + + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->e_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len); + json_len = os_strlen(json); + wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len); + + /* { E-nonce, configAttrib }ke */ + clear_len = 4 + nonce_len + 4 + json_len; + clear = wpabuf_alloc(clear_len); + attr_len = 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = wpabuf_alloc(attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce"); + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len - 1); + wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len); + wpabuf_put_data(clear, auth->e_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_e_nonce: + if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib"); + goto skip_conf_attr_obj; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* configAttrib */ + wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ); + wpabuf_put_le16(clear, json_len); + wpabuf_put_data(clear, json, json_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_conf_attr_obj: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + /* No AES-SIV AD */ + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 0, NULL, NULL, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Configuration Request frame attributes", msg); + wpabuf_free(clear); + return msg; + +fail: + wpabuf_free(clear); + wpabuf_free(msg); + return NULL; +} + + +static void dpp_write_adv_proto(struct wpabuf *buf) +{ + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 8); /* Length */ + wpabuf_put_u8(buf, 0x7f); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, DPP_OUI_TYPE); + wpabuf_put_u8(buf, 0x01); +} + + +static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query) +{ + /* GAS Query */ + wpabuf_put_le16(buf, wpabuf_len(query)); + wpabuf_put_buf(buf, query); +} + + +struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, + const char *json) +{ + struct wpabuf *buf, *conf_req; + + conf_req = dpp_build_conf_req_attr(auth, json); + if (!conf_req) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration request data available"); + return NULL; + } + + buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); + if (!buf) { + wpabuf_free(conf_req); + return NULL; + } + + dpp_write_adv_proto(buf); + dpp_write_gas_query(buf, conf_req); + wpabuf_free(conf_req); + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf); + + return buf; +} + + +static void dpp_auth_success(struct dpp_authentication *auth) +{ + wpa_printf(MSG_DEBUG, + "DPP: Authentication success - clear temporary keys"); + os_memset(auth->Mx, 0, sizeof(auth->Mx)); + auth->Mx_len = 0; + os_memset(auth->Nx, 0, sizeof(auth->Nx)); + auth->Nx_len = 0; + os_memset(auth->Lx, 0, sizeof(auth->Lx)); + auth->Lx_len = 0; + os_memset(auth->k1, 0, sizeof(auth->k1)); + os_memset(auth->k2, 0, sizeof(auth->k2)); + + auth->auth_success = 1; +} + + +static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth) +{ + struct wpabuf *pix, *prx, *bix, *brx; + const u8 *addr[7]; + size_t len[7]; + size_t i, num_elem = 0; + size_t nonce_len; + u8 zero = 0; + int res = -1; + + /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + nonce_len = auth->curve->nonce_len; + + if (auth->initiator) { + pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + if (auth->own_bi) + bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + else + bix = NULL; + brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + } else { + pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (auth->peer_bi) + bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + else + bix = NULL; + brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + } + if (!pix || !prx || !brx) + goto fail; + + addr[num_elem] = auth->i_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = auth->r_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = wpabuf_head(pix); + len[num_elem] = wpabuf_len(pix) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(prx); + len[num_elem] = wpabuf_len(prx) / 2; + num_elem++; + + if (bix) { + addr[num_elem] = wpabuf_head(bix); + len[num_elem] = wpabuf_len(bix) / 2; + num_elem++; + } + + addr[num_elem] = wpabuf_head(brx); + len[num_elem] = wpabuf_len(brx) / 2; + num_elem++; + + addr[num_elem] = &zero; + len[num_elem] = 1; + num_elem++; + + wpa_printf(MSG_DEBUG, "DPP: R-auth hash components"); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); + res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth); + if (res == 0) + wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth, + auth->curve->hash_len); +fail: + wpabuf_free(pix); + wpabuf_free(prx); + wpabuf_free(bix); + wpabuf_free(brx); + return res; +} + + +static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth) +{ + struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL; + const u8 *addr[7]; + size_t len[7]; + size_t i, num_elem = 0; + size_t nonce_len; + u8 one = 1; + int res = -1; + + /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + nonce_len = auth->curve->nonce_len; + + if (auth->initiator) { + pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + if (auth->own_bi) + bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + else + bix = NULL; + if (!auth->peer_bi) + goto fail; + brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + } else { + pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (auth->peer_bi) + bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + else + bix = NULL; + if (!auth->own_bi) + goto fail; + brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + } + if (!pix || !prx || !brx) + goto fail; + + addr[num_elem] = auth->r_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = auth->i_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = wpabuf_head(prx); + len[num_elem] = wpabuf_len(prx) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(pix); + len[num_elem] = wpabuf_len(pix) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(brx); + len[num_elem] = wpabuf_len(brx) / 2; + num_elem++; + + if (bix) { + addr[num_elem] = wpabuf_head(bix); + len[num_elem] = wpabuf_len(bix) / 2; + num_elem++; + } + + addr[num_elem] = &one; + len[num_elem] = 1; + num_elem++; + + wpa_printf(MSG_DEBUG, "DPP: I-auth hash components"); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); + res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth); + if (res == 0) + wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth, + auth->curve->hash_len); +fail: + wpabuf_free(pix); + wpabuf_free(prx); + wpabuf_free(bix); + wpabuf_free(brx); + return res; +} + + +static int dpp_auth_derive_l_responder(struct dpp_authentication *auth) +{ + const EC_GROUP *group; + EC_POINT *l = NULL; + EC_KEY *BI = NULL, *bR = NULL, *pR = NULL; + const EC_POINT *BI_point; + BN_CTX *bnctx; + BIGNUM *lx, *sum, *q; + const BIGNUM *bR_bn, *pR_bn; + int ret = -1; + + /* L = ((bR + pR) modulo q) * BI */ + + bnctx = BN_CTX_new(); + sum = BN_new(); + q = BN_new(); + lx = BN_new(); + if (!bnctx || !sum || !q || !lx) + goto fail; + BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); + if (!BI) + goto fail; + BI_point = EC_KEY_get0_public_key(BI); + group = EC_KEY_get0_group(BI); + if (!group) + goto fail; + + bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); + pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); + if (!bR || !pR) + goto fail; + bR_bn = EC_KEY_get0_private_key(bR); + pR_bn = EC_KEY_get0_private_key(pR); + if (!bR_bn || !pR_bn) + goto fail; + if (EC_GROUP_get_order(group, q, bnctx) != 1 || + BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1) + goto fail; + l = EC_POINT_new(group); + if (!l || + EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, + bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); + auth->Lx_len = auth->secret_len; + ret = 0; +fail: + EC_POINT_clear_free(l); + EC_KEY_free(BI); + EC_KEY_free(bR); + EC_KEY_free(pR); + BN_clear_free(lx); + BN_clear_free(sum); + BN_free(q); + BN_CTX_free(bnctx); + return ret; +} + + +static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth) +{ + const EC_GROUP *group; + EC_POINT *l = NULL, *sum = NULL; + EC_KEY *bI = NULL, *BR = NULL, *PR = NULL; + const EC_POINT *BR_point, *PR_point; + BN_CTX *bnctx; + BIGNUM *lx; + const BIGNUM *bI_bn; + int ret = -1; + + /* L = bI * (BR + PR) */ + + bnctx = BN_CTX_new(); + lx = BN_new(); + if (!bnctx || !lx) + goto fail; + BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); + PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key); + if (!BR || !PR) + goto fail; + BR_point = EC_KEY_get0_public_key(BR); + PR_point = EC_KEY_get0_public_key(PR); + + bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); + if (!bI) + goto fail; + group = EC_KEY_get0_group(bI); + bI_bn = EC_KEY_get0_private_key(bI); + if (!group || !bI_bn) + goto fail; + sum = EC_POINT_new(group); + l = EC_POINT_new(group); + if (!sum || !l || + EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 || + EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, + bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); + auth->Lx_len = auth->secret_len; + ret = 0; +fail: + EC_POINT_clear_free(l); + EC_POINT_clear_free(sum); + EC_KEY_free(bI); + EC_KEY_free(BR); + EC_KEY_free(PR); + BN_clear_free(lx); + BN_CTX_free(bnctx); + return ret; +} + + +static int dpp_auth_build_resp_ok(struct dpp_authentication *auth) +{ + size_t nonce_len; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + struct wpabuf *msg, *pr = NULL; + u8 r_auth[4 + DPP_MAX_HASH_LEN]; + u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth; + size_t wrapped_r_auth_len; + int ret = -1; + const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce; + enum dpp_status_error status = DPP_STATUS_OK; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); + if (!auth->own_bi) + return -1; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_nonce_override_len > 0) { + wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce"); + nonce_len = dpp_nonce_override_len; + os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len); + } else { + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->r_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate R-nonce"); + goto fail; + } + } +#else /* CONFIG_TESTING_OPTIONS */ + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->r_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce"); + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_protocol_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override protocol key"); + auth->own_protocol_key = dpp_set_keypair( + &tmp_curve, dpp_protocol_key_override, + dpp_protocol_key_override_len); + } else { + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + auth->own_protocol_key = dpp_gen_keypair(auth->curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!auth->own_protocol_key) + goto fail; + + pr = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (!pr) + goto fail; + + /* ECDH: N = pR * PI */ + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + auth->Nx, auth->secret_len); + auth->Nx_len = auth->secret_len; + + if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, + auth->curve->hash_len) < 0) + goto fail; + + if (auth->own_bi && auth->peer_bi) { + /* Mutual authentication */ + if (dpp_auth_derive_l_responder(auth) < 0) + goto fail; + } + + if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) + goto fail; + + /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG); + WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len); + if (dpp_gen_r_auth(auth, r_auth + 4) < 0) + goto fail; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch"); + r_auth[4 + auth->curve->hash_len / 2] ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + r_auth, 4 + auth->curve->hash_len, + 0, NULL, NULL, wrapped_r_auth) < 0) + goto fail; + wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke", + wrapped_r_auth, wrapped_r_auth_len); + w_r_auth = wrapped_r_auth; + + r_pubkey_hash = auth->own_bi->pubkey_hash; + if (auth->peer_bi) + i_pubkey_hash = auth->peer_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + + i_nonce = auth->i_nonce; + r_nonce = auth->r_nonce; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key"); + wpabuf_free(pr); + pr = NULL; + } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key"); + wpabuf_free(pr); + pr = wpabuf_alloc(2 * auth->curve->prime_len); + if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0) + goto fail; + } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth"); + w_r_auth = NULL; + wrapped_r_auth_len = 0; + } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + status = 255; + } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce"); + r_nonce = NULL; + } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); + i_nonce = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + msg = dpp_auth_build_resp(auth, status, pr, nonce_len, + r_pubkey_hash, i_pubkey_hash, + r_nonce, i_nonce, + w_r_auth, wrapped_r_auth_len, + auth->k2); + if (!msg) + goto fail; + wpabuf_free(auth->resp_msg); + auth->resp_msg = msg; + ret = 0; +fail: + wpabuf_free(pr); + return ret; +} + + +static int dpp_auth_build_resp_status(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg; + const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!auth->own_bi) + return -1; + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); + + r_pubkey_hash = auth->own_bi->pubkey_hash; + if (auth->peer_bi) + i_pubkey_hash = auth->peer_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + + i_nonce = auth->i_nonce; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + status = 255; + } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); + i_nonce = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len, + r_pubkey_hash, i_pubkey_hash, + NULL, i_nonce, NULL, 0, auth->k1); + if (!msg) + return -1; + wpabuf_free(auth->resp_msg); + auth->resp_msg = msg; + return 0; +} + + +struct dpp_authentication * +dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + unsigned int freq, const u8 *hdr, const u8 *attr_start, + size_t attr_len) +{ + EVP_PKEY *pi = NULL; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap, + *channel; + u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len, + i_bootstrap_len, channel_len; + struct dpp_authentication *auth = NULL; +#ifdef CONFIG_DPP2 + const u8 *version; + u16 version_len; +#endif /* CONFIG_DPP2 */ + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Request"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data", + wrapped_data, wrapped_data_len); + attr_len = wrapped_data - 4 - attr_start; + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + goto fail; + auth->msg_ctx = msg_ctx; + auth->peer_bi = peer_bi; + auth->own_bi = own_bi; + auth->curve = own_bi->curve; + auth->curr_freq = freq; + + auth->peer_version = 1; /* default to the first version */ +#ifdef CONFIG_DPP2 + version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, + &version_len); + if (version) { + if (version_len < 1 || version[0] == 0) { + dpp_auth_fail(auth, + "Invalid Protocol Version attribute"); + goto fail; + } + auth->peer_version = version[0]; + wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", + auth->peer_version); + } +#endif /* CONFIG_DPP2 */ + + channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL, + &channel_len); + if (channel) { + int neg_freq; + + if (channel_len < 2) { + dpp_auth_fail(auth, "Too short Channel attribute"); + goto fail; + } + + neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]); + wpa_printf(MSG_DEBUG, + "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d", + channel[0], channel[1], neg_freq); + if (neg_freq < 0) { + dpp_auth_fail(auth, + "Unsupported Channel attribute value"); + goto fail; + } + + if (auth->curr_freq != (unsigned int) neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Changing negotiation channel from %u MHz to %u MHz", + freq, neg_freq); + auth->curr_freq = neg_freq; + } + } + + i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY, + &i_proto_len); + if (!i_proto) { + dpp_auth_fail(auth, + "Missing required Initiator Protocol Key attribute"); + goto fail; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key", + i_proto, i_proto_len); + + /* M = bR * PI */ + pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len); + if (!pi) { + dpp_auth_fail(auth, "Invalid Initiator Protocol Key"); + goto fail; + } + dpp_debug_print_key("Peer (Initiator) Protocol Key", pi); + + ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pi) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); + goto fail; + } + auth->secret_len = secret_len; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", + auth->Mx, auth->secret_len); + auth->Mx_len = auth->secret_len; + + if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, + auth->curve->hash_len) < 0) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + os_memcpy(auth->i_nonce, i_nonce, i_nonce_len); + + i_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_I_CAPABILITIES, + &i_capab_len); + if (!i_capab || i_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid I-capabilities"); + goto fail; + } + auth->i_capab = i_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab); + + bin_clear_free(unwrapped, unwrapped_len); + unwrapped = NULL; + + switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) { + case DPP_CAPAB_ENROLLEE: + if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Configurator role"); + goto not_compatible; + } + wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); + auth->configurator = 1; + break; + case DPP_CAPAB_CONFIGURATOR: + if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Enrollee role"); + goto not_compatible; + } + wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); + auth->configurator = 0; + break; + case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE: + if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) { + wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); + auth->configurator = 0; + } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) { + wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); + auth->configurator = 1; + } else { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Configurator/Enrollee role"); + goto not_compatible; + } + break; + default: + wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities"); + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x", + auth->i_capab & DPP_CAPAB_ROLE_MASK); + goto fail; + } + + auth->peer_protocol_key = pi; + pi = NULL; + if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) { + char hex[SHA256_MAC_LEN * 2 + 1]; + + wpa_printf(MSG_DEBUG, + "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time"); + if (dpp_auth_build_resp_status(auth, + DPP_STATUS_RESPONSE_PENDING) < 0) + goto fail; + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) { + auth->response_pending = 1; + os_memcpy(auth->waiting_pubkey_hash, + i_bootstrap, i_bootstrap_len); + wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap, + i_bootstrap_len); + } else { + hex[0] = '\0'; + } + + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE + "%s", hex); + return auth; + } + if (dpp_auth_build_resp_ok(auth) < 0) + goto fail; + + return auth; + +not_compatible: + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE + "i-capab=0x%02x", auth->i_capab); + if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) + auth->configurator = 1; + else + auth->configurator = 0; + auth->peer_protocol_key = pi; + pi = NULL; + if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0) + goto fail; + + auth->remove_on_tx_status = 1; + return auth; +fail: + bin_clear_free(unwrapped, unwrapped_len); + EVP_PKEY_free(pi); + EVP_PKEY_CTX_free(ctx); + dpp_auth_deinit(auth); + return NULL; +} + + +int dpp_notify_new_qr_code(struct dpp_authentication *auth, + struct dpp_bootstrap_info *peer_bi) +{ + if (!auth || !auth->response_pending || + os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) + return 0; + + wpa_printf(MSG_DEBUG, + "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with " + MACSTR, MAC2STR(auth->peer_mac_addr)); + auth->peer_bi = peer_bi; + + if (dpp_auth_build_resp_ok(auth) < 0) + return -1; + + return 1; +} + + +static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg; + u8 i_auth[4 + DPP_MAX_HASH_LEN]; + size_t i_auth_len; + u8 r_nonce[4 + DPP_MAX_NONCE_LEN]; + size_t r_nonce_len; + const u8 *addr[2]; + size_t len[2], attr_len; + u8 *wrapped_i_auth; + u8 *wrapped_r_nonce; + u8 *attr_start, *attr_end; + const u8 *r_pubkey_hash, *i_pubkey_hash; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation"); + + i_auth_len = 4 + auth->curve->hash_len; + r_nonce_len = 4 + auth->curve->nonce_len; + /* Build DPP Authentication Confirmation frame attributes */ + attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + + 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len); + if (!msg) + goto fail; + + attr_start = wpabuf_put(msg, 0); + + r_pubkey_hash = auth->peer_bi->pubkey_hash; + if (auth->own_bi) + i_pubkey_hash = auth->own_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + goto skip_status; + } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + dpp_build_attr_status(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); + + /* Initiator Bootstrapping Key Hash (mutual authentication) */ + dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF) + goto skip_wrapped_data; + if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) + i_auth_len = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + + attr_end = wpabuf_put(msg, 0); + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data */ + addr[1] = attr_start; + len[1] = attr_end - attr_start; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + if (status == DPP_STATUS_OK) { + /* I-auth wrapped with ke */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE); + wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) + goto skip_i_auth; +#endif /* CONFIG_TESTING_OPTIONS */ + + /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] + * 1) */ + WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG); + WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len); + if (dpp_gen_i_auth(auth, i_auth + 4) < 0) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch"); + i_auth[4 + auth->curve->hash_len / 2] ^= 0x01; + } +skip_i_auth: +#endif /* CONFIG_TESTING_OPTIONS */ + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + i_auth, i_auth_len, + 2, addr, len, wrapped_i_auth) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke", + wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE); + } else { + /* R-nonce wrapped with k2 */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE); + wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE); + + WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE); + WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len); + os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len); + + if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, + r_nonce, r_nonce_len, + 2, addr, len, wrapped_r_nonce) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2", + wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Confirmation frame attributes", + msg); + if (status == DPP_STATUS_OK) + dpp_auth_success(auth); + + return msg; + +fail: + wpabuf_free(msg); + return NULL; +} + + +static void +dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len, + const u8 *wrapped_data, u16 wrapped_data_len, + enum dpp_status_error status) +{ + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *i_nonce, *r_capab; + u16 i_nonce_len, r_capab_len; + + if (status == DPP_STATUS_NOT_COMPATIBLE) { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported incompatible roles"); + } else if (status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported more time needed"); + } else { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported failure (status %d)", + status); + dpp_auth_fail(auth, "Responder reported failure"); + return; + } + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { + dpp_auth_fail(auth, "I-nonce mismatch"); + goto fail; + } + + r_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_R_CAPABILITIES, + &r_capab_len); + if (!r_capab || r_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid R-capabilities"); + goto fail; + } + auth->r_capab = r_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); + if (status == DPP_STATUS_NOT_COMPATIBLE) { + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE + "r-capab=0x%02x", auth->r_capab); + } else if (status == DPP_STATUS_RESPONSE_PENDING) { + u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK; + + if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || + (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x", + role); + } else { + wpa_printf(MSG_DEBUG, + "DPP: Continue waiting for full DPP Authentication Response"); + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_RESPONSE_PENDING "%s", + auth->tmp_own_bi ? auth->tmp_own_bi->uri : ""); + } + } +fail: + bin_clear_free(unwrapped, unwrapped_len); +} + + +struct wpabuf * +dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + EVP_PKEY *pr; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL, *unwrapped2 = NULL; + size_t unwrapped_len = 0, unwrapped2_len = 0; + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto, + *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, + r_proto_len, r_nonce_len, i_nonce_len, r_capab_len, + wrapped2_len, r_auth_len; + u8 r_auth2[DPP_MAX_HASH_LEN]; + u8 role; +#ifdef CONFIG_DPP2 + const u8 *version; + u16 version_len; +#endif /* CONFIG_DPP2 */ + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Response"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!auth->initiator || !auth->peer_bi) { + dpp_auth_fail(auth, "Unexpected Authentication Response"); + return NULL; + } + + auth->waiting_auth_resp = 0; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + r_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Unexpected Responder Bootstrapping Key Hash value"); + wpa_hexdump(MSG_DEBUG, + "DPP: Expected Responder Bootstrapping Key Hash", + auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + return NULL; + } + + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap) { + if (i_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Invalid Initiator Bootstrapping Key Hash attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, + "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + if (!auth->own_bi || + os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Initiator Bootstrapping Key Hash attribute did not match"); + return NULL; + } + } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) { + /* PKEX bootstrapping mandates use of mutual authentication */ + dpp_auth_fail(auth, + "Missing Initiator Bootstrapping Key Hash attribute"); + return NULL; + } + + auth->peer_version = 1; /* default to the first version */ +#ifdef CONFIG_DPP2 + version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, + &version_len); + if (version) { + if (version_len < 1 || version[0] == 0) { + dpp_auth_fail(auth, + "Invalid Protocol Version attribute"); + return NULL; + } + auth->peer_version = version[0]; + wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", + auth->peer_version); + } +#endif /* CONFIG_DPP2 */ + + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + return NULL; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + auth->auth_resp_status = status[0]; + if (status[0] != DPP_STATUS_OK) { + dpp_auth_resp_rx_status(auth, hdr, attr_start, + attr_len, wrapped_data, + wrapped_data_len, status[0]); + return NULL; + } + + if (!i_bootstrap && auth->own_bi) { + wpa_printf(MSG_DEBUG, + "DPP: Responder decided not to use mutual authentication"); + auth->own_bi = NULL; + } + + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d", + auth->own_bi != NULL); + + r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY, + &r_proto_len); + if (!r_proto) { + dpp_auth_fail(auth, + "Missing required Responder Protocol Key attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key", + r_proto, r_proto_len); + + /* N = pI * PR */ + pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len); + if (!pr) { + dpp_auth_fail(auth, "Invalid Responder Protocol Key"); + return NULL; + } + dpp_debug_print_key("Peer (Responder) Protocol Key", pr); + + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pr) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); + goto fail; + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + auth->peer_protocol_key = pr; + pr = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + auth->Nx, auth->secret_len); + auth->Nx_len = auth->secret_len; + + if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, + auth->curve->hash_len) < 0) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, + &r_nonce_len); + if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len); + os_memcpy(auth->r_nonce, r_nonce, r_nonce_len); + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { + dpp_auth_fail(auth, "I-nonce mismatch"); + goto fail; + } + + if (auth->own_bi) { + /* Mutual authentication */ + if (dpp_auth_derive_l_initiator(auth) < 0) + goto fail; + } + + r_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_R_CAPABILITIES, + &r_capab_len); + if (!r_capab || r_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid R-capabilities"); + goto fail; + } + auth->r_capab = r_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); + role = auth->r_capab & DPP_CAPAB_ROLE_MASK; + if ((auth->allowed_roles == + (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) && + (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) { + /* Peer selected its role, so move from "either role" to the + * role that is compatible with peer's selection. */ + auth->configurator = role == DPP_CAPAB_ENROLLEE; + wpa_printf(MSG_DEBUG, "DPP: Acting as %s", + auth->configurator ? "Configurator" : "Enrollee"); + } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || + (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { + wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection"); + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Unexpected role in R-capabilities 0x%02x", + role); + if (role != DPP_CAPAB_ENROLLEE && + role != DPP_CAPAB_CONFIGURATOR) + goto fail; + bin_clear_free(unwrapped, unwrapped_len); + auth->remove_on_tx_status = 1; + return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE); + } + + wrapped2 = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_WRAPPED_DATA, &wrapped2_len); + if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid Secondary Wrapped Data"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped2, wrapped2_len); + + if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) + goto fail; + + unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE; + unwrapped2 = os_malloc(unwrapped2_len); + if (!unwrapped2) + goto fail; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped2, wrapped2_len, + 0, NULL, NULL, unwrapped2) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped2, unwrapped2_len); + + if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) { + dpp_auth_fail(auth, + "Invalid attribute in secondary unwrapped data"); + goto fail; + } + + r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG, + &r_auth_len); + if (!r_auth || r_auth_len != auth->curve->hash_len) { + dpp_auth_fail(auth, + "Missing or invalid Responder Authenticating Tag"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag", + r_auth, r_auth_len); + /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + if (dpp_gen_r_auth(auth, r_auth2) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag", + r_auth2, r_auth_len); + if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) { + dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag"); + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + auth->remove_on_tx_status = 1; + return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE); + } + + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - Authentication Response in place of Confirm"); + if (dpp_auth_build_resp_ok(auth) < 0) + return NULL; + return wpabuf_dup(auth->resp_msg); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + return dpp_auth_build_conf(auth, DPP_STATUS_OK); + +fail: + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + EVP_PKEY_free(pr); + EVP_PKEY_CTX_free(ctx); + return NULL; +} + + +static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len, + const u8 *wrapped_data, + u16 wrapped_data_len, + enum dpp_status_error status) +{ + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *r_nonce; + u16 r_nonce_len; + + /* Authentication Confirm failure cases are expected to include + * {R-nonce}k2 in the Wrapped Data attribute. */ + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) { + dpp_auth_fail(auth, "Authentication failed"); + goto fail; + } + if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, + &r_nonce_len); + if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); + goto fail; + } + if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) { + wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce", + r_nonce, r_nonce_len); + wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce", + auth->r_nonce, r_nonce_len); + dpp_auth_fail(auth, "R-nonce mismatch"); + goto fail; + } + + if (status == DPP_STATUS_NOT_COMPATIBLE) + dpp_auth_fail(auth, "Peer reported incompatible R-capab role"); + else if (status == DPP_STATUS_AUTH_FAILURE) + dpp_auth_fail(auth, "Peer reported authentication failure)"); + +fail: + bin_clear_free(unwrapped, unwrapped_len); + return -1; +} + + +int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, + i_auth_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + u8 i_auth2[DPP_MAX_HASH_LEN]; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Confirm"); + return -1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (auth->initiator || !auth->own_bi) { + dpp_auth_fail(auth, "Unexpected Authentication Confirm"); + return -1; + } + + auth->waiting_auth_conf = 0; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + r_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, + "DPP: Expected Responder Bootstrapping Key Hash", + auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + dpp_auth_fail(auth, + "Responder Bootstrapping Key Hash mismatch"); + return -1; + } + + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap) { + if (i_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Invalid Initiator Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, + "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + if (!auth->peer_bi || + os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Initiator Bootstrapping Key Hash mismatch"); + return -1; + } + } else if (auth->peer_bi) { + /* Mutual authentication and peer did not include its + * Bootstrapping Key Hash attribute. */ + dpp_auth_fail(auth, + "Missing Initiator Bootstrapping Key Hash attribute"); + return -1; + } + + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + if (status[0] == DPP_STATUS_NOT_COMPATIBLE || + status[0] == DPP_STATUS_AUTH_FAILURE) + return dpp_auth_conf_rx_failure(auth, hdr, attr_start, + attr_len, wrapped_data, + wrapped_data_len, status[0]); + + if (status[0] != DPP_STATUS_OK) { + dpp_auth_fail(auth, "Authentication failed"); + return -1; + } + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return -1; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, + &i_auth_len); + if (!i_auth || i_auth_len != auth->curve->hash_len) { + dpp_auth_fail(auth, + "Missing or invalid Initiator Authenticating Tag"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag", + i_auth, i_auth_len); + /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + if (dpp_gen_i_auth(auth, i_auth2) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag", + i_auth2, i_auth_len); + if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) { + dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag"); + goto fail; + } + + bin_clear_free(unwrapped, unwrapped_len); + dpp_auth_success(auth); + return 0; +fail: + bin_clear_free(unwrapped, unwrapped_len); + return -1; +} + + +static int bin_str_eq(const char *val, size_t len, const char *cmp) +{ + return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0; +} + + +struct dpp_configuration * dpp_configuration_alloc(const char *type) +{ + struct dpp_configuration *conf; + const char *end; + size_t len; + + conf = os_zalloc(sizeof(*conf)); + if (!conf) + goto fail; + + end = os_strchr(type, ' '); + if (end) + len = end - type; + else + len = os_strlen(type); + + if (bin_str_eq(type, len, "psk")) + conf->akm = DPP_AKM_PSK; + else if (bin_str_eq(type, len, "sae")) + conf->akm = DPP_AKM_SAE; + else if (bin_str_eq(type, len, "psk-sae") || + bin_str_eq(type, len, "psk+sae")) + conf->akm = DPP_AKM_PSK_SAE; + else if (bin_str_eq(type, len, "sae-dpp") || + bin_str_eq(type, len, "dpp+sae")) + conf->akm = DPP_AKM_SAE_DPP; + else if (bin_str_eq(type, len, "psk-sae-dpp") || + bin_str_eq(type, len, "dpp+psk+sae")) + conf->akm = DPP_AKM_PSK_SAE_DPP; + else if (bin_str_eq(type, len, "dpp")) + conf->akm = DPP_AKM_DPP; + else + goto fail; + + return conf; +fail: + dpp_configuration_free(conf); + return NULL; +} + + +int dpp_akm_psk(enum dpp_akm akm) +{ + return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_sae(enum dpp_akm akm) +{ + return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_legacy(enum dpp_akm akm) +{ + return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_SAE; +} + + +int dpp_akm_dpp(enum dpp_akm akm) +{ + return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP || + akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_ver2(enum dpp_akm akm) +{ + return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_configuration_valid(const struct dpp_configuration *conf) +{ + if (conf->ssid_len == 0) + return 0; + if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set) + return 0; + if (dpp_akm_sae(conf->akm) && !conf->passphrase) + return 0; + return 1; +} + + +void dpp_configuration_free(struct dpp_configuration *conf) +{ + if (!conf) + return; + str_clear_free(conf->passphrase); + os_free(conf->group_id); + bin_clear_free(conf, sizeof(*conf)); +} + + +static int dpp_configuration_parse(struct dpp_authentication *auth, + const char *cmd) +{ + const char *pos, *end; + struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; + struct dpp_configuration *conf = NULL; + + pos = os_strstr(cmd, " conf=sta-"); + if (pos) { + conf_sta = dpp_configuration_alloc(pos + 10); + if (!conf_sta) + goto fail; + conf = conf_sta; + } + + pos = os_strstr(cmd, " conf=ap-"); + if (pos) { + conf_ap = dpp_configuration_alloc(pos + 9); + if (!conf_ap) + goto fail; + conf = conf_ap; + } + + if (!conf) + return 0; + + pos = os_strstr(cmd, " ssid="); + if (pos) { + pos += 6; + end = os_strchr(pos, ' '); + conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); + conf->ssid_len /= 2; + if (conf->ssid_len > sizeof(conf->ssid) || + hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0) + goto fail; + } else { +#ifdef CONFIG_TESTING_OPTIONS + /* use a default SSID for legacy testing reasons */ + os_memcpy(conf->ssid, "test", 4); + conf->ssid_len = 4; +#else /* CONFIG_TESTING_OPTIONS */ + goto fail; +#endif /* CONFIG_TESTING_OPTIONS */ + } + + pos = os_strstr(cmd, " pass="); + if (pos) { + size_t pass_len; + + pos += 6; + end = os_strchr(pos, ' '); + pass_len = end ? (size_t) (end - pos) : os_strlen(pos); + pass_len /= 2; + if (pass_len > 63 || pass_len < 8) + goto fail; + conf->passphrase = os_zalloc(pass_len + 1); + if (!conf->passphrase || + hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0) + goto fail; + } + + pos = os_strstr(cmd, " psk="); + if (pos) { + pos += 5; + if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0) + goto fail; + conf->psk_set = 1; + } + + pos = os_strstr(cmd, " group_id="); + if (pos) { + size_t group_id_len; + + pos += 10; + end = os_strchr(pos, ' '); + group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); + conf->group_id = os_malloc(group_id_len + 1); + if (!conf->group_id) + goto fail; + os_memcpy(conf->group_id, pos, group_id_len); + conf->group_id[group_id_len] = '\0'; + } + + pos = os_strstr(cmd, " expiry="); + if (pos) { + long int val; + + pos += 8; + val = strtol(pos, NULL, 0); + if (val <= 0) + goto fail; + conf->netaccesskey_expiry = val; + } + + if (!dpp_configuration_valid(conf)) + goto fail; + + auth->conf_sta = conf_sta; + auth->conf_ap = conf_ap; + return 0; + +fail: + dpp_configuration_free(conf_sta); + dpp_configuration_free(conf_ap); + return -1; +} + + +static struct dpp_configurator * +dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_configurator *conf; + + if (!dpp) + return NULL; + + dl_list_for_each(conf, &dpp->configurator, + struct dpp_configurator, list) { + if (conf->id == id) + return conf; + } + return NULL; +} + + +int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, + struct dpp_authentication *auth, + const char *cmd) +{ + const char *pos; + + if (!cmd) + return 0; + + wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); + + pos = os_strstr(cmd, " configurator="); + if (pos) { + pos += 14; + auth->conf = dpp_configurator_get_id(dpp, atoi(pos)); + if (!auth->conf) { + wpa_printf(MSG_INFO, + "DPP: Could not find the specified configurator"); + return -1; + } + } + + if (dpp_configuration_parse(auth, cmd) < 0) { + wpa_msg(msg_ctx, MSG_INFO, + "DPP: Failed to set configurator parameters"); + return -1; + } + return 0; +} + + +void dpp_auth_deinit(struct dpp_authentication *auth) +{ + if (!auth) + return; + dpp_configuration_free(auth->conf_ap); + dpp_configuration_free(auth->conf_sta); + EVP_PKEY_free(auth->own_protocol_key); + EVP_PKEY_free(auth->peer_protocol_key); + wpabuf_free(auth->req_msg); + wpabuf_free(auth->resp_msg); + wpabuf_free(auth->conf_req); + os_free(auth->connector); + wpabuf_free(auth->net_access_key); + wpabuf_free(auth->c_sign_key); + dpp_bootstrap_info_free(auth->tmp_own_bi); +#ifdef CONFIG_TESTING_OPTIONS + os_free(auth->config_obj_override); + os_free(auth->discovery_override); + os_free(auth->groups_override); +#endif /* CONFIG_TESTING_OPTIONS */ + bin_clear_free(auth, sizeof(*auth)); +} + + +static struct wpabuf * +dpp_build_conf_start(struct dpp_authentication *auth, + struct dpp_configuration *conf, size_t tailroom) +{ + struct wpabuf *buf; + char ssid[6 * sizeof(conf->ssid) + 1]; + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->discovery_override) + tailroom += os_strlen(auth->discovery_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + buf = wpabuf_alloc(200 + tailroom); + if (!buf) + return NULL; + wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":"); +#ifdef CONFIG_TESTING_OPTIONS + if (auth->discovery_override) { + wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'", + auth->discovery_override); + wpabuf_put_str(buf, auth->discovery_override); + wpabuf_put_u8(buf, ','); + return buf; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpabuf_put_str(buf, "{\"ssid\":\""); + json_escape_string(ssid, sizeof(ssid), + (const char *) conf->ssid, conf->ssid_len); + wpabuf_put_str(buf, ssid); + wpabuf_put_str(buf, "\"},"); + + return buf; +} + + +static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key, + const char *kid, const struct dpp_curve_params *curve) +{ + struct wpabuf *pub; + const u8 *pos; + char *x = NULL, *y = NULL; + int ret = -1; + + pub = dpp_get_pubkey_point(key, 0); + if (!pub) + goto fail; + pos = wpabuf_head(pub); + x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); + pos += curve->prime_len; + y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); + if (!x || !y) + goto fail; + + wpabuf_put_str(buf, "\""); + wpabuf_put_str(buf, name); + wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\""); + wpabuf_put_str(buf, curve->jwk_crv); + wpabuf_put_str(buf, "\",\"x\":\""); + wpabuf_put_str(buf, x); + wpabuf_put_str(buf, "\",\"y\":\""); + wpabuf_put_str(buf, y); + if (kid) { + wpabuf_put_str(buf, "\",\"kid\":\""); + wpabuf_put_str(buf, kid); + } + wpabuf_put_str(buf, "\"}"); + ret = 0; +fail: + wpabuf_free(pub); + os_free(x); + os_free(y); + return ret; +} + + +static void dpp_build_legacy_cred_params(struct wpabuf *buf, + struct dpp_configuration *conf) +{ + if (conf->passphrase && os_strlen(conf->passphrase) < 64) { + char pass[63 * 6 + 1]; + + json_escape_string(pass, sizeof(pass), conf->passphrase, + os_strlen(conf->passphrase)); + wpabuf_put_str(buf, "\"pass\":\""); + wpabuf_put_str(buf, pass); + wpabuf_put_str(buf, "\""); + os_memset(pass, 0, sizeof(pass)); + } else if (conf->psk_set) { + char psk[2 * sizeof(conf->psk) + 1]; + + wpa_snprintf_hex(psk, sizeof(psk), + conf->psk, sizeof(conf->psk)); + wpabuf_put_str(buf, "\"psk_hex\":\""); + wpabuf_put_str(buf, psk); + wpabuf_put_str(buf, "\""); + os_memset(psk, 0, sizeof(psk)); + } +} + + +static struct wpabuf * +dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, + struct dpp_configuration *conf) +{ + struct wpabuf *buf = NULL; + char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL; + size_t tailroom; + const struct dpp_curve_params *curve; + char jws_prot_hdr[100]; + size_t signed1_len, signed2_len, signed3_len; + struct wpabuf *dppcon = NULL; + unsigned char *signature = NULL; + const unsigned char *p; + size_t signature_len; + EVP_MD_CTX *md_ctx = NULL; + ECDSA_SIG *sig = NULL; + char *dot = "."; + const EVP_MD *sign_md; + const BIGNUM *r, *s; + size_t extra_len = 1000; + int incl_legacy; + enum dpp_akm akm; + + if (!auth->conf) { + wpa_printf(MSG_INFO, + "DPP: No configurator specified - cannot generate DPP config object"); + goto fail; + } + curve = auth->conf->curve; + if (curve->hash_len == SHA256_MAC_LEN) { + sign_md = EVP_sha256(); + } else if (curve->hash_len == SHA384_MAC_LEN) { + sign_md = EVP_sha384(); + } else if (curve->hash_len == SHA512_MAC_LEN) { + sign_md = EVP_sha512(); + } else { + wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm"); + goto fail; + } + + akm = conf->akm; + if (dpp_akm_ver2(akm) && auth->peer_version < 2) { + wpa_printf(MSG_DEBUG, + "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2"); + akm = DPP_AKM_DPP; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->groups_override) + extra_len += os_strlen(auth->groups_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + if (conf->group_id) + extra_len += os_strlen(conf->group_id); + + /* Connector (JSON dppCon object) */ + dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3); + if (!dppcon) + goto fail; +#ifdef CONFIG_TESTING_OPTIONS + if (auth->groups_override) { + wpabuf_put_u8(dppcon, '{'); + if (auth->groups_override) { + wpa_printf(MSG_DEBUG, + "DPP: TESTING - groups override: '%s'", + auth->groups_override); + wpabuf_put_str(dppcon, "\"groups\":"); + wpabuf_put_str(dppcon, auth->groups_override); + wpabuf_put_u8(dppcon, ','); + } + goto skip_groups; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",", + conf->group_id ? conf->group_id : "*"); + wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta"); +#ifdef CONFIG_TESTING_OPTIONS +skip_groups: +#endif /* CONFIG_TESTING_OPTIONS */ + if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL, + auth->curve) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK"); + goto fail; + } + if (conf->netaccesskey_expiry) { + struct os_tm tm; + + if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to generate expiry string"); + goto fail; + } + wpabuf_printf(dppcon, + ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"", + tm.year, tm.month, tm.day, + tm.hour, tm.min, tm.sec); + } + wpabuf_put_u8(dppcon, '}'); + wpa_printf(MSG_DEBUG, "DPP: dppCon: %s", + (const char *) wpabuf_head(dppcon)); + + os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr), + "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}", + auth->conf->kid, curve->jws_alg); + signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr, + os_strlen(jws_prot_hdr), + &signed1_len, 0); + signed2 = (char *) base64_url_encode(wpabuf_head(dppcon), + wpabuf_len(dppcon), + &signed2_len, 0); + if (!signed1 || !signed2) + goto fail; + + md_ctx = EVP_MD_CTX_create(); + if (!md_ctx) + goto fail; + + ERR_clear_error(); + if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, + auth->conf->csign) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 || + EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 || + EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + signature = os_malloc(signature_len); + if (!signature) + goto fail; + if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)", + signature, signature_len); + /* Convert to raw coordinates r,s */ + p = signature; + sig = d2i_ECDSA_SIG(NULL, &p, signature_len); + if (!sig) + goto fail; + ECDSA_SIG_get0(sig, &r, &s); + if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 || + dpp_bn2bin_pad(s, signature + curve->prime_len, + curve->prime_len) < 0) + goto fail; + signature_len = 2 * curve->prime_len; + wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)", + signature, signature_len); + signed3 = (char *) base64_url_encode(signature, signature_len, + &signed3_len, 0); + if (!signed3) + goto fail; + + incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm); + tailroom = 1000; + tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); + tailroom += signed1_len + signed2_len + signed3_len; + if (incl_legacy) + tailroom += 1000; + buf = dpp_build_conf_start(auth, conf, tailroom); + if (!buf) + goto fail; + + wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm)); + if (incl_legacy) { + dpp_build_legacy_cred_params(buf, conf); + wpabuf_put_str(buf, ","); + } + wpabuf_put_str(buf, "\"signedConnector\":\""); + wpabuf_put_str(buf, signed1); + wpabuf_put_u8(buf, '.'); + wpabuf_put_str(buf, signed2); + wpabuf_put_u8(buf, '.'); + wpabuf_put_str(buf, signed3); + wpabuf_put_str(buf, "\","); + if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid, + curve) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK"); + goto fail; + } + + wpabuf_put_str(buf, "}}"); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object", + wpabuf_head(buf), wpabuf_len(buf)); + +out: + EVP_MD_CTX_destroy(md_ctx); + ECDSA_SIG_free(sig); + os_free(signed1); + os_free(signed2); + os_free(signed3); + os_free(signature); + wpabuf_free(dppcon); + return buf; +fail: + wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object"); + wpabuf_free(buf); + buf = NULL; + goto out; +} + + +static struct wpabuf * +dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap, + struct dpp_configuration *conf) +{ + struct wpabuf *buf; + + buf = dpp_build_conf_start(auth, conf, 1000); + if (!buf) + return NULL; + + wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm)); + dpp_build_legacy_cred_params(buf, conf); + wpabuf_put_str(buf, "}}"); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", + wpabuf_head(buf), wpabuf_len(buf)); + + return buf; +} + + +static struct wpabuf * +dpp_build_conf_obj(struct dpp_authentication *auth, int ap) +{ + struct dpp_configuration *conf; + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->config_obj_override) { + wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override"); + return wpabuf_alloc_copy(auth->config_obj_override, + os_strlen(auth->config_obj_override)); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + conf = ap ? auth->conf_ap : auth->conf_sta; + if (!conf) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration available for Enrollee(%s) - reject configuration request", + ap ? "ap" : "sta"); + return NULL; + } + + if (dpp_akm_dpp(conf->akm)) + return dpp_build_conf_obj_dpp(auth, ap, conf); + return dpp_build_conf_obj_legacy(auth, ap, conf); +} + + +static struct wpabuf * +dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, + u16 e_nonce_len, int ap) +{ + struct wpabuf *conf; + size_t clear_len, attr_len; + struct wpabuf *clear = NULL, *msg = NULL; + u8 *wrapped; + const u8 *addr[1]; + size_t len[1]; + enum dpp_status_error status; + + conf = dpp_build_conf_obj(auth, ap); + if (conf) { + wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", + wpabuf_head(conf), wpabuf_len(conf)); + } + status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE; + auth->conf_resp_status = status; + + /* { E-nonce, configurationObject}ke */ + clear_len = 4 + e_nonce_len; + if (conf) + clear_len += 4 + wpabuf_len(conf); + clear = wpabuf_alloc(clear_len); + attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = wpabuf_alloc(attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch"); + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, e_nonce_len); + wpabuf_put_data(clear, e_nonce, e_nonce_len - 1); + wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, e_nonce_len); + wpabuf_put_data(clear, e_nonce, e_nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_e_nonce: + if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - Config Object"); + goto skip_config_obj; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (conf) { + wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); + wpabuf_put_le16(clear, wpabuf_len(conf)); + wpabuf_put_buf(clear, conf); + } + +#ifdef CONFIG_TESTING_OPTIONS +skip_config_obj: + if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - Status"); + goto skip_status; + } + if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 255; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + dpp_build_attr_status(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: +#endif /* CONFIG_TESTING_OPTIONS */ + + addr[0] = wpabuf_head(msg); + len[0] = wpabuf_len(msg); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 1, addr, len, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Configuration Response attributes", msg); +out: + wpabuf_free(conf); + wpabuf_free(clear); + + return msg; +fail: + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +struct wpabuf * +dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len) +{ + const u8 *wrapped_data, *e_nonce, *config_attr; + u16 wrapped_data_len, e_nonce_len, config_attr_len; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + struct wpabuf *resp = NULL; + struct json_token *root = NULL, *token; + int ap; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Config Request"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (dpp_check_attrs(attr_start, attr_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in config request"); + return NULL; + } + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return NULL; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 0, NULL, NULL, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + os_memcpy(auth->e_nonce, e_nonce, e_nonce_len); + + config_attr = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_CONFIG_ATTR_OBJ, + &config_attr_len); + if (!config_attr) { + dpp_auth_fail(auth, + "Missing or invalid Config Attributes attribute"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes", + config_attr, config_attr_len); + + root = json_parse((const char *) config_attr, config_attr_len); + if (!root) { + dpp_auth_fail(auth, "Could not parse Config Attributes"); + goto fail; + } + + token = json_get_member(root, "name"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - name"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string); + + token = json_get_member(root, "wi-fi_tech"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string); + if (os_strcmp(token->string, "infra") != 0) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'", + token->string); + dpp_auth_fail(auth, "Unsupported wi-fi_tech"); + goto fail; + } + + token = json_get_member(root, "netRole"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - netRole"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string); + if (os_strcmp(token->string, "sta") == 0) { + ap = 0; + } else if (os_strcmp(token->string, "ap") == 0) { + ap = 1; + } else { + wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'", + token->string); + dpp_auth_fail(auth, "Unsupported netRole"); + goto fail; + } + + resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap); + +fail: + json_free(root); + os_free(unwrapped); + return resp; +} + + +static struct wpabuf * +dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve, + const u8 *prot_hdr, u16 prot_hdr_len, + const EVP_MD **ret_md) +{ + struct json_token *root, *token; + struct wpabuf *kid = NULL; + + root = json_parse((const char *) prot_hdr, prot_hdr_len); + if (!root) { + wpa_printf(MSG_DEBUG, + "DPP: JSON parsing failed for JWS Protected Header"); + goto fail; + } + + if (root->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, + "DPP: JWS Protected Header root is not an object"); + goto fail; + } + + token = json_get_member(root, "typ"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No typ string value found"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s", + token->string); + if (os_strcmp(token->string, "dppCon") != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported JWS Protected Header typ=%s", + token->string); + goto fail; + } + + token = json_get_member(root, "alg"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No alg string value found"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s", + token->string); + if (os_strcmp(token->string, curve->jws_alg) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)", + token->string, curve->jws_alg); + goto fail; + } + if (os_strcmp(token->string, "ES256") == 0 || + os_strcmp(token->string, "BS256") == 0) + *ret_md = EVP_sha256(); + else if (os_strcmp(token->string, "ES384") == 0 || + os_strcmp(token->string, "BS384") == 0) + *ret_md = EVP_sha384(); + else if (os_strcmp(token->string, "ES512") == 0 || + os_strcmp(token->string, "BS512") == 0) + *ret_md = EVP_sha512(); + else + *ret_md = NULL; + if (!*ret_md) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported JWS Protected Header alg=%s", + token->string); + goto fail; + } + + kid = json_get_member_base64url(root, "kid"); + if (!kid) { + wpa_printf(MSG_DEBUG, "DPP: No kid string value found"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)", + kid); + +fail: + json_free(root); + return kid; +} + + +static int dpp_parse_cred_legacy(struct dpp_authentication *auth, + struct json_token *cred) +{ + struct json_token *pass, *psk_hex; + + wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential"); + + pass = json_get_member(cred, "pass"); + psk_hex = json_get_member(cred, "psk_hex"); + + if (pass && pass->type == JSON_STRING) { + size_t len = os_strlen(pass->string); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase", + pass->string, len); + if (len < 8 || len > 63) + return -1; + os_strlcpy(auth->passphrase, pass->string, + sizeof(auth->passphrase)); + } else if (psk_hex && psk_hex->type == JSON_STRING) { + if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected psk_hex with akm=sae"); + return -1; + } + if (os_strlen(psk_hex->string) != PMK_LEN * 2 || + hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK", + auth->psk, PMK_LEN); + auth->psk_set = 1; + } else { + wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found"); + return -1; + } + + if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) { + wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); + return -1; + } + + return 0; +} + + +static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk, + const struct dpp_curve_params **key_curve) +{ + struct json_token *token; + const struct dpp_curve_params *curve; + struct wpabuf *x = NULL, *y = NULL; + EC_GROUP *group; + EVP_PKEY *pkey = NULL; + + token = json_get_member(jwk, "kty"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No kty in JWK"); + goto fail; + } + if (os_strcmp(token->string, "EC") != 0) { + wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'", + token->string); + goto fail; + } + + token = json_get_member(jwk, "crv"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No crv in JWK"); + goto fail; + } + curve = dpp_get_curve_jwk_crv(token->string); + if (!curve) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'", + token->string); + goto fail; + } + + x = json_get_member_base64url(jwk, "x"); + if (!x) { + wpa_printf(MSG_DEBUG, "DPP: No x in JWK"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x); + if (wpabuf_len(x) != curve->prime_len) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWK x length %u (expected %u for curve %s)", + (unsigned int) wpabuf_len(x), + (unsigned int) curve->prime_len, curve->name); + goto fail; + } + + y = json_get_member_base64url(jwk, "y"); + if (!y) { + wpa_printf(MSG_DEBUG, "DPP: No y in JWK"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y); + if (wpabuf_len(y) != curve->prime_len) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWK y length %u (expected %u for curve %s)", + (unsigned int) wpabuf_len(y), + (unsigned int) curve->prime_len, curve->name); + goto fail; + } + + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); + if (!group) { + wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK"); + goto fail; + } + + pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y), + wpabuf_len(x)); + *key_curve = curve; + +fail: + wpabuf_free(x); + wpabuf_free(y); + + return pkey; +} + + +int dpp_key_expired(const char *timestamp, os_time_t *expiry) +{ + struct os_time now; + unsigned int year, month, day, hour, min, sec; + os_time_t utime; + const char *pos; + + /* ISO 8601 date and time: + * T